123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "sfx/fmod/sfxFMODProject.h"
- #include "sfx/fmod/sfxFMODDevice.h"
- #include "sfx/fmod/sfxFMODEvent.h"
- #include "sfx/fmod/sfxFMODEventGroup.h"
- #include "sfx/sfxDescription.h"
- #include "core/stringTable.h"
- #include "core/volume.h"
- #include "core/util/path.h"
- #include "core/stream/fileStream.h"
- #include "core/stream/bitStream.h"
- #include "core/util/safeDelete.h"
- IMPLEMENT_CO_DATABLOCK_V1( SFXFMODProject );
- ConsoleDocClass( SFXFMODProject,
- "@brief An FMOD Designer project loaded into Torque.\n\n"
-
- "@section SFXFMODProject_resources Resource Loading\n\n"
-
- "@ingroup SFXFMOD\n"
- "@ingroup Datablocks"
- );
- //-----------------------------------------------------------------------------
- SFXFMODProject::SFXFMODProject()
- : mHandle( NULL ),
- mRootGroups( NULL )
- {
- VECTOR_SET_ASSOCIATION( mGroups );
- VECTOR_SET_ASSOCIATION( mEvents );
-
- SFX->getEventSignal().notify( this, &SFXFMODProject::_onSystemEvent );
- }
- //-----------------------------------------------------------------------------
- SFXFMODProject::~SFXFMODProject()
- {
- AssertFatal( mGroups.empty(), "SFXFMODProject::~SFXFMODProject - project still has groups attached" );
- AssertFatal( mEvents.empty(), "SFXFMODProject::~SFXFMODProject - project still has events attached" );
-
- if( SFX )
- SFX->getEventSignal().remove( this, &SFXFMODProject::_onSystemEvent );
- }
- //-----------------------------------------------------------------------------
- void SFXFMODProject::initPersistFields()
- {
- addGroup( "FMOD" );
-
- addField( "fileName", TypeStringFilename, Offset( mFileName, SFXFMODProject ), "The compiled .fev file from FMOD Designer." );
- addField( "mediaPath", TypeStringFilename, Offset( mMediaPath, SFXFMODProject ), "Path to the media files; if unset, defaults to project directory." );
-
- endGroup( "FMOD" );
-
- Parent::initPersistFields();
- }
- //-----------------------------------------------------------------------------
- bool SFXFMODProject::onAdd()
- {
- if( !Parent::onAdd() )
- return false;
-
- // If this is a non-networked datablock, load the
- // project data now.
-
- if( isClientOnly() && !_load() )
- return false;
-
- return true;
- }
- //-----------------------------------------------------------------------------
- void SFXFMODProject::onRemove()
- {
- Parent::onRemove();
-
- _clear();
- }
- //-----------------------------------------------------------------------------
- bool SFXFMODProject::preload( bool server, String& errorStr )
- {
- if( !Parent::preload( server, errorStr ) )
- return false;
-
- if( server )
- {
- if( mFileName.isEmpty() )
- {
- errorStr = String::ToString( "SFXFMODProject::preload - no filename set on %i (%s)",
- getId(), getName() );
- return false;
- }
-
- if( mGroups.empty() || mEvents.empty() )
- _load();
-
- release();
- }
-
- return true;
- }
- //-----------------------------------------------------------------------------
- void SFXFMODProject::packData( BitStream* stream )
- {
- Parent::packData( stream );
-
- stream->write( mFileName );
- stream->write( mMediaPath );
- }
- //-----------------------------------------------------------------------------
- void SFXFMODProject::unpackData( BitStream* stream )
- {
- Parent::unpackData( stream );
-
- stream->read( &mFileName );
- stream->read( &mMediaPath );
- }
- //-----------------------------------------------------------------------------
- void SFXFMODProject::_onSystemEvent( SFXSystemEventType event )
- {
- switch( event )
- {
- case SFXSystemEvent_DestroyDevice:
-
- // If the FMOD device is being destroyed,
- // release all our data.
-
- if( SFXFMODDevice::instance() )
- release();
-
- break;
-
- default:
- break;
- }
- }
- //-----------------------------------------------------------------------------
- void SFXFMODProject::_clear()
- {
- release();
-
- for( U32 i = 0; i < mGroups.size(); ++ i )
- if( !mGroups[ i ]->isRemoved() )
- mGroups[ i ]->deleteObject();
-
- mGroups.clear();
- mEvents.clear();
-
- mRootGroups = NULL;
- }
- //-----------------------------------------------------------------------------
- bool SFXFMODProject::_load()
- {
- const Torque::Path eventScriptFileName = mFileName + ".cs";
- const Torque::Path eventScriptFileNameDSO = eventScriptFileName + ".dso";
- const bool eventScriptFileExists = Torque::FS::IsFile( eventScriptFileName );
- const bool eventScriptFileDSOExists = Torque::FS::IsFile( eventScriptFileNameDSO );
-
- // Check if we need to (re-)generate the event script file.
-
- bool needToGenerateEventScriptFile = false;
- if( ( !eventScriptFileExists && !eventScriptFileDSOExists )
- || ( Torque::FS::CompareModifiedTimes( mFileName, eventScriptFileName ) > 0
- || Torque::FS::CompareModifiedTimes( mFileName, eventScriptFileNameDSO ) > 0 ) )
- needToGenerateEventScriptFile = true;
-
- // If we need to generate, check if we can.
-
- SFXFMODDevice* fmodDevice = SFXFMODDevice::instance();
- if( needToGenerateEventScriptFile && !fmodDevice )
- {
- // If we have neither FMOD nor the event scripts (even if outdated),
- // there's nothing we can do.
-
- if( !eventScriptFileExists && !eventScriptFileDSOExists )
- {
- Con::errorf( "SFXFMODProject::_load() - event script for '%s' does not exist and device is not FMOD; load this project under FMOD first",
- mFileName.c_str() );
- return false;
- }
-
- // Use the oudated versions.
-
- Con::warnf( "SFXMODProject::_load() - event script for '%s' is outdated and device is not FMOD; event data may not match .fev contents",
- mFileName.c_str() );
- needToGenerateEventScriptFile = false;
- }
-
- // If we don't need to regenerate, try executing the event script now.
-
- if( !needToGenerateEventScriptFile )
- {
- if( ( eventScriptFileExists || eventScriptFileDSOExists )
- && !Con::evaluatef( "exec( \"%s\" );", eventScriptFileName.getFullPath().c_str() ) )
- {
- Con::errorf( "SFXFMODProject::_load() - failed to execute event script for '%s'%s",
- mFileName.c_str(),
- fmodDevice != NULL ? "; trying to regenerate" : ""
- );
-
- if( !fmodDevice )
- return false;
-
- needToGenerateEventScriptFile = true;
- }
- else
- Con::printf( "SFXFMODProject - %s: Loaded event script", getName() );
- }
-
- // If we need to generate the event script file,
- // load the FMOD project now and then emit the file.
-
- if( needToGenerateEventScriptFile )
- {
- // Try to load the project.
-
- acquire();
-
- if( !mHandle )
- return false;
-
- // Get the project info.
- FMOD_EVENT_PROJECTINFO info;
-
- int numEvents;
- int numGroups;
-
- SFXFMODDevice::smFunc->FMOD_EventProject_GetInfo( mHandle, &info );
- SFXFMODDevice::smFunc->FMOD_EventProject_GetNumEvents( mHandle, &numEvents );
- SFXFMODDevice::smFunc->FMOD_EventProject_GetNumGroups( mHandle, &numGroups );
-
- Con::printf( "SFXFMODProject - %s: Loading '%s' from '%s' (index: %i, events: %i, groups: %i)",
- getName(), info.name, mFileName.c_str(), info.index, numEvents, numGroups );
-
- // Load the root groups.
- for( U32 i = 0; i < numGroups; ++ i )
- {
- FMOD_EVENTGROUP* group;
- if( SFXFMODDevice::smFunc->FMOD_EventProject_GetGroupByIndex( mHandle, i, true, &group ) == FMOD_OK )
- {
- SFXFMODEventGroup* object = new SFXFMODEventGroup( this, group );
-
- object->mSibling = mRootGroups;
- mRootGroups = object;
-
- String qualifiedName = FMODEventPathToTorqueName( object->getQualifiedName() );
-
- if( !isClientOnly() )
- object->assignId();
-
- object->registerObject( String::ToString( "%s_%s", getName(), qualifiedName.c_str() ) );
- if( isClientOnly() )
- Sim::getRootGroup()->addObject( object );
-
- object->_load();
- }
- }
- // Create the event script file.
-
- FileStream stream;
- if( !stream.open( eventScriptFileName.getFullPath(), Torque::FS::File::Write ) )
- {
- Con::errorf( "SFXFMODProject::_load - could not create event script file for '%s'", mFileName.c_str() );
- return true; // Don't treat as failure.
- }
-
- // Write a header.
-
- stream.writeText( String::ToString( "// This file has been auto-generated from '%s'\n", mFileName.c_str() ) );
- stream.writeText( "// Do not edit this file manually and do not move it away from the Designer file.\n\n" );
-
- // Write the group objects.
-
- for( U32 i = 0; i < mGroups.size(); ++ i )
- {
- mGroups[ i ]->write( stream, 0 );
- stream.writeText( "\n" );
- }
- // Write the event objects along with their
- // SFXDescriptions.
-
- for( U32 i = 0; i < mEvents.size(); ++ i )
- {
- mEvents[ i ]->getDescription()->write( stream, 0 );
- mEvents[ i ]->write( stream, 0 );
- stream.writeText( "\n" );
- }
-
- Con::printf( "SFXFMODProject - %s: Generated event script '%s'", getName(), eventScriptFileName.getFullPath().c_str() );
- }
- return true;
- }
- //-----------------------------------------------------------------------------
- void SFXFMODProject::acquire( bool recursive )
- {
- // Load the project file.
-
- if( !mHandle )
- {
- FMOD_RESULT result = SFXFMODDevice::smFunc->FMOD_EventSystem_Load(
- SFXFMODDevice::smEventSystem,
- mFileName.c_str(),
- ( FMOD_EVENT_LOADINFO* ) 0,
- &mHandle
- );
-
- if( result != FMOD_OK )
- {
- Con::errorf( "SFXFMODProject::acquire - could not load '%s' (%s)",
- mFileName.c_str(), FMODResultToString( result ).c_str() );
- mHandle = NULL;
- return;
- }
-
- Con::printf( "SFXFMODProject - %s: Opened project '%s'", getName(), mFileName.c_str() );
-
- // Set the media path.
-
- String mediaPath;
- if( !mMediaPath.isEmpty() )
- {
- mediaPath = mMediaPath;
- if( mediaPath[ mediaPath.length() - 1 ] != '/' )
- mediaPath += '/';
- }
- else
- {
- // Set to project directory.
-
- Torque::Path path = mFileName;
- if( path.getRoot().isEmpty() )
- path.setRoot( "game" );
- path.setFileName( "" );
- path.setExtension( "" );
-
- mediaPath = path.getFullPath() + '/';
- }
- SFXFMODDevice::smFunc->FMOD_EventSystem_SetMediaPath(
- SFXFMODDevice::smEventSystem,
- mediaPath.c_str()
- );
- }
-
- // Acquire the root groups.
-
- if( recursive )
- for( SFXFMODEventGroup* group = mRootGroups; group != NULL; group = group->mSibling )
- group->acquire( true );
-
- SFXFMODDevice::instance()->updateMemUsageStats();
- }
- //-----------------------------------------------------------------------------
- void SFXFMODProject::release()
- {
- if( !mHandle )
- return;
-
- Con::printf( "SFXFMODProject - %s: Closing project '%s'",
- getName(), mFileName.c_str() );
-
- // Clear media path.
-
- SFXFMODDevice::smFunc->FMOD_EventSystem_SetMediaPath(
- SFXFMODDevice::smEventSystem, "" );
-
- // Release the root groups.
-
- for( SFXFMODEventGroup* group = mRootGroups; group != NULL; group = group->mSibling )
- group->release();
-
- // Release the project.
-
- SFXFMODDevice::smFunc->FMOD_EventProject_Release( mHandle );
- mHandle = NULL;
- SFXFMODDevice::instance()->updateMemUsageStats();
- }
- //-----------------------------------------------------------------------------
- void SFXFMODProject::_addEvent( SFXFMODEvent* event )
- {
- mEvents.push_back( event );
- }
- //-----------------------------------------------------------------------------
- void SFXFMODProject::_addGroup( SFXFMODEventGroup* group )
- {
- mGroups.push_back( group );
- }
- //-----------------------------------------------------------------------------
- void SFXFMODProject::_removeEvent( SFXFMODEvent* event )
- {
- for( U32 i = 0; i < mEvents.size(); ++ i )
- if( mEvents[ i ] == event )
- {
- mEvents.erase( i );
- break;
- }
- }
- //-----------------------------------------------------------------------------
- void SFXFMODProject::_removeGroup( SFXFMODEventGroup* group )
- {
- // Remove from group array.
-
- for( U32 i = 0; i < mGroups.size(); ++ i )
- if( mGroups[ i ] == group )
- {
- mGroups.erase( i );
- break;;
- }
-
- // Unlink if it's a root group.
-
- if( !group->mParent )
- {
- if( group == mRootGroups )
- {
- mRootGroups = group->mSibling;
- group->mSibling = NULL;
- }
- else
- {
- SFXFMODEventGroup* p = mRootGroups;
- while( p && p->mSibling != group )
- p = p->mSibling;
-
- if( p )
- {
- p->mSibling = group->mSibling;
- group->mSibling = NULL;
- }
- }
- }
- }
|