You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
982 lines
29 KiB
982 lines
29 KiB
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: Defines a group of app systems that all have the same lifetime
|
|
// that need to be connected/initialized, etc. in a well-defined order
|
|
//
|
|
// $Revision: $
|
|
// $NoKeywords: $
|
|
//===========================================================================//
|
|
|
|
#include "tier0/platform.h"
|
|
|
|
#include "appframework/ilaunchermgr.h"
|
|
#if defined( PLATFORM_PS3)
|
|
#include "ps3/ps3_helpers.h"
|
|
#endif
|
|
|
|
#include "tier0/platwindow.h"
|
|
#include "appframework/IAppSystemGroup.h"
|
|
#include "appframework/iappsystem.h"
|
|
#include "interface.h"
|
|
#include "filesystem.h"
|
|
#include "filesystem_init.h"
|
|
#include <algorithm>
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// constructor, destructor
|
|
//-----------------------------------------------------------------------------
|
|
extern ILoggingListener *g_pDefaultLoggingListener;
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// constructor, destructor
|
|
//-----------------------------------------------------------------------------
|
|
CAppSystemGroup::CAppSystemGroup( CAppSystemGroup *pAppSystemParent ) : m_SystemDict(false, 0, 16)
|
|
{
|
|
m_pParentAppSystem = pAppSystemParent;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Actually loads a DLL
|
|
//-----------------------------------------------------------------------------
|
|
CSysModule *CAppSystemGroup::LoadModuleDLL( const char *pDLLName )
|
|
{
|
|
return Sys_LoadModule( pDLLName );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Methods to load + unload DLLs
|
|
//-----------------------------------------------------------------------------
|
|
AppModule_t CAppSystemGroup::LoadModule( const char *pDLLName )
|
|
{
|
|
// Remove the extension when creating the name.
|
|
int nLen = Q_strlen( pDLLName ) + 1;
|
|
char *pModuleName = (char*)stackalloc( nLen );
|
|
Q_StripExtension( pDLLName, pModuleName, nLen );
|
|
|
|
// See if we already loaded it...
|
|
for ( int i = m_Modules.Count(); --i >= 0; )
|
|
{
|
|
if ( m_Modules[i].m_pModuleName )
|
|
{
|
|
if ( !Q_stricmp( pModuleName, m_Modules[i].m_pModuleName ) )
|
|
return i;
|
|
}
|
|
}
|
|
|
|
CSysModule *pSysModule = LoadModuleDLL( pDLLName );
|
|
if (!pSysModule)
|
|
{
|
|
#ifdef _X360
|
|
Warning("AppFramework : Unable to load module %s! (err #%d)\n", pDLLName, GetLastError() );
|
|
#else
|
|
Warning("AppFramework : Unable to load module %s!\n", pDLLName );
|
|
#endif
|
|
return APP_MODULE_INVALID;
|
|
}
|
|
|
|
int nIndex = m_Modules.AddToTail();
|
|
m_Modules[nIndex].m_pModule = pSysModule;
|
|
m_Modules[nIndex].m_Factory = 0;
|
|
m_Modules[nIndex].m_pModuleName = (char*)malloc( nLen );
|
|
Q_strncpy( m_Modules[nIndex].m_pModuleName, pModuleName, nLen );
|
|
|
|
return nIndex;
|
|
}
|
|
|
|
|
|
int CAppSystemGroup::ReloadModule( const char * pDLLName )
|
|
{
|
|
// Remove the extension when creating the name.
|
|
int nLen = Q_strlen( pDLLName ) + 1;
|
|
char *pModuleName = (char*)stackalloc( nLen );
|
|
Q_StripExtension( pDLLName, pModuleName, nLen );
|
|
|
|
// See if we already loaded it...
|
|
for ( int i = m_Modules.Count(); --i >= 0; )
|
|
{
|
|
Module_t &module = m_Modules[i];
|
|
if ( module.m_pModuleName && !Q_stricmp( pModuleName, module.m_pModuleName ) )
|
|
{
|
|
// found the module, reload
|
|
Msg("Unloading module %s, dll %s\n", pModuleName, pDLLName );
|
|
Sys_UnloadModule( m_Modules[i].m_pModule );
|
|
Msg("Module %s unloaded, reloading\n", pModuleName );
|
|
CSysModule *pSysModule = NULL;
|
|
CreateInterfaceFn fnFactory = NULL;
|
|
while( !pSysModule )
|
|
{
|
|
pSysModule = LoadModuleDLL( pDLLName );
|
|
if( !pSysModule )
|
|
{
|
|
Warning("Cannot load, retrying in 5 seconds..\n");
|
|
ThreadSleep( 5000 );
|
|
}
|
|
fnFactory = Sys_GetFactory( pSysModule ) ;
|
|
if( !fnFactory )
|
|
{
|
|
Error( "Could not get factory from %s\n", pModuleName );
|
|
}
|
|
( *fnFactory )( "Reload Interface", NULL ); // let the CreateInterface function work and do after-reload stuff
|
|
}
|
|
|
|
Msg( "Reload complete, module %p->%p, factory %llx->%llx\n", module.m_pModule, pSysModule, (uint64)(uintp)module.m_Factory, (uint64)(uintp)fnFactory );
|
|
module.m_pModule = pSysModule;
|
|
if( module.m_Factory )
|
|
{ // don't reload factory pointer unless it was initialized to non-NULL
|
|
module.m_Factory = fnFactory;
|
|
}
|
|
|
|
return 0; // no error
|
|
}
|
|
}
|
|
|
|
Warning( "No such module: '%s' in appsystem @%p. Dumping available modules:\n", pModuleName, this );
|
|
for ( int i = 0; i < m_Modules.Count(); ++i )
|
|
{
|
|
Module_t &module = m_Modules[i];
|
|
#ifdef _PS3
|
|
Msg( "%25s %llx %p %6d %6d bytes\n", module.m_pModuleName, (uint64)module.m_Factory, module.m_pModule, ( ( PS3_PrxLoadParametersBase_t *)module.m_pModule )->sysPrxId, ( ( PS3_PrxLoadParametersBase_t *)module.m_pModule )->cbSize );
|
|
#else
|
|
Msg("%25s %p %p\n", module.m_pModuleName, (void*)module.m_Factory, module.m_pModule );
|
|
#endif
|
|
}
|
|
|
|
return m_pParentAppSystem ? m_pParentAppSystem->ReloadModule( pDLLName ) : -1;
|
|
}
|
|
|
|
|
|
AppModule_t CAppSystemGroup::LoadModule( CreateInterfaceFn factory )
|
|
{
|
|
if (!factory)
|
|
{
|
|
Warning("AppFramework : Unable to load module %p!\n", factory );
|
|
return APP_MODULE_INVALID;
|
|
}
|
|
|
|
// See if we already loaded it...
|
|
for ( int i = m_Modules.Count(); --i >= 0; )
|
|
{
|
|
if ( m_Modules[i].m_Factory )
|
|
{
|
|
if ( m_Modules[i].m_Factory == factory )
|
|
return i;
|
|
}
|
|
}
|
|
|
|
int nIndex = m_Modules.AddToTail();
|
|
m_Modules[nIndex].m_pModule = NULL;
|
|
m_Modules[nIndex].m_Factory = factory;
|
|
m_Modules[nIndex].m_pModuleName = NULL;
|
|
return nIndex;
|
|
}
|
|
|
|
void CAppSystemGroup::UnloadAllModules()
|
|
{
|
|
// NOTE: Iterate in reverse order so they are unloaded in opposite order
|
|
// from loading
|
|
for (int i = m_Modules.Count(); --i >= 0; )
|
|
{
|
|
if ( m_Modules[i].m_pModule )
|
|
{
|
|
Sys_UnloadModule( m_Modules[i].m_pModule );
|
|
}
|
|
if ( m_Modules[i].m_pModuleName )
|
|
{
|
|
free( m_Modules[i].m_pModuleName );
|
|
}
|
|
}
|
|
m_Modules.RemoveAll();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Methods to add/remove various global singleton systems
|
|
//-----------------------------------------------------------------------------
|
|
IAppSystem *CAppSystemGroup::AddSystem( AppModule_t module, const char *pInterfaceName )
|
|
{
|
|
if (module == APP_MODULE_INVALID)
|
|
return NULL;
|
|
|
|
int nFoundIndex = m_SystemDict.Find( pInterfaceName );
|
|
if ( nFoundIndex != m_SystemDict.InvalidIndex() )
|
|
{
|
|
Warning("AppFramework : Attempted to add two systems with the same interface name %s!\n", pInterfaceName );
|
|
return m_Systems[ m_SystemDict[nFoundIndex] ];
|
|
}
|
|
|
|
Assert( (module >= 0) && (module < m_Modules.Count()) );
|
|
CreateInterfaceFn pFactory = m_Modules[module].m_pModule ? Sys_GetFactory( m_Modules[module].m_pModule ) : m_Modules[module].m_Factory;
|
|
|
|
int retval;
|
|
void *pSystem = pFactory( pInterfaceName, &retval );
|
|
if ((retval != IFACE_OK) || (!pSystem))
|
|
{
|
|
Warning("AppFramework : Unable to create system %s!\n", pInterfaceName );
|
|
return NULL;
|
|
}
|
|
|
|
IAppSystem *pAppSystem = static_cast<IAppSystem*>(pSystem);
|
|
|
|
int sysIndex = m_Systems.AddToTail( pAppSystem );
|
|
|
|
// Inserting into the dict will help us do named lookup later
|
|
MEM_ALLOC_CREDIT();
|
|
m_SystemDict.Insert( pInterfaceName, sysIndex );
|
|
return pAppSystem;
|
|
}
|
|
|
|
static const char *g_StageLookup[] =
|
|
{
|
|
"CREATION",
|
|
"LOADING DEPENDENCIES",
|
|
"CONNECTION",
|
|
"PREINITIALIZATION",
|
|
"INITIALIZATION",
|
|
"POSTINITIALIZATION",
|
|
"RUNNING",
|
|
"PRESHUTDOWN",
|
|
"SHUTDOWN",
|
|
"POSTSHUTDOWN",
|
|
"DISCONNECTION",
|
|
"DESTRUCTION",
|
|
};
|
|
|
|
void CAppSystemGroup::ReportStartupFailure( int nErrorStage, int nSysIndex )
|
|
{
|
|
COMPILE_TIME_ASSERT( APPSYSTEM_GROUP_STAGE_COUNT == ARRAYSIZE( g_StageLookup ) );
|
|
|
|
const char *pszStageDesc = "Unknown";
|
|
if ( nErrorStage >= 0 && nErrorStage < ( int )ARRAYSIZE( g_StageLookup ) )
|
|
{
|
|
pszStageDesc = g_StageLookup[ nErrorStage ];
|
|
}
|
|
|
|
const char *pszSystemName = "(Unknown)";
|
|
for ( int i = m_SystemDict.First(); i != m_SystemDict.InvalidIndex(); i = m_SystemDict.Next( i ) )
|
|
{
|
|
if ( m_SystemDict[ i ] != nSysIndex )
|
|
continue;
|
|
|
|
pszSystemName = m_SystemDict.GetElementName( i );
|
|
break;
|
|
}
|
|
|
|
// Walk the dictionary
|
|
Warning( "System (%s) failed during stage %s\n", pszSystemName, pszStageDesc );
|
|
}
|
|
|
|
void CAppSystemGroup::AddSystem( IAppSystem *pAppSystem, const char *pInterfaceName )
|
|
{
|
|
if ( !pAppSystem )
|
|
return;
|
|
|
|
int sysIndex = m_Systems.AddToTail( pAppSystem );
|
|
|
|
// Inserting into the dict will help us do named lookup later
|
|
MEM_ALLOC_CREDIT();
|
|
m_SystemDict.Insert( pInterfaceName, sysIndex );
|
|
}
|
|
|
|
void CAppSystemGroup::RemoveAllSystems()
|
|
{
|
|
// NOTE: There's no deallcation here since we don't really know
|
|
// how the allocation has happened. We could add a deallocation method
|
|
// to the code in interface.h; although when the modules are unloaded
|
|
// the deallocation will happen anyways
|
|
m_Systems.RemoveAll();
|
|
m_SystemDict.RemoveAll();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Simpler method of doing the LoadModule/AddSystem thing.
|
|
//-----------------------------------------------------------------------------
|
|
bool CAppSystemGroup::AddSystems( AppSystemInfo_t *pSystemList )
|
|
{
|
|
while ( pSystemList->m_pModuleName[0] )
|
|
{
|
|
AppModule_t module = LoadModule( pSystemList->m_pModuleName );
|
|
IAppSystem *pSystem = AddSystem( module, pSystemList->m_pInterfaceName );
|
|
if ( !pSystem )
|
|
{
|
|
Warning( "Unable to load interface %s from %s, requested from EXE.\n", pSystemList->m_pInterfaceName, pSystemList->m_pModuleName );
|
|
return false;
|
|
}
|
|
++pSystemList;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Methods to find various global singleton systems
|
|
//-----------------------------------------------------------------------------
|
|
void *CAppSystemGroup::FindSystem( const char *pSystemName )
|
|
{
|
|
unsigned short i = m_SystemDict.Find( pSystemName );
|
|
if (i != m_SystemDict.InvalidIndex())
|
|
return m_Systems[m_SystemDict[i]];
|
|
|
|
// If it's not an interface we know about, it could be an older
|
|
// version of an interface, or maybe something implemented by
|
|
// one of the instantiated interfaces...
|
|
|
|
// QUESTION: What order should we iterate this in?
|
|
// It controls who wins if multiple ones implement the same interface
|
|
for ( i = 0; i < m_Systems.Count(); ++i )
|
|
{
|
|
void *pInterface = m_Systems[i]->QueryInterface( pSystemName );
|
|
if (pInterface)
|
|
return pInterface;
|
|
}
|
|
|
|
int nExternalCount = m_NonAppSystemFactories.Count();
|
|
for ( i = 0; i < nExternalCount; ++i )
|
|
{
|
|
void *pInterface = m_NonAppSystemFactories[i]( pSystemName, NULL );
|
|
if (pInterface)
|
|
return pInterface;
|
|
}
|
|
|
|
if ( m_pParentAppSystem )
|
|
{
|
|
void* pInterface = m_pParentAppSystem->FindSystem( pSystemName );
|
|
if ( pInterface )
|
|
return pInterface;
|
|
}
|
|
|
|
// No dice..
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Adds a factory to the system so other stuff can query it. Triggers a connect systems
|
|
//-----------------------------------------------------------------------------
|
|
void CAppSystemGroup::AddNonAppSystemFactory( CreateInterfaceFn fn )
|
|
{
|
|
m_NonAppSystemFactories.AddToTail( fn );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Removes a factory, triggers a disconnect call if it succeeds
|
|
//-----------------------------------------------------------------------------
|
|
void CAppSystemGroup::RemoveNonAppSystemFactory( CreateInterfaceFn fn )
|
|
{
|
|
m_NonAppSystemFactories.FindAndRemove( fn );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Causes the systems to reconnect to an interface
|
|
//-----------------------------------------------------------------------------
|
|
void CAppSystemGroup::ReconnectSystems( const char *pInterfaceName )
|
|
{
|
|
// Let the libraries regrab the specified interface
|
|
for (int i = 0; i < m_Systems.Count(); ++i )
|
|
{
|
|
IAppSystem *pSystem = m_Systems[i];
|
|
pSystem->Reconnect( GetFactory(), pInterfaceName );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets at the parent appsystem group
|
|
//-----------------------------------------------------------------------------
|
|
CAppSystemGroup *CAppSystemGroup::GetParent()
|
|
{
|
|
return m_pParentAppSystem;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Deals with sorting dependencies and finding circular dependencies
|
|
//-----------------------------------------------------------------------------
|
|
void CAppSystemGroup::ComputeDependencies( LibraryDependencies_t &depend )
|
|
{
|
|
bool bDone = false;
|
|
while ( !bDone )
|
|
{
|
|
bDone = true;
|
|
|
|
// If i depends on j, then i depends on what j depends on
|
|
// Add secondary dependencies to i. We stop when no dependencies are added
|
|
int nCount = depend.GetNumStrings();
|
|
for ( int i = 0; i < nCount; ++i )
|
|
{
|
|
int nDependentCount = depend[i].GetNumStrings();
|
|
for ( int j = 0; j < nDependentCount; ++j )
|
|
{
|
|
int nIndex = depend.Find( depend[i].String( j ) );
|
|
if ( nIndex == UTL_INVAL_SYMBOL )
|
|
continue;
|
|
|
|
int nSecondaryDepCount = depend[nIndex].GetNumStrings();
|
|
for ( int k = 0; k < nSecondaryDepCount; ++k )
|
|
{
|
|
// Don't bother if we already contain the secondary dependency
|
|
const char *pSecondaryDependency = depend[nIndex].String( k );
|
|
if ( depend[i].Find( pSecondaryDependency ) != UTL_INVAL_SYMBOL )
|
|
continue;
|
|
|
|
// Check for circular dependency
|
|
if ( !Q_stricmp( pSecondaryDependency, depend.String( i ) ) )
|
|
{
|
|
Warning( "Encountered a circular dependency with library %s!\n", pSecondaryDependency );
|
|
continue;
|
|
}
|
|
|
|
bDone = false;
|
|
depend[i].AddString( pSecondaryDependency );
|
|
nDependentCount = depend[i].GetNumStrings();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sorts dependencies
|
|
//-----------------------------------------------------------------------------
|
|
CAppSystemGroup::LibraryDependencies_t *CAppSystemGroup::sm_pSortDependencies;
|
|
bool CAppSystemGroup::SortLessFunc( const int &left, const int &right )
|
|
{
|
|
const char *pLeftInterface = sm_pSortDependencies->String( left );
|
|
const char *pRightInterface = sm_pSortDependencies->String( right );
|
|
bool bRightDependsOnLeft = ( (*sm_pSortDependencies)[pRightInterface].Find( pLeftInterface ) != UTL_INVAL_SYMBOL );
|
|
return ( bRightDependsOnLeft );
|
|
}
|
|
|
|
void CAppSystemGroup::SortDependentLibraries( LibraryDependencies_t &depend )
|
|
{
|
|
int nCount = depend.GetNumStrings();
|
|
|
|
int *pIndices = (int*)stackalloc( depend.GetNumStrings() * sizeof(int) );
|
|
for ( int i = 0; i < nCount; ++i )
|
|
{
|
|
pIndices[i] = i;
|
|
}
|
|
|
|
// Sort by dependency. Can't use fancy stl algorithms here because the sort func isn't strongly transitive.
|
|
// Using lame bubble sort instead. We could speed this up using a proper depth-first graph walk, but it's not worth the effort.
|
|
sm_pSortDependencies = &depend;
|
|
bool bChanged = true;
|
|
while ( bChanged )
|
|
{
|
|
bChanged = false;
|
|
for ( int i = 1; i < nCount; i++ )
|
|
{
|
|
for ( int j = 0; j < i; j++ )
|
|
{
|
|
if ( SortLessFunc( pIndices[i], pIndices[j] ) )
|
|
{
|
|
int nTmp = pIndices[i];
|
|
pIndices[i] = pIndices[j];
|
|
pIndices[j] = nTmp;
|
|
bChanged = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
sm_pSortDependencies = NULL;
|
|
|
|
|
|
// This logic will make it so it respects the specified initialization order
|
|
// in the face of no dependencies telling the system otherwise.
|
|
// Doing this just for safety to reduce the amount of changed code
|
|
bool bDone = false;
|
|
while ( !bDone )
|
|
{
|
|
bDone = true;
|
|
for ( int i = 1; i < nCount; ++i )
|
|
{
|
|
int nLeft = pIndices[i-1];
|
|
int nRight = pIndices[i];
|
|
if ( nRight > nLeft )
|
|
continue;
|
|
|
|
const char *pLeftInterface = depend.String( nLeft );
|
|
const char *pRightInterface = depend.String( nRight );
|
|
bool bRightDependsOnLeft = ( depend[pRightInterface].Find( pLeftInterface ) != UTL_INVAL_SYMBOL );
|
|
if ( bRightDependsOnLeft )
|
|
continue;
|
|
Assert ( UTL_INVAL_SYMBOL == depend[pRightInterface].Find( pLeftInterface ) );
|
|
V_swap( pIndices[i], pIndices[i-1] );
|
|
bDone = false;
|
|
}
|
|
}
|
|
|
|
// Reorder appsystem list + dictionary indexing
|
|
Assert( m_Systems.Count() == nCount );
|
|
int nTempSize = nCount * sizeof(IAppSystem*);
|
|
IAppSystem **pTemp = (IAppSystem**)stackalloc( nTempSize );
|
|
memcpy( pTemp, m_Systems.Base(), nTempSize );
|
|
for ( int i = 0; i < nCount; ++i )
|
|
{
|
|
m_Systems[i] = pTemp[ pIndices[i] ];
|
|
}
|
|
|
|
// Remap system indices
|
|
for ( uint16 i = m_SystemDict.First(); i != m_SystemDict.InvalidIndex(); i = m_SystemDict.Next( i ) )
|
|
{
|
|
int j = 0;
|
|
for ( ; j < nCount; ++j )
|
|
{
|
|
if ( pIndices[j] == m_SystemDict[i] )
|
|
{
|
|
m_SystemDict[i] = j;
|
|
break;
|
|
}
|
|
}
|
|
Assert( j != nCount );
|
|
}
|
|
|
|
( void )stackfree( pTemp );
|
|
( void )stackfree( pIndices );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Finds appsystem names
|
|
//-----------------------------------------------------------------------------
|
|
const char *CAppSystemGroup::FindSystemName( int nIndex )
|
|
{
|
|
for ( uint16 i = m_SystemDict.First(); i != m_SystemDict.InvalidIndex(); i = m_SystemDict.Next( i ) )
|
|
{
|
|
if ( m_SystemDict[i] == nIndex )
|
|
return m_SystemDict.GetElementName( i );
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Method to load all dependent systems
|
|
//-----------------------------------------------------------------------------
|
|
bool CAppSystemGroup::LoadDependentSystems()
|
|
{
|
|
LibraryDependencies_t dependencies;
|
|
|
|
// First, load dependencies.
|
|
for ( int i = 0; i < m_Systems.Count(); ++i )
|
|
{
|
|
IAppSystem *pSystem = m_Systems[i];
|
|
const char *pInterfaceName = FindSystemName( i );
|
|
dependencies.AddString( pInterfaceName );
|
|
|
|
const AppSystemInfo_t *pDependencies = pSystem->GetDependencies();
|
|
if ( !pDependencies )
|
|
continue;
|
|
|
|
for ( ; pDependencies->m_pInterfaceName && pDependencies->m_pInterfaceName[0]; ++pDependencies )
|
|
{
|
|
dependencies[ pInterfaceName ].AddString( pDependencies->m_pInterfaceName );
|
|
|
|
CreateInterfaceFn factory = GetFactory();
|
|
if ( factory( pDependencies->m_pInterfaceName, NULL ) )
|
|
continue;
|
|
|
|
AppModule_t module = LoadModule( pDependencies->m_pModuleName );
|
|
IAppSystem *pSystem = AddSystem( module, pDependencies->m_pInterfaceName );
|
|
if ( !pSystem )
|
|
{
|
|
Warning( "Unable to load interface %s from %s (Dependency of %s)\n", pDependencies->m_pInterfaceName, pDependencies->m_pModuleName, pInterfaceName );
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
ComputeDependencies( dependencies );
|
|
SortDependentLibraries( dependencies );
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Method to connect/disconnect all systems
|
|
//-----------------------------------------------------------------------------
|
|
bool CAppSystemGroup::ConnectSystems()
|
|
{
|
|
// Let the libraries grab any other interfaces they may need
|
|
for (int i = 0; i < m_Systems.Count(); ++i )
|
|
{
|
|
IAppSystem *pSystem = m_Systems[i];
|
|
if ( !pSystem->Connect( GetFactory() ) )
|
|
{
|
|
ReportStartupFailure( CONNECTION, i );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CAppSystemGroup::DisconnectSystems()
|
|
{
|
|
// Disconnect in reverse order of connection
|
|
for (int i = m_Systems.Count(); --i >= 0; )
|
|
{
|
|
m_Systems[i]->Disconnect();
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Method to initialize/shutdown all systems
|
|
//-----------------------------------------------------------------------------
|
|
InitReturnVal_t CAppSystemGroup::InitSystems()
|
|
{
|
|
for (int nSystemsInitialized = 0; nSystemsInitialized < m_Systems.Count(); ++nSystemsInitialized )
|
|
{
|
|
InitReturnVal_t nRetVal = m_Systems[nSystemsInitialized]->Init();
|
|
if ( nRetVal != INIT_OK )
|
|
{
|
|
for( int nSystemsRewind = nSystemsInitialized; nSystemsRewind-->0; )
|
|
{
|
|
m_Systems[nSystemsRewind]->Shutdown();
|
|
}
|
|
|
|
ReportStartupFailure( INITIALIZATION, nSystemsInitialized );
|
|
return nRetVal;
|
|
}
|
|
}
|
|
return INIT_OK;
|
|
}
|
|
|
|
void CAppSystemGroup::ShutdownSystems()
|
|
{
|
|
// Shutdown in reverse order of initialization
|
|
for (int i = m_Systems.Count(); --i >= 0; )
|
|
{
|
|
m_Systems[i]->Shutdown();
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Window management
|
|
//-----------------------------------------------------------------------------
|
|
void* CAppSystemGroup::CreateAppWindow( void *hInstance, const char *pTitle, bool bWindowed, int w, int h, bool bResizing )
|
|
{
|
|
#if defined( PLATFORM_WINDOWS ) || defined( PLATFORM_OSX )
|
|
int nFlags = 0;
|
|
if ( !bWindowed )
|
|
{
|
|
nFlags |= WINDOW_CREATE_FULLSCREEN;
|
|
}
|
|
if ( bResizing )
|
|
{
|
|
nFlags |= WINDOW_CREATE_RESIZING;
|
|
}
|
|
|
|
PlatWindow_t hWnd = Plat_CreateWindow( hInstance, pTitle, w, h, nFlags );
|
|
if ( hWnd == PLAT_WINDOW_INVALID )
|
|
return NULL;
|
|
|
|
int CenterX, CenterY;
|
|
Plat_GetDesktopResolution( &CenterX, &CenterY );
|
|
CenterX = ( CenterX - w ) / 2;
|
|
CenterY = ( CenterY - h ) / 2;
|
|
CenterX = (CenterX < 0) ? 0: CenterX;
|
|
CenterY = (CenterY < 0) ? 0: CenterY;
|
|
|
|
// In VCR modes, keep it in the upper left so mouse coordinates are always relative to the window.
|
|
Plat_SetWindowPos( hWnd, CenterX, CenterY );
|
|
|
|
return hWnd;
|
|
#elif defined( PLATFORM_OSX )
|
|
extern ICocoaMgr *g_pCocoaMgr;
|
|
g_pCocoaMgr->CreateGameWindow( pTitle, bWindowed, w, h );
|
|
return (void*)Sys_GetFactoryThis(); // Other stuff will query for ICocoaBridge out of this.
|
|
#elif defined( PLATFORM_LINUX )
|
|
#ifndef DEDICATED
|
|
|
|
// PBTODO
|
|
// extern IGLXMgr *g_pGLXMgr;
|
|
// g_pGLXMgr->CreateWindow( pTitle, bWindowed, w, h );
|
|
return (void*)Sys_GetFactoryThis(); // Other stuff will query for ICocoaBridge out of this.
|
|
#endif
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
void CAppSystemGroup::SetAppWindowTitle( void* hWnd, const char *pTitle )
|
|
{
|
|
Plat_SetWindowTitle( (PlatWindow_t)hWnd, pTitle );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the stage at which the app system group ran into an error
|
|
//-----------------------------------------------------------------------------
|
|
CAppSystemGroup::AppSystemGroupStage_t CAppSystemGroup::GetCurrentStage() const
|
|
{
|
|
return m_nCurrentStage;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets at a factory that works just like FindSystem
|
|
//-----------------------------------------------------------------------------
|
|
// This function is used to make this system appear to the outside world to
|
|
// function exactly like the currently existing factory system
|
|
CAppSystemGroup *s_pCurrentAppSystem;
|
|
void *AppSystemCreateInterfaceFn(const char *pName, int *pReturnCode)
|
|
{
|
|
void *pInterface = s_pCurrentAppSystem->FindSystem( pName );
|
|
if ( pReturnCode )
|
|
{
|
|
*pReturnCode = pInterface ? IFACE_OK : IFACE_FAILED;
|
|
}
|
|
return pInterface;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets at a class factory for the topmost appsystem group in an appsystem stack
|
|
//-----------------------------------------------------------------------------
|
|
CreateInterfaceFn CAppSystemGroup::GetFactory()
|
|
{
|
|
return AppSystemCreateInterfaceFn;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Main application loop
|
|
//-----------------------------------------------------------------------------
|
|
int CAppSystemGroup::Run()
|
|
{
|
|
// The factory now uses this app system group
|
|
s_pCurrentAppSystem = this;
|
|
|
|
// Load, connect, init
|
|
int nRetVal = OnStartup();
|
|
|
|
// NOTE: In case of OnStartup Failure
|
|
// On PS/3, not unloading the PRXes in order will cause crashes on quit, which is a TRC failure
|
|
// We probably should, but don't have to do this on all platforms, since it's not required to clean-up crash-free.
|
|
|
|
if ( m_nCurrentStage == RUNNING )
|
|
{
|
|
// Main loop implemented by the application
|
|
// FIXME: HACK workaround to avoid vgui porting
|
|
nRetVal = Main();
|
|
}
|
|
|
|
// Shutdown, disconnect, unload
|
|
OnShutdown();
|
|
|
|
// The factory now uses the parent's app system group
|
|
s_pCurrentAppSystem = GetParent();
|
|
|
|
return nRetVal;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Virtual methods for override
|
|
//-----------------------------------------------------------------------------
|
|
int CAppSystemGroup::Startup()
|
|
{
|
|
return OnStartup();
|
|
}
|
|
|
|
void CAppSystemGroup::Shutdown()
|
|
{
|
|
return OnShutdown();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Use this version in cases where you can't control the main loop and
|
|
// expect to be ticked
|
|
//-----------------------------------------------------------------------------
|
|
int CAppSystemGroup::OnStartup()
|
|
{
|
|
// The factory now uses this app system group
|
|
s_pCurrentAppSystem = this;
|
|
|
|
// Call an installed application creation function
|
|
m_nCurrentStage = CREATION;
|
|
if ( !Create() )
|
|
return -1;
|
|
|
|
// Load dependent libraries
|
|
m_nCurrentStage = DEPENDENCIES;
|
|
if ( !LoadDependentSystems() )
|
|
return -1;
|
|
|
|
// Let all systems know about each other
|
|
m_nCurrentStage = CONNECTION;
|
|
if ( !ConnectSystems() )
|
|
return -1;
|
|
|
|
// Allow the application to do some work before init
|
|
m_nCurrentStage = PREINITIALIZATION;
|
|
if ( !PreInit() )
|
|
return -1;
|
|
|
|
// Call Init on all App Systems
|
|
m_nCurrentStage = INITIALIZATION;
|
|
int nRetVal = InitSystems();
|
|
if ( nRetVal != INIT_OK )
|
|
return -1;
|
|
|
|
m_nCurrentStage = POSTINITIALIZATION;
|
|
if ( !PostInit() )
|
|
return -1;
|
|
|
|
m_nCurrentStage = RUNNING;
|
|
return nRetVal;
|
|
}
|
|
|
|
void CAppSystemGroup::OnShutdown()
|
|
{
|
|
// The factory now uses this app system group
|
|
s_pCurrentAppSystem = this;
|
|
|
|
switch( m_nCurrentStage )
|
|
{
|
|
case RUNNING:
|
|
case POSTINITIALIZATION:
|
|
break;
|
|
|
|
case PREINITIALIZATION:
|
|
case INITIALIZATION:
|
|
goto disconnect;
|
|
|
|
case CREATION:
|
|
case DEPENDENCIES:
|
|
case CONNECTION:
|
|
goto destroy;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Allow the application to do some work before shutdown
|
|
m_nCurrentStage = PRESHUTDOWN;
|
|
PreShutdown();
|
|
|
|
// Cal Shutdown on all App Systems
|
|
m_nCurrentStage = SHUTDOWN;
|
|
ShutdownSystems();
|
|
|
|
// Allow the application to do some work after shutdown
|
|
m_nCurrentStage = POSTSHUTDOWN;
|
|
PostShutdown();
|
|
|
|
disconnect:
|
|
// Systems should disconnect from each other
|
|
m_nCurrentStage = DISCONNECTION;
|
|
DisconnectSystems();
|
|
|
|
destroy:
|
|
// Unload all DLLs loaded in the AppCreate block
|
|
m_nCurrentStage = DESTRUCTION;
|
|
RemoveAllSystems();
|
|
|
|
// Have to do this because the logging listeners & response policies may live in modules which are being unloaded
|
|
// @TODO: this seems like a bad legacy practice... app systems should unload their spew handlers gracefully.
|
|
LoggingSystem_ResetCurrentLoggingState();
|
|
Assert( g_pDefaultLoggingListener != NULL );
|
|
LoggingSystem_RegisterLoggingListener( g_pDefaultLoggingListener );
|
|
|
|
UnloadAllModules();
|
|
|
|
// Call an installed application destroy function
|
|
Destroy();
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// This class represents a group of app systems that are loaded through steam
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Constructor
|
|
//-----------------------------------------------------------------------------
|
|
CSteamAppSystemGroup::CSteamAppSystemGroup( IFileSystem *pFileSystem, CAppSystemGroup *pAppSystemParent )
|
|
{
|
|
m_pFileSystem = pFileSystem;
|
|
m_pGameInfoPath[0] = 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Used by CSteamApplication to set up necessary pointers if we can't do it in the constructor
|
|
//-----------------------------------------------------------------------------
|
|
void CSteamAppSystemGroup::Setup( IFileSystem *pFileSystem, CAppSystemGroup *pParentAppSystem )
|
|
{
|
|
m_pFileSystem = pFileSystem;
|
|
m_pParentAppSystem = pParentAppSystem;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Loads the module from Steam
|
|
//-----------------------------------------------------------------------------
|
|
CSysModule *CSteamAppSystemGroup::LoadModuleDLL( const char *pDLLName )
|
|
{
|
|
return m_pFileSystem->LoadModule( pDLLName );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the game info path
|
|
//-----------------------------------------------------------------------------
|
|
const char *CSteamAppSystemGroup::GetGameInfoPath() const
|
|
{
|
|
return m_pGameInfoPath;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets up the search paths
|
|
//-----------------------------------------------------------------------------
|
|
bool CSteamAppSystemGroup::SetupSearchPaths( const char *pStartingDir, bool bOnlyUseStartingDir, bool bIsTool )
|
|
{
|
|
CFSSteamSetupInfo steamInfo;
|
|
steamInfo.m_pDirectoryName = pStartingDir;
|
|
steamInfo.m_bOnlyUseDirectoryName = bOnlyUseStartingDir;
|
|
steamInfo.m_bToolsMode = bIsTool;
|
|
steamInfo.m_bSetSteamDLLPath = true;
|
|
steamInfo.m_bSteam = m_pFileSystem->IsSteam();
|
|
if ( FileSystem_SetupSteamEnvironment( steamInfo ) != FS_OK )
|
|
return false;
|
|
|
|
CFSMountContentInfo fsInfo;
|
|
fsInfo.m_pFileSystem = m_pFileSystem;
|
|
fsInfo.m_bToolsMode = bIsTool;
|
|
fsInfo.m_pDirectoryName = steamInfo.m_GameInfoPath;
|
|
|
|
if ( FileSystem_MountContent( fsInfo ) != FS_OK )
|
|
return false;
|
|
|
|
// Finally, load the search paths for the "GAME" path.
|
|
CFSSearchPathsInit searchPathsInit;
|
|
searchPathsInit.m_pDirectoryName = steamInfo.m_GameInfoPath;
|
|
searchPathsInit.m_pFileSystem = fsInfo.m_pFileSystem;
|
|
if ( FileSystem_LoadSearchPaths( searchPathsInit ) != FS_OK )
|
|
return false;
|
|
|
|
FileSystem_AddSearchPath_Platform( fsInfo.m_pFileSystem, steamInfo.m_GameInfoPath );
|
|
Q_strncpy( m_pGameInfoPath, steamInfo.m_GameInfoPath, sizeof(m_pGameInfoPath) );
|
|
return true;
|
|
}
|