1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 03:28:20 +02:00
Beef/BeefySysLib/sound/Common/AkFilePackageLowLevelIO.h
2019-08-23 11:56:54 -07:00

399 lines
15 KiB
C++

//////////////////////////////////////////////////////////////////////
//
// AkFilePackageLowLevelIO.h
//
// CAkFilePackageLowLevelIO extends a Low-Level I/O device by providing
// the ability to reference files that are part of a file package.
//
// It can extend either blocking or deferred I/O hooks (both inheriting from
// AK::StreamMgr::IAkLowLevelIOHook), since its base class is templated.
// In either case, the base class must also implement
// AK::StreamMgr::IAkFileLocationResolver. This interface defines both overloads
// for Open(), and this is where the package's look-up table is searched.
// If no match is found, then it falls back on the base implementation.
//
// Clients of devices that use this class' functionnality simply need to call
// LoadFilePackage(), which loads and parses file packages that were created with
// the AkFilePackager utility app (located in ($WWISESDK)/samples/FilePackager/).
// The header of these file packages contains look-up tables that describe the
// internal offset of each file it references, their block size (required alignment),
// and their language. Each combination of AkFileID and Language ID is unique.
//
// LoadFilePackage() returns a package ID that can be used to unload it. Any number
// of packages can be loaded simultaneously. When Open() is called, the last package
// loaded is searched first, then the previous one, and so on.
//
// The language ID was created dynamically when the package was created. The header
// also contains a map of language names (strings) to their ID, so that the proper
// language-specific version of files can be resolved. The language name that is stored
// matches the name of the directory that is created by the Wwise Bank Manager,
// except for the trailing slash.
//
// The type of package is also a template argument. By default, it is a disk package
// (see AkDiskPackage.h).
//
// Copyright (c) 2006 Audiokinetic Inc. / All Rights Reserved
//
//////////////////////////////////////////////////////////////////////
#ifndef _AK_FILE_PACKAGE_LOW_LEVEL_IO_H_
#define _AK_FILE_PACKAGE_LOW_LEVEL_IO_H_
#include <AK/SoundEngine/Common/AkStreamMgrModule.h>
#include "AkFilePackage.h"
//-----------------------------------------------------------------------------
// Name: AkFilePackageReader
// Desc: This class wraps an AK::IAkStdStream to read a file package.
//-----------------------------------------------------------------------------
class AkFilePackageReader
{
public:
AkFilePackageReader()
: m_pStream( NULL ), m_uBlockSize( 0 ) {}
~AkFilePackageReader()
{
// IMPORTANT: Do not close file. This object can be copied.
}
AKRESULT Open(
const AkOSChar* in_pszFilePackageName, // File package name. Location is resolved using base class' Open().
bool in_bReadFromSFXOnlyDir // If true, the file package is opened from the language agnostic directory only. Otherwise, it tries to open it
// from the current language-specific directory first, and then from the common directory if it fails, similarly to the soundbanks loader of the Sound Engine (Default).
)
{
AkFileSystemFlags flags;
flags.uCompanyID = AKCOMPANYID_AUDIOKINETIC;
flags.uCodecID = AKCODECID_FILE_PACKAGE;
flags.uCustomParamSize = 0;
flags.pCustomParam = NULL;
flags.bIsLanguageSpecific = !in_bReadFromSFXOnlyDir;
flags.bIsFromRSX = false;
AKRESULT eResult = AK::IAkStreamMgr::Get()->CreateStd(
in_pszFilePackageName,
&flags,
AK_OpenModeRead,
m_pStream,
true );
if ( eResult != AK_Success
&& !in_bReadFromSFXOnlyDir )
{
// Try again, in SFX-only directory.
flags.bIsLanguageSpecific = false;
eResult = AK::IAkStreamMgr::Get()->CreateStd(
in_pszFilePackageName,
&flags,
AK_OpenModeRead,
m_pStream,
true );
}
return eResult;
}
AKRESULT Read(
void * in_pBuffer, // Buffer. Must be aligned with value returned by GetBlockSize().
AkUInt32 in_uSizeToRead, // Size to read. Must be a multiple of value returned by GetBlockSize().
AkUInt32 & out_uSizeRead, // Returned size read.
AkPriority in_priority = AK_DEFAULT_PRIORITY, // Priority heuristic.
AkReal32 in_fThroughput = 0 // Throughput heuristic. 0 means "not set", and results in "immediate".
)
{
AKASSERT( m_pStream );
AkReal32 fDeadline = ( in_fThroughput > 0 ) ? in_uSizeToRead / in_fThroughput : 0;
return m_pStream->Read(
in_pBuffer,
in_uSizeToRead,
true,
in_priority,
fDeadline,
out_uSizeRead );
}
AKRESULT Seek(
AkUInt32 in_uPosition,
AkUInt32 & out_uRealOffset
)
{
AkInt64 iRealOffset;
AKRESULT eResult = m_pStream->SetPosition( in_uPosition, AK_MoveBegin, &iRealOffset );
AKASSERT( eResult == AK_Success || !"Failed changing stream position" );
out_uRealOffset = (AkUInt32)iRealOffset;
return eResult;
}
void Close()
{
if ( m_pStream )
m_pStream->Destroy();
m_pStream = NULL;
}
void SetName(
const AkOSChar*
#ifndef AK_OPTIMIZED
in_pszName
#endif
)
{
#ifndef AK_OPTIMIZED
AKASSERT( m_pStream );
m_pStream->SetStreamName( in_pszName );
#endif
}
AkUInt64 GetSize()
{
AKASSERT( m_pStream );
AkStreamInfo info;
m_pStream->GetInfo( info );
return info.uSize;
}
AkUInt32 GetBlockSize()
{
AKASSERT( m_pStream );
// AK::IAkStdStream::GetBlockSize() is costly. Cache block size.
if ( !m_uBlockSize )
m_uBlockSize = m_pStream->GetBlockSize();
return m_uBlockSize;
}
AkFileHandle GetHandle()
{
AKASSERT( m_pStream );
AkFileDesc * pFileDesc = (AkFileDesc*)m_pStream->GetFileDescriptor();
AKASSERT( pFileDesc );
return pFileDesc->hFile;
}
private:
AK::IAkStdStream * m_pStream;
AkUInt32 m_uBlockSize;
};
//-----------------------------------------------------------------------------
// Name: CAkDiskPackage
// Desc: This class extends the CAkFilePackage class by providing system handle
// management.
// It keeps a copy of the file package reader that was used to read the file package
// header from disk, and uses it to query and cache its low-level system handle
// (AkFileDesc::hFile). This handle is kept open and used directly to read portions
// of the package from disk, corresponding to read requests for the files it
// contains. The streaming object / package handle is closed when the package
// is destroyed.
//-----------------------------------------------------------------------------
class CAkDiskPackage : public CAkFilePackage
{
public:
// Factory for disk package.
// Instantiates a file package object, queries its file handle once and keep in package.
// Also keeps a copy of its reader object, which is used to close the file handle on destruction.
static CAkDiskPackage * Create(
AkFilePackageReader & in_reader, // File package reader.
const AkOSChar* in_pszPackageName, // Name of the file package (for memory monitoring).
AkMemPoolId in_memPoolID, // Memory pool in which the package is created with its lookup table.
AkUInt32 in_uHeaderSize, // File package header size, including the size of the header chunk AKPK_HEADER_CHUNK_DEF_SIZE.
AkUInt32 & out_uReservedHeaderSize, // Size reserved for header, taking mem align into account.
AkUInt8 *& out_pHeaderBuffer // Returned address of memory for header.
)
{
CAkDiskPackage * pPackage = CAkFilePackage::Create<CAkDiskPackage>(
in_pszPackageName,
in_memPoolID,
in_uHeaderSize,
in_reader.GetBlockSize(),
out_uReservedHeaderSize,
out_pHeaderBuffer );
if ( pPackage )
{
pPackage->m_reader = in_reader; // Copy reader.
pPackage->m_hFile = in_reader.GetHandle(); // Cache handle.
}
return pPackage;
}
CAkDiskPackage( AkUInt32 in_uPackageID, AkUInt32 in_uHeaderSize, AkMemPoolId in_poolID, void * in_pToRelease, bool in_bIsInternalPool )
: CAkFilePackage( in_uPackageID, in_uHeaderSize, in_poolID, in_pToRelease, in_bIsInternalPool )
{ }
// Override Destroy(): Close
virtual void Destroy()
{
m_reader.Close();
CAkFilePackage::Destroy();
}
// Fills an AkFileHandle with a value that will be useable by the low-level I/O hook.
// Disk packages return the package's system handle: the hook reads from the package file itself, with
// proper offset, to get the data it needs.
inline void GetHandleForFileDesc( AkFileHandle & out_hFile ) { out_hFile = m_hFile; }
protected:
AkFilePackageReader m_reader; // READER object. Holds the stream used to read the package. Closed only upon package destruction.
AkFileHandle m_hFile; // Platform-independent file handle (cached from READER).
};
//-----------------------------------------------------------------------------
// Name: class CAkFilePackageLowLevelIO.
// Desc: Extends default Low-level IO implementation with packaged file support.
// Base class must implement one of the low-level I/O hooks
// (AK::StreamMgr::IAkIOHookBlocking or AK::StreamMgr::IAkIOHookDeferred)
// _and_ the AK::StreamMgr::IAkFileLocationResolver interface.
// It must also define the following methods:
// - void Term()
// Note: This class uses AkFileDesc::uCustomParamSize to store the block size
// of files opened from a package, and relies on the fact that it is 0
// when they are not part of the package.
//-----------------------------------------------------------------------------
template <class T_LLIOHOOK_FILELOC, class T_PACKAGE = CAkDiskPackage>
class CAkFilePackageLowLevelIO : public T_LLIOHOOK_FILELOC
{
public:
CAkFilePackageLowLevelIO();
virtual ~CAkFilePackageLowLevelIO();
// File package loading:
// Opens a package file, parses its header, fills LUT.
// Overrides of Open() will search files in loaded LUTs first, then use default Low-Level I/O
// services if they cannot be found.
// Any number of packages can be loaded at a time. Each LUT is searched until a match is found.
// Returns AK_Success if successful, AK_InvalidLanguage if the current language
// does not exist in the LUT (not necessarily an error), AK_Fail for any other reason.
// Also returns a package ID which can be used to unload it (see UnloadFilePackage()).
// WARNING: This method is not thread safe. Ensure there are no I/O occurring on this device
// when loading a file package.
virtual AKRESULT LoadFilePackage(
const AkOSChar* in_pszFilePackageName, // File package name. Location is resolved using base class' Open().
AkUInt32 & out_uPackageID, // Returned package ID.
AkMemPoolId in_memPoolID = AK_DEFAULT_POOL_ID // Memory pool in which the LUT is written. Passing AK_DEFAULT_POOL_ID will create a new pool automatically.
// Note that the bulk of the package's data is stored in VRAM, which is allocated through the VRAM allocation hook.
);
// Unload a file package.
// Returns AK_Success if in_uPackageID exists, AK_Fail otherwise.
// WARNING: This method is not thread safe. Ensure there are no I/O occurring on this device
// when unloading a file package.
virtual AKRESULT UnloadFilePackage(
AkUInt32 in_uPackageID // Returned package ID.
);
// Unload all file packages.
// Returns AK_Success;
// WARNING: This method is not thread safe. Ensure there are no I/O occurring on this device
// when unloading a file package.
virtual AKRESULT UnloadAllFilePackages();
//
// Overriden base class policies.
// ---------------------------------------------------------------
// Clean up.
void Term();
protected:
//
// IAkFileLocationResolver interface overriden methods.
// ---------------------------------------------------------------
// Override Open (string): Search file in each LUT first. If it cannot be found, use base class services.
// If the file is found in the LUTs, open is always synchronous.
// Applies to AK soundbanks only.
virtual AKRESULT 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.
);
// Override Open (ID): Search file in each LUT first. If it cannot be found, use base class services.
// If the file is found in the LUTs, open is always synchronous.
virtual AKRESULT 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.
);
//
// IAkLowLevelIOHook interface overriden methods.
// ---------------------------------------------------------------
// Override Close: Do not close handle if file descriptor is part of the current packaged file.
virtual AKRESULT Close(
AkFileDesc & in_fileDesc // File descriptor.
);
// Override GetBlockSize: Get the block size of the LUT if a file package is loaded.
virtual AkUInt32 GetBlockSize(
AkFileDesc & in_fileDesc // File descriptor.
);
protected:
// Language change handling.
// ------------------------------------------
// Handler for global language change.
static AK_FUNC( void, LanguageChangeHandler )(
const AkOSChar * const in_pLanguageName,// New language name.
void * in_pCookie // Cookie that was passed to AddLanguageChangeObserver().
)
{
((CAkFilePackageLowLevelIO<T_LLIOHOOK_FILELOC, T_PACKAGE>*)in_pCookie)->OnLanguageChange( in_pLanguageName );
}
// Updates language of all loaded packages. Packages keep a language ID to help them find
// language-specific assets quickly.
void OnLanguageChange(
const AkOSChar * const in_pLanguageName // New language name.
);
// File package handling methods.
// ------------------------------------------
// Loads a file package, with a given file package reader.
AKRESULT _LoadFilePackage(
const AkOSChar* in_pszFilePackageName, // File package name. Location is resolved using base class' Open().
AkFilePackageReader & in_reader, // File package reader.
AkPriority in_readerPriority, // File package reader priority heuristic.
AkMemPoolId in_memPoolID, // Memory pool in which the LUT is written. Passing AK_DEFAULT_POOL_ID will create a new pool automatically.
T_PACKAGE *& out_pPackage // Returned package
);
// Searches the LUT to find the file data associated with the FileID.
// Returns AK_Success if the file is found.
template <class T_FILEID>
AKRESULT FindPackagedFile(
T_PACKAGE * in_pPackage, // Package to search into.
T_FILEID in_fileID, // File ID.
AkFileSystemFlags * in_pFlags, // Special flags. Can pass NULL.
AkFileDesc & out_fileDesc // Returned file descriptor.
);
// Returns true if file described by in_fileDesc is in a package.
inline bool IsInPackage(
const AkFileDesc & in_fileDesc // File descriptor.
)
{
return in_fileDesc.uCustomParamSize > 0;
}
protected:
// List of loaded packages.
ListFilePackages m_packages;
bool m_bRegisteredToLangChg; // True after registering to language change notifications.
};
#include "AkFilePackageLowLevelIO.inl"
#endif //_AK_FILE_PACKAGE_LOW_LEVEL_IO_H_