0ad/source/ps/Config.cpp
MarkT d963d9c046 CConfig, vfs, misc engine changes.
This was SVN commit r52.
2003-11-10 02:02:28 +00:00

309 lines
8.1 KiB
C++
Executable File

// Last modified: 6 November 2003 (Mark Thompson)
// TODO: A few changes from VFS -> CFile usage if required.
#include "Config.h"
#include "vfs.h"
using namespace std;
DEFINE_ERROR( PS_FILE_NOT_FOUND, "A data file required by the engine could \
not be found. Check that it exists within the game directory or archive tree." );
DEFINE_ERROR( PS_FILE_LOAD_FAILURE, "One or more data files required by the \
engine could not be loaded. These files may have been deleted or corrupted." );
DEFINE_ERROR( PS_FILE_NODYNAMIC, "A data file was modified during execution, \
but the engine cannot make one or more of these alterations while the game is \
running." );
//--------------------------------------------------------
// SConfigData: Internal file representation
//--------------------------------------------------------
SConfigData::SConfigData( CStr _Filename, void* _Data, LoaderFunction _DynamicLoader )
{
Filename = _Filename;
Data = _Data;
DynamicLoader = _DynamicLoader;
Timestamp = TIME_UNREGISTERED;
}
//--------------------------------------------------------
// CConfig: Dynamic Data Manager (singleton)
//--------------------------------------------------------
//--------------------------------------------------------
// CConfig::CConfig()
//--------------------------------------------------------
CConfig::CConfig()
{
Clear();
Attach( NULL );
i = m_FileList.begin();
}
//--------------------------------------------------------
// CConfig::Register()
//
// Add a file to the registered list.
//--------------------------------------------------------
PS_RESULT CConfig::Register( CStr Filename, void* Data, LoaderFunction DynamicLoader )
{
assert( DynamicLoader != NULL );
if( m_LogFile )
{
CStr Report = _T( "Adding file: " );
Report += Filename;
m_LogFile->WriteText( (const TCHAR*)Report );
}
// Might as well check we can find the thing.
char filepath[PATH_MAX];
strcpy( filepath, Filename );
if( vfs_access( filepath ) ) // This changes filepath to the disk location
// of the file, if we know it, to speed up
// checks later.
{
if( m_LogFile )
{
CStr Error = _T( "File not found on: " );
Error += Filename;
m_LogFile->WriteError( (const TCHAR*)Error );
}
return( PS_FILE_NOT_FOUND );
}
m_FileList.push_back( SConfigData( CStr( filepath ), Data, DynamicLoader ) );
i = m_FileList.begin();
if( m_LogFile )
{
CStr Report = _T( "Found file: " );
Report += CStr( filepath );
m_LogFile->WriteText( (const TCHAR*)Report );
}
return( PS_OK );
}
//--------------------------------------------------------
// CConfig::Update()
//
// Check timestamps of files and reload as required.
//--------------------------------------------------------
PS_RESULT CConfig::Update()
{
_int slice = 0;
_int failed = 0;
struct stat FileInfo;
for( slice = 0; ( i != m_FileList.end() ) && ( slice < CONFIG_SLICE ); i++, slice++ )
{
// TODO: CFile change on following line.
if( vfs_stat( i->Filename, &FileInfo ) )
{
// We can't find the file; if it exists, it's in an archive.
if( i->Timestamp )
{
// And it's already been loaded once, don't do so again.
continue;
}
// == TIME_UNREGISTERED. Load it, and set the modified date
// to now so that if it does turn up later on with a time
// after the start of the program, it will get loaded.
i->Timestamp = time( NULL );
}
else
{
if( FileInfo.st_nlink )
{
// This flag is set by vfs_stat to indicate that the file's
// gone walkabouts since last we knew its location.
// Find its new path and copy it back to the data we maintain
// here to speed up future queries.
char filepath[PATH_MAX];
strcpy( filepath, i->Filename );
vfs_access( filepath );
if( m_LogFile )
{
CStr Report = _T( "File " );
Report += i->Filename;
Report += CStr( _T( " moved to: " ) );
Report += CStr( filepath );
m_LogFile->WriteText( (const TCHAR*)Report );
}
i->Filename = CStr( filepath );
}
if( i->Timestamp == FileInfo.st_mtime )
{
// This file has the same modification time as it did last
// time we checked.
continue;
}
i->Timestamp = FileInfo.st_mtime;
}
// If we reach here, the file needs to be (re)loaded.
// Note also that polling every frame via _stat() for a file which
// either does not exist or exists only in an archive could be a
// considerable waste of time, but if not done the game won't pick
// up on modified versions of archived files moved into the main
// directory trees. Also, alternatives to polling don't tend to be
// portable.
slice--;
// Reloaded files do not count against the slice quota.
if( m_LogFile )
{
CStr Report = _T( "Reloading file: " );
Report += i->Filename;
m_LogFile->WriteText( (const TCHAR*)Report );
}
PS_RESULT Result;
if( ( Result = i->DynamicLoader( i->Filename, i->Data ) ) != PS_OK )
{
if( m_LogFile )
{
CStr Error = _T( "Load failed on: " );
Error += CStr( i->Filename );
Error += CStr( "Load function returned: " );
Error += CStr( Result );
m_LogFile->WriteError( (const TCHAR*)Error );
}
failed++;
if( Result != PS_FILE_NODYNAMIC )
return( PS_FILE_LOAD_FAILURE ); // Oops. Serious problem, bail.
}
}
if( i == m_FileList.end() ) i = m_FileList.begin();
if( failed )
return( PS_FILE_NODYNAMIC );
return( PS_OK );
}
//--------------------------------------------------------
// CConfig::ReloadAll()
//
// Reloads all files.
//--------------------------------------------------------
PS_RESULT CConfig::ReloadAll()
{
// Mostly identical to Update(), above.
_int failed = 0;
_int notfound = 0;
struct stat FileInfo;
for( i = m_FileList.begin(); i != m_FileList.end(); i++ )
{
// TODO: CFile change on following line.
if( vfs_stat( i->Filename, &FileInfo ) )
{
// We can't find the file. Seeing as this should reload everything,
// check that it exists.
char filepath[PATH_MAX];
strcpy( filepath, i->Filename );
if( vfs_access( filepath ) )
{
// Oops.
notfound++;
if( m_LogFile )
{
CStr Error = _T( "File not found on: " );
Error += i->Filename;
m_LogFile->WriteError( (const TCHAR*)Error );
}
continue;
}
i->Filename = CStr( filepath );
i->Timestamp = time( NULL );
}
else
{
if( FileInfo.st_nlink )
{
// This flag is set by vfs_stat to indicate that the file's
// gone walkabouts since last we knew its location.
// Find its new path and copy it back to the data we maintain
// here to speed up future queries.
char filepath[PATH_MAX];
strcpy( filepath, i->Filename );
vfs_access( filepath );
if( m_LogFile )
{
CStr Report = _T( "File " );
Report += i->Filename;
Report += CStr( _T( " moved to: " ) );
Report += CStr( filepath );
m_LogFile->WriteText( (const TCHAR*)Report );
}
i->Filename = CStr( filepath );
}
i->Timestamp = FileInfo.st_mtime;
}
// And load them all again...
if( m_LogFile )
{
CStr Report = _T( "Reloading file: " );
Report += i->Filename;
m_LogFile->WriteText( (const TCHAR*)Report );
}
PS_RESULT Result;
if( ( Result = i->DynamicLoader( i->Filename, i->Data ) ) != PS_OK )
{
if( m_LogFile )
{
CStr Error = _T( "Load failed on: " );
Error += CStr( i->Filename );
Error += CStr( "Load function returned: " );
Error += CStr( Result );
m_LogFile->WriteError( (const TCHAR*)Error );
}
failed++;
if( Result != PS_FILE_NODYNAMIC )
return( PS_FILE_LOAD_FAILURE ); // Oops. Serious problem, bail.
}
}
if( notfound )
return( PS_FILE_NOT_FOUND );
if( failed )
return( PS_FILE_NODYNAMIC );
return( PS_OK );
}
//--------------------------------------------------------
// CConfig::Clear()
//
// Erases registered list.
//--------------------------------------------------------
void CConfig::Clear()
{
m_FileList.clear();
}
//--------------------------------------------------------
// CConfig::Attach()
//
// Attaches (or detaches, with a NULL argument) a logfile class.
//--------------------------------------------------------
void CConfig::Attach( CLogFile* LogFile )
{
m_LogFile = LogFile;
}