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

274 lines
No EOL
7.8 KiB
C++

#include "BFPlatform.h"
#ifdef BF_WWISE_ENABLED
//////////////////////////////////////////////////////////////////////
//
// AkFilePackageLUT.cpp
//
// This class parses the header of file packages that were created with the
// AkFilePackager utility app (located in ($WWISESDK)/samples/FilePackager/),
// and looks-up files at run-time.
//
// 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.
//
// The language 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.
//
// Copyright (c) 2007-2009 Audiokinetic Inc. / All Rights Reserved
//
//////////////////////////////////////////////////////////////////////
#include "Common.h"
#include "AkFilePackageLUT.h"
#include <AK/SoundEngine/Common/AkMemoryMgr.h>
#include <AK/SoundEngine/Common/AkSoundEngine.h> // For string hash.
#include <AK/Tools/Common/AkPlatformFuncs.h>
#include <AK/Tools/Common/AkFNVHash.h>
#ifdef _DEBUG
template<bool> struct AkCompileTimeAssert;
template<> struct AkCompileTimeAssert<true> { };
#define AK_STATIC_ASSERT(e) (AkCompileTimeAssert<(e) != 0>())
#else
#define AK_STATIC_ASSERT(e)
#endif
#define AK_MAX_EXTERNAL_NAME_SIZE 260
CAkFilePackageLUT::CAkFilePackageLUT()
:m_curLangID( AK_INVALID_LANGUAGE_ID )
,m_pLangMap( NULL )
,m_pSoundBanks( NULL )
,m_pStmFiles( NULL )
,m_pExternals( NULL )
{
AK_STATIC_ASSERT(sizeof(AkFileEntry<AkFileID>) == 20);
AK_STATIC_ASSERT(sizeof(AkFileEntry<AkUInt64>) == 24);
}
CAkFilePackageLUT::~CAkFilePackageLUT()
{
}
// Create a new LUT from a packaged file header.
// The LUT sets pointers to appropriate location inside header data (in_pData).
AKRESULT CAkFilePackageLUT::Setup(
AkUInt8 * in_pData, // Header data.
AkUInt32 in_uHeaderSize // Size of file package header.
)
{
struct FileHeaderFormat
{
char headerDefinition[AKPK_HEADER_CHUNK_DEF_SIZE];
AkUInt32 uVersion;
AkUInt32 uLanguageMapSize;
AkUInt32 uSoundBanksLUTSize;
AkUInt32 uStmFilesLUTSize;
AkUInt32 uExternalsLUTSize;
};
FileHeaderFormat * pHeader = (FileHeaderFormat*)in_pData;
// Check header size,
if ( in_uHeaderSize < sizeof(FileHeaderFormat)
+ pHeader->uLanguageMapSize
+ pHeader->uSoundBanksLUTSize
+ pHeader->uStmFilesLUTSize
+ pHeader->uExternalsLUTSize)
{
return AK_Fail;
}
// Check version.
if ( pHeader->uVersion < AKPK_CURRENT_VERSION )
return AK_Fail;
// Get address of maps and LUTs.
in_pData += sizeof(FileHeaderFormat);
m_pLangMap = (StringMap*)in_pData;
in_pData += pHeader->uLanguageMapSize;
m_pSoundBanks = (FileLUT<AkFileID>*)in_pData;
in_pData += pHeader->uSoundBanksLUTSize;
m_pStmFiles = (FileLUT<AkFileID>*)in_pData;
in_pData += pHeader->uStmFilesLUTSize;
m_pExternals = (FileLUT<AkUInt64>*)in_pData;
return AK_Success;
}
// Find a file entry by ID.
const CAkFilePackageLUT::AkFileEntry<AkFileID> * CAkFilePackageLUT::LookupFile(
AkFileID in_uID, // File ID.
AkFileSystemFlags * in_pFlags // Special flags. Do not pass NULL.
)
{
AKASSERT( in_pFlags && in_pFlags->uCompanyID == AKCOMPANYID_AUDIOKINETIC );
if ( in_pFlags->uCodecID == AKCODECID_BANK
&& m_pSoundBanks
&& m_pSoundBanks->HasFiles() )
{
return LookupFile<AkFileID>( in_uID, m_pSoundBanks, in_pFlags->bIsLanguageSpecific );
}
else if ( m_pStmFiles && m_pStmFiles->HasFiles() )
{
// We assume that the file is a streamed audio file.
return LookupFile<AkFileID>( in_uID, m_pStmFiles, in_pFlags->bIsLanguageSpecific );
}
// No table loaded.
return NULL;
}
// Find a file entry by ID.
const CAkFilePackageLUT::AkFileEntry<AkUInt64> * CAkFilePackageLUT::LookupFile(
AkUInt64 in_uID, // File ID.
AkFileSystemFlags * in_pFlags // Special flags. Do not pass NULL.
)
{
AKASSERT( in_pFlags );
if ( in_pFlags->uCompanyID == AKCOMPANYID_AUDIOKINETIC_EXTERNAL
&& m_pExternals
&& m_pExternals->HasFiles() )
{
return LookupFile<AkUInt64>( in_uID, m_pExternals, in_pFlags->bIsLanguageSpecific );
}
// No table loaded.
return NULL;
}
// Set current language.
// Returns AK_InvalidLanguage if a package is loaded but the language string cannot be found.
// Returns AK_Success otherwise.
AKRESULT CAkFilePackageLUT::SetCurLanguage(
const AkOSChar* in_pszLanguage // Language string.
)
{
m_curLangID = AK_INVALID_LANGUAGE_ID;
if ( m_pLangMap && in_pszLanguage )
{
AkUInt16 uLangID = (AkUInt16)m_pLangMap->GetID( in_pszLanguage );
if ( uLangID == AK_INVALID_UNIQUE_ID
&& m_pLangMap->GetNumStrings() > 1 ) // Do not return AK_InvalidLanguage if package contains only SFX data.
{
return AK_InvalidLanguage;
}
m_curLangID = uLangID;
}
return AK_Success;
}
void CAkFilePackageLUT::RemoveFileExtension( AkOSChar* in_pstring )
{
while( *in_pstring != 0 )
{
if( *in_pstring == AKTEXT('.') )
{
*in_pstring = 0;
return;
}
++in_pstring;
}
}
// Find a soundbank ID by its name.
// Returns AK_INVALID_FILE_ID if no soundbank LUT is loaded.
AkFileID CAkFilePackageLUT::GetSoundBankID(
const AkOSChar* in_pszBankName // Soundbank name.
)
{
// Remove the file extension if it was used.
AkUInt32 stringSize = (AkUInt32)AKPLATFORM::OsStrLen( in_pszBankName ) + 1;
AkOSChar* pStringWithoutExtension = (AkOSChar *)AkAlloca( (stringSize) * sizeof( AkOSChar ) );
AKPLATFORM::SafeStrCpy( pStringWithoutExtension, in_pszBankName, stringSize );
RemoveFileExtension( pStringWithoutExtension );
// Hash
return AK::SoundEngine::GetIDFromString( pStringWithoutExtension );
}
AkUInt64 CAkFilePackageLUT::GetExternalID(
const AkOSChar* in_pszExternalName // External Source name.
)
{
char* szString;
CONVERT_OSCHAR_TO_CHAR(in_pszExternalName, szString);
size_t stringSize = strlen( szString );
// 1- Make lower case.
_MakeLowerA( szString, stringSize );
AK::FNVHash64 MainHash;
return MainHash.Compute( (const unsigned char *) szString, (unsigned int)stringSize );
}
void CAkFilePackageLUT::_MakeLowerA( char* in_pString, size_t in_strlen )
{
for( size_t i = 0; i < in_strlen; ++i )
{
if( in_pString[i] >= 'A' && in_pString[i] <= 'Z' )
{
in_pString[i] += 0x20;
}
}
}
void CAkFilePackageLUT::_MakeLower( AkOSChar* in_pString )
{
size_t uStrlen = AKPLATFORM::OsStrLen( in_pString );
const AkOSChar CaseDiff = AKTEXT('a') - AKTEXT('A');
for( size_t i = 0; i < uStrlen; ++i )
{
if( in_pString[i] >= AKTEXT('A') && in_pString[i] <= AKTEXT('Z') )
{
in_pString[i] += CaseDiff;
}
}
}
AkUInt32 CAkFilePackageLUT::StringMap::GetID( const AkOSChar* in_pszString )
{
// Make string lower case.
size_t uStrLen = AKPLATFORM::OsStrLen(in_pszString)+1;
AkOSChar * pszLowerCaseString = (AkOSChar*)AkAlloca(uStrLen*sizeof(AkOSChar));
AKASSERT( pszLowerCaseString );
AKPLATFORM::SafeStrCpy(pszLowerCaseString, in_pszString, uStrLen );
_MakeLower( pszLowerCaseString );
// 'this' is m_uNumStrings. +1 points to the beginning of the StringEntry array.
StringEntry * pTable = (StringEntry*)((AkUInt32*)this + 1);
// Binary search: strings are sorted (case sensitive).
AkInt32 uTop = 0, uBottom = m_uNumStrings-1;
do
{
AkInt32 uThis = ( uBottom - uTop ) / 2 + uTop;
AkOSChar * pString = (AkOSChar*)((AkUInt8*)this + pTable[ uThis ].uOffset);
int iCmp = AKPLATFORM::OsStrCmp( pString, pszLowerCaseString );
if ( 0 == iCmp )
return pTable[uThis].uID;
else if ( iCmp > 0 ) //in_pTable[ uThis ].pString > pszLowerCaseString
uBottom = uThis - 1;
else //in_pTable[ uThis ].pString < pszLowerCaseString
uTop = uThis + 1;
}
while ( uTop <= uBottom );
// ID not found.
return AK_INVALID_UNIQUE_ID;
}
#endif