The-Simpsons-Hit-and-Run/game/code/loading/loadingmanager.cpp

649 lines
19 KiB
C++

//=============================================================================
// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
//
// File: loadingmanager.cpp
//
// Description: Implementation for the LoadingManager class.
//
// History: + Created -- Darwin Chau
//
//=============================================================================
//========================================
// System Includes
//========================================
// Ftech
#include <raddebug.hpp>
#include <radtime.hpp>
#include <radfile.hpp>
#include <string.h>
//========================================
// Project Includes
//========================================
#include <loading/loadingmanager.h>
#include <loading/filehandlerfactory.h>
#include <loading/cementfilehandler.h>
#include <memory/srrmemory.h>
#include <debug/profiler.h>
#include <radtextdisplay.hpp>
#include <main/game.h>
#include <cheats/cheatinputsystem.h>
#include <mission/gameplaymanager.h>
//******************************************************************************
//
// Global Data, Local Data, Local Classes
//
//******************************************************************************
// Static pointer to instance of singleton.
LoadingManager* LoadingManager::spInstance = NULL;
//******************************************************************************
//
// Public Member Functions
//
//******************************************************************************
//==============================================================================
// LoadingManager::GetInstance
//==============================================================================
//
// Description: Access point for the LoadingManager singleton.
//
// Parameters: None.
//
// Return: Pointer to the LoadingManager.
//
// Constraints: This is a singleton so only one instance is allowed.
//
//==============================================================================
LoadingManager* LoadingManager::GetInstance()
{
rAssert( spInstance != NULL );
return spInstance;
}
//==============================================================================
// LoadingManager::CreateInstance
//==============================================================================
//
// Description: Constructs the LoadingManager singleton.
//
// Parameters: None.
//
// Return: Pointer to the LoadingManager.
//
// Constraints: This is a singleton so only one instance is allowed.
//
//==============================================================================
LoadingManager* LoadingManager::CreateInstance()
{
MEMTRACK_PUSH_GROUP( "LoadingManager" );
rAssert( spInstance == NULL );
#ifdef RAD_GAMECUBE
HeapMgr()->PushHeap( GMA_GC_VMM );
#else
HeapMgr()->PushHeap( GMA_PERSISTENT );
#endif
spInstance = new LoadingManager;
rAssert( spInstance );
#ifdef RAD_GAMECUBE
HeapMgr()->PopHeap( GMA_GC_VMM );
#else
HeapMgr()->PopHeap( GMA_PERSISTENT );
#endif
MEMTRACK_POP_GROUP("LoadingManager");
return spInstance;
}
//==============================================================================
// LoadingManager::DestroyInstance
//==============================================================================
//
// Description: Destroy the LoadingManager.
//
// Parameters: None.
//
// Return: None.
//
//==============================================================================
void LoadingManager::DestroyInstance()
{
rAssert( spInstance != NULL );
delete spInstance;
spInstance = NULL;
}
void LoadingManager::AddCallback( LoadingManager::ProcessRequestsCallback* pCallback, void* pUserData)
{
if ( mCancellingLoads )
{
return;
}
if(mRequestHead == mRequestTail && !mLoading )
{
pCallback->OnProcessRequestsComplete(pUserData);
return;
}
int newTail = (mRequestTail + 1) % MAX_REQUESTS;
rAssert( newTail != mRequestHead );
//rAssert(!mRequests[lastAddedRequest].pCallback);
mRequests[mRequestTail].pCallback = pCallback;
mRequests[mRequestTail].pUserData = pUserData;
mRequests[mRequestTail].filename[0] = '\0';
mRequestTail = newTail;
}
//==============================================================================
// LoadingManager::AddRequest
//==============================================================================
//
// Description: Clients use this method to submit a loading request.
// This request is not serviced immediately. The request remains
// queued until LoadingManager::ProcessRequests() is invoked.
//
// Parameters: handlerType - an enumeration is used to specify which
// file handler to use for loading and processing the data
// (the use of an enumeration keeps the clients of LoadingManager
// from being dependant on any FileHandler classes.
//
// filename - fully qualified path and name of file.
//
// secitonName - hack to allow p3d inventory sections to be created
// based on filenames
//
// groupName - this allows a memtag section to be declared.
//
// Return: true - on success
// false - request queue is full or loading is in progress
//
//==============================================================================
void LoadingManager::AddRequest
(
FileHandlerEnum handlerType,
const char* filename,
GameMemoryAllocator heap,
const char* sectionName,
const char* groupTag,
LoadingManager::ProcessRequestsCallback* pCallback,
void* pUserData
)
{
if ( mCancellingLoads )
{
return;
}
unsigned newTail = (mRequestTail + 1) % MAX_REQUESTS;
#ifndef FINAL
rReleaseAssertMsg( static_cast< int >( newTail ) != mRequestHead, "Too many load requests already!\n");
// Dusit [Dec 2, 2002]:
// Bad if we're overwriting the other load request. This is a fatal error.
if( static_cast<int>(newTail) == mRequestHead )
{
IRadTextDisplay* textDisplay;
::radTextDisplayGet( &textDisplay, GMA_DEFAULT );
if ( textDisplay )
{
textDisplay->SetBackgroundColor( 0 );
textDisplay->SetTextColor( 0xffffffff );
textDisplay->Clear();
textDisplay->TextOutAt( "TOO MANY LOAD REQUESTS!", 20, 10 );
textDisplay->TextOutAt( ">:-8", 20, 14 );
if ( CommandLineOptions::Get( CLO_DEMO_TEST ) ||
GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_DEMO_TEST ) )
{
char buffy[32];
sprintf( buffy, "Demo Count: %d", GetGame()->GetDemoCount() );
textDisplay->TextOutAt( buffy, 20, 16 );
unsigned int time = GetGame()->GetTime();
unsigned int hours = time / 3600000;
unsigned int deltaTime = time % 3600000;
unsigned int minutes = deltaTime / 60000;
deltaTime = deltaTime % 60000;
unsigned int seconds = deltaTime / 1000;
deltaTime = deltaTime % 1000;
sprintf( buffy, "Time: %d:%d:%d.%d", hours, minutes, seconds, deltaTime );
textDisplay->TextOutAt( buffy, 20, 18 );
if ( GetGameplayManager() )
{
sprintf( buffy, "Level %d", GetGameplayManager()->GetCurrentLevelIndex() );
textDisplay->TextOutAt( buffy, 20, 20 );
}
}
textDisplay->SwapBuffers();
textDisplay->Release();
}
rReleaseBreak();
}
#endif
//mRequests[mNumRequests].filename = filename;
rAssert( strlen( filename ) < LoadingRequest::LOADING_FILENAME_LENGTH );
strcpy( mRequests[mRequestTail].filename, filename );
mRequests[mRequestTail].pFileHandler = FileHandlerFactory::CreateFileHandler( handlerType, sectionName );
mRequests[mRequestTail].pFileHandler->AddRef();
#ifdef MEMORYTRACKER_ENABLED
if( groupTag == NULL )
{
//
// Use the filename by default
//
rAssert( strlen( filename ) < LoadingRequest::LOADING_FILENAME_LENGTH );
strcpy( mRequests[mRequestTail].groupTag, filename );
}
else
{
rAssert( strlen( groupTag ) < LoadingRequest::LOADING_FILENAME_LENGTH );
strcpy( mRequests[mRequestTail].groupTag, groupTag );
}
#endif
mRequests[mRequestTail].heap = heap;
mRequests[mRequestTail].pCallback = pCallback;
mRequests[mRequestTail].pUserData = pUserData;
mRequestTail = newTail;
this->ProcessNextRequest();
}
//==============================================================================
// LoadingManager::OnLoadFileComplete
//==============================================================================
//
// Description: FileHandler's will invoke this callback when an asynchronous
// load completes.
//
// Parameters: pUserData - optional user data that was passed in when the
// load was requested
//
// Return: None.
//
//==============================================================================
void LoadingManager::OnLoadFileComplete( void* pUserData )
{
rAssert( (int)pUserData == mRequestHead );
// Display some debug info.
LoadingRequest& request = mRequests[mRequestHead ];
extern bool gLoadingSpew;
if ( !(CommandLineOptions::Get( CLO_NO_LOADING_SPEW )) )
{
rReleasePrintf( "<< END >> Async Loading: %s (%u msecs)\n",
request.filename,
radTimeGetMilliseconds() - request.startTime );
}
// Continue onto the next request.
mRequestHead = (mRequestHead + 1) % MAX_REQUESTS;
mLoading = false;
if(request.pCallback)
{
request.pCallback->OnProcessRequestsComplete(request.pUserData);
}
request.pFileHandler->Release();
this->ProcessNextRequest();
}
//==============================================================================
// LoadingManager::LoadSync
//==============================================================================
//
// Description: Load a file synchronously.
//
// Parameters: handlerType - an enumeration is used to specify which
// file handler to use for loading and processing the data
// (the use of an enumeration keeps the clients of LoadingManager
// from being dependant on any FileHandler classes.
//
// filename - fully qualified path and name of file.
//
// Return: None.
//
//==============================================================================
void LoadingManager::LoadSync
(
FileHandlerEnum handlerType,
const char* filename,
GameMemoryAllocator heap,
const char* sectionName
)
{
rReleasePrintf("\n\n!!!!!! TRC VIOLATION, USE ASYNC!!!!!!!\n\n");
// rAssert( false );
unsigned int startTime = radTimeGetMilliseconds();
if ( !(CommandLineOptions::Get( CLO_NO_LOADING_SPEW )) )
{
rDebugPrintf( "<<START>> Sync Loading: %s\n", filename );
}
HeapMgr()->PushHeap( GMA_TEMP );
FileHandler* pHandler = FileHandlerFactory::CreateFileHandler( handlerType, sectionName );
HeapMgr()->PopHeap( GMA_TEMP );
pHandler->AddRef( );
rAssert( pHandler );
HeapMgr()->PushHeap( heap );
pHandler->LoadFileSync( filename );
HeapMgr()->PopHeap( heap );
pHandler->Release( );
pHandler = 0;
if ( !(CommandLineOptions::Get( CLO_NO_LOADING_SPEW )) )
{
rReleasePrintf( "<< END >> Sync Loading: %s (%u msecs)\n",
filename,
radTimeGetMilliseconds() - startTime );
}
}
//=============================================================================
// LoadingManager::CancelPendingRequests
//=============================================================================
// Description: Comment
//
// Parameters: ()
//
// Return: void
//
//=============================================================================
void LoadingManager::CancelPendingRequests()
{
if ( mRequestHead != mRequestTail )
{
mRequestTail = (mRequestHead + 1) % MAX_REQUESTS; //Catch up and lose the rest of the loads.
}
mCancellingLoads = true;
while ( mLoading )
{
::radFileService();
p3d::loadManager->SwitchTask();
p3d::loadManager->TriggerCallbacks();
}
//Close all the open cement files.
CementFileHandle i;
for ( i = 0; i < MAX_CEMENT_LIBRARIES; ++i )
{
if ( mCementLibraries[ i ].library != NULL )
{
UnregisterCementLibrary( i );
}
}
}
//=============================================================================
// LoadingManager::RegisterCementLibrary
//=============================================================================
// Description: Registers the named cement library with RadFile.
//
// Parameters: filename - name of cement file
//
// Return: CementFileHandle - internally, its the index of the cement
// library pointer. Used for indicating which
// cement library to release later on
//
//=============================================================================
CementFileHandle LoadingManager::RegisterCementLibrary( const char* filename )
{
if ( mCancellingLoads )
{
return 0;
}
int i;
for( i = 0; i < MAX_CEMENT_LIBRARIES; i++ )
{
if( ( mCementLibraries[i].library == NULL )
&& ( mCementLibraries[i].isLoading == false ) )
{
break;
}
}
rAssertMsg( ( i < MAX_CEMENT_LIBRARIES ), "Too many cement libraries\n" );
//
// Put a file handler into the queue so that we'll wait until this
// thing is fully registered. This file handler is slightly different
// than the rest, and we're in a special-purpose function anyway, so don't
// go through the factory.
//
int newTail = (mRequestTail + 1) % MAX_REQUESTS;
if( newTail != mRequestHead)
{
rAssert( strlen( filename ) < LoadingRequest::LOADING_FILENAME_LENGTH );
strcpy( mRequests[mRequestTail].filename, filename );
#ifdef MEMORYTRACKER_ENABLED
strcpy( mRequests[mRequestTail].groupTag, filename );
#endif
#ifdef RAD_GAMECUBE
mRequests[mRequestTail].heap = GMA_GC_VMM;
#else
mRequests[mRequestTail].heap = GMA_PERSISTENT; // Cement library registrations should be around for the duration --jdy
#endif
MEMTRACK_PUSH_GROUP( "LoadingManager" );
HeapMgr()->PushHeap (GMA_TEMP);
mRequests[mRequestTail].pFileHandler = new(GMA_TEMP) CementFileHandler( &(mCementLibraries[i]) );
mRequests[mRequestTail].pFileHandler->AddRef();
mCementLibraries[i].isLoading = true;
HeapMgr()->PopHeap (GMA_TEMP);
MEMTRACK_POP_GROUP("LoadingManager");
mRequests[mRequestTail].pCallback = NULL;
mRequestTail = newTail;
this->ProcessNextRequest();
return( i );
}
else
{
rWarningMsg( 0, "Failed to add cement library request" );
return( -1 );
}
}
//=============================================================================
// LoadingManager::UnregisterCementLibrary
//=============================================================================
// Description: Unregister a cement library (hence the name)
//
// Parameters: handle - in practice, the array index for the cement library
//
// Return: void
//
//=============================================================================
void LoadingManager::UnregisterCementLibrary( CementFileHandle handle )
{
rAssert( handle >= 0 );
//rAssert( mCementLibraries[handle].library != NULL );
//
// Just to be safe, make sure that the cement library is inactive before
// we release it. Synchronous wait okay here?
//
if( mCementLibraries[handle].library != NULL )
{
mCementLibraries[handle].library->WaitForCompletion();
mCementLibraries[handle].library->Release();
mCementLibraries[handle].library = NULL;
}
}
//******************************************************************************
//
// Private Member Functions
//
//******************************************************************************
//==============================================================================
// LoadingManager::LoadingManager
//==============================================================================
//
// Description: Constructor.
//
// Parameters: None.
//
// Return: N/A.
//
//==============================================================================
LoadingManager::LoadingManager()
:
mRequestHead( 0 ),
mRequestTail( 0 ),
mLoading(false),
mCancellingLoads( false )
{
int i;
for( i = 0; i < MAX_CEMENT_LIBRARIES; i++ )
{
mCementLibraries[i].library = NULL;
mCementLibraries[i].isLoading = false;
}
#ifdef RAD_WIN32
mRequestsProcessed = 0;
#endif
}
//==============================================================================
// LoadingManager::~LoadingManager
//==============================================================================
//
// Description: Destructor.
//
// Parameters: None.
//
// Return: N/A.
//
//==============================================================================
LoadingManager::~LoadingManager()
{
int i;
for( i = 0; i < MAX_CEMENT_LIBRARIES; i++ )
{
if( mCementLibraries[i].library != NULL )
{
mCementLibraries[i].library->Release();
}
}
}
//==============================================================================
// LoadingManager::ProcessNextRequest
//==============================================================================
//
// Description: Handle the next load request in the queue.
//
// Parameters: None.
//
// Return: None.
//
//==============================================================================
void LoadingManager::ProcessNextRequest()
{
// Are we done?
if(!mLoading)
{
if(mRequestHead != mRequestTail)
{
// Start loading the next file.
LoadingRequest& request = mRequests[mRequestHead];
if ( request.filename[0] != '\0' )
{
if ( !(CommandLineOptions::Get( CLO_NO_LOADING_SPEW )) )
{
rReleasePrintf( "<<START>> Async Loading: %s\n", request.filename );
}
#ifdef RAD_WIN32
mRequestsProcessed++;
#endif
request.startTime = radTimeGetMilliseconds();
mLoading = true;
GameMemoryAllocator heap = request.heap;
#ifndef RAD_RELEASE
if (HeapManager::s_bSpecialRoute)
{
heap = GMA_SPECIAL;
}
#endif
request.pFileHandler->LoadFile( request.filename,
this,
(void*)mRequestHead,
heap );
}
else
{
rAssert( request.pCallback != NULL );
mRequestHead = (mRequestHead + 1) % MAX_REQUESTS;
mLoading = false;
request.pCallback->OnProcessRequestsComplete(request.pUserData);
ProcessNextRequest();
}
}
}
}