mirror of
https://github.com/thug1src/thug.git
synced 2024-12-02 12:56:45 +00:00
771 lines
26 KiB
C++
771 lines
26 KiB
C++
#include <core/defines.h>
|
|
#include <core/crc.h>
|
|
#include "p_streamer.h"
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// DVD/HD sector sizes (these will never change)
|
|
//-----------------------------------------------------------------------------
|
|
#define HD_SECTOR_SIZE ( 512 )
|
|
#define DVD_SECTOR_SIZE ( 2 * 1024 )
|
|
|
|
const char *p_time_string = __TIME__;
|
|
const char *p_date_string = __DATE__;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: CheckDMAAlignment
|
|
// Desc: checks the alignment of file offset, read/write size, and
|
|
// target/source memory for DMA IO
|
|
//-----------------------------------------------------------------------------
|
|
#ifdef _DEBUG
|
|
VOID Check_DMA_Alignment( HANDLE hFile, VOID* pBuffer, DWORD dwOffset, DWORD dwNumBytesIO )
|
|
{
|
|
// assert proper target memory alignment
|
|
Dbg_Assert( pBuffer );
|
|
Dbg_Assert( (DWORD(pBuffer) & 0x00000003) == 0 );
|
|
|
|
// figure out whether this file is on the DVD or hard disk
|
|
BY_HANDLE_FILE_INFORMATION Info;
|
|
GetFileInformationByHandle( hFile, &Info );
|
|
DWORD dwDVDSerialNumber;
|
|
GetVolumeInformation( "D:\\", NULL, 0, &dwDVDSerialNumber, NULL, NULL, NULL, 0 );
|
|
|
|
// assert proper file offset and read/write size
|
|
if( Info.dwVolumeSerialNumber == dwDVDSerialNumber )
|
|
{
|
|
Dbg_Assert( dwOffset % DVD_SECTOR_SIZE == 0 );
|
|
Dbg_Assert( dwNumBytesIO % DVD_SECTOR_SIZE == 0);
|
|
}
|
|
else
|
|
{
|
|
Dbg_Assert( dwOffset % HD_SECTOR_SIZE == 0 );
|
|
Dbg_Assert( dwNumBytesIO % HD_SECTOR_SIZE == 0 );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: CLevelLoader()
|
|
// Desc: Constructor
|
|
//-----------------------------------------------------------------------------
|
|
CLevelLoader::CLevelLoader()
|
|
: m_pSysMemData( NULL ),
|
|
m_dwSysMemSize( 0 ),
|
|
m_pLevels( NULL ),
|
|
m_dwNumLevels( 0 ),
|
|
m_pCurrentLevel( NULL ),
|
|
m_IOState( Idle ),
|
|
m_pSysMemBuffer( NULL ),
|
|
m_pFileSig( NULL ),
|
|
m_hSignature( INVALID_HANDLE_VALUE )
|
|
{
|
|
// create file sig buffer
|
|
Dbg_Assert( m_pFileSig == NULL );
|
|
Dbg_Assert( XCALCSIG_SIGNATURE_SIZE + sizeof(SIG_MAGIC) <= HD_SECTOR_SIZE );
|
|
m_pFileSig = new BYTE[HD_SECTOR_SIZE];
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: ~CLevelLoader()
|
|
// Desc: Destructor
|
|
//-----------------------------------------------------------------------------
|
|
CLevelLoader::~CLevelLoader()
|
|
{
|
|
// Ensure we are not being deleted in the middle of an IO op.
|
|
Dbg_Assert( IsIdle());
|
|
|
|
// These should be already be cleaned up.
|
|
Dbg_Assert( m_hSignature == INVALID_HANDLE_VALUE );
|
|
Dbg_Assert( m_pSysMemBuffer == NULL );
|
|
Dbg_Assert( m_pFileSig == NULL );
|
|
|
|
// Close all handles.
|
|
for( UINT i = 0; i < m_dwNumLevels; i++ )
|
|
{
|
|
CloseHandle( m_pLevels[i].hDVDFile );
|
|
CloseHandle( m_pLevels[i].hHDFile );
|
|
CloseHandle( m_pLevels[i].hSigFile );
|
|
}
|
|
|
|
// We don't own this memory, so don't delete it.
|
|
m_pSysMemData = NULL;
|
|
|
|
// Free the level state array.
|
|
delete [] m_pLevels;
|
|
m_pLevels = NULL;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: Initialize
|
|
// Desc: Initialize loader
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CLevelLoader::Initialize( SLevelDesc* pDescs, DWORD dwNumLevels, BYTE* pSysMemData, DWORD dwSysMemSize, BOOL *p_signal )
|
|
{
|
|
// Make sure relevant data directories are on the HD utility section.
|
|
CreateDirectory( "Z:\\data", NULL );
|
|
CreateDirectory( "Z:\\data\\pre", NULL );
|
|
CreateDirectory( "Z:\\data\\music", NULL );
|
|
CreateDirectory( "Z:\\data\\music\\wma", NULL );
|
|
|
|
// Copy signal.
|
|
m_pOkayToUseUtilityDrive = p_signal;
|
|
|
|
// Initialize data.
|
|
Dbg_Assert( pSysMemData );
|
|
m_dwSysMemSize = dwSysMemSize;
|
|
m_pSysMemData = pSysMemData;
|
|
|
|
// Create level states.
|
|
m_dwNumLevels = dwNumLevels;
|
|
m_pLevels = new SLevelState[m_dwNumLevels];
|
|
|
|
// Init level states.
|
|
for( UINT i = 0; i < m_dwNumLevels; i++ )
|
|
{
|
|
// Copy level desc.
|
|
m_pLevels[i].Desc = pDescs[i];
|
|
|
|
// Clear file handle.
|
|
m_pLevels[i].hDVDFile = INVALID_HANDLE_VALUE;
|
|
m_pLevels[i].hHDFile = INVALID_HANDLE_VALUE;
|
|
m_pLevels[i].hSigFile = INVALID_HANDLE_VALUE;
|
|
m_pLevels[i].bWasPreCached = FALSE;
|
|
m_pLevels[i].bWasCacheCorrupted = FALSE;
|
|
m_pLevels[i].dwDVDFileSize = 0;
|
|
}
|
|
|
|
return RefreshLevelStates();
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: RefreshLevelStates
|
|
// Desc: Refreshes the level loader's level states
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CLevelLoader::RefreshLevelStates()
|
|
{
|
|
Dbg_Assert( IsIdle());
|
|
|
|
// Init states.
|
|
for( UINT i = 0; i < m_dwNumLevels; i++ )
|
|
{
|
|
m_pLevels[i].bIsPreCached = FALSE;
|
|
m_pLevels[i].bIsCacheCorrupted = FALSE;
|
|
|
|
m_pLevels[i].bIsOpen = FALSE;
|
|
m_pLevels[i].dwSysMemSize = 0;
|
|
m_pLevels[i].dwVidMemSize = 0;
|
|
|
|
// Close any opened handles.
|
|
CloseHandle( m_pLevels[i].hDVDFile );
|
|
CloseHandle( m_pLevels[i].hSigFile );
|
|
CloseHandle( m_pLevels[i].hHDFile );
|
|
|
|
m_pLevels[i].hDVDFile = INVALID_HANDLE_VALUE;
|
|
m_pLevels[i].hHDFile = INVALID_HANDLE_VALUE;
|
|
m_pLevels[i].hSigFile = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* Name: OpenLevel */
|
|
/* Desc: opens a level */
|
|
/******************************************************************/
|
|
HRESULT CLevelLoader::OpenLevel( SLevelState* pLevel, DWORD dwFlags )
|
|
{
|
|
// If the level has never been opened before, open in.
|
|
if( !pLevel->bIsOpen )
|
|
{
|
|
// Check that the level file handles are not open.
|
|
Dbg_Assert( pLevel->hDVDFile == INVALID_HANDLE_VALUE );
|
|
Dbg_Assert( pLevel->hHDFile == INVALID_HANDLE_VALUE );
|
|
Dbg_Assert( pLevel->hSigFile == INVALID_HANDLE_VALUE );
|
|
|
|
char szBuffer[MAX_PATH];
|
|
|
|
// DVD File.
|
|
sprintf( szBuffer, "D:\\data\\%s", pLevel->Desc.szName );
|
|
|
|
// Open DVD file for reading.
|
|
pLevel->hDVDFile = CreateFile( szBuffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, dwFlags, NULL );
|
|
|
|
if( pLevel->hDVDFile == INVALID_HANDLE_VALUE )
|
|
return EndOpenLevel( pLevel, BadOpen );
|
|
|
|
// Get size of file on DVD. NOTE: This size is used to help detect corrupt cached levels. If a cached level
|
|
// is not the same size as its DVD counterpart, the cached level is considered corrupt.
|
|
pLevel->dwDVDFileSize = ::GetFileSize( pLevel->hDVDFile, NULL );
|
|
|
|
// NOTE: Both files stored on the utility drive (signature file and level) are pre-sized. This prevents the files from
|
|
// becoming fragmented in the FATX. Also, Overlapped reads/writes to the hard disk are synchronous if the file system
|
|
// has to hit the FATX to determine local->physical cluster mapping.
|
|
|
|
// SIG File.
|
|
sprintf( szBuffer, "Z:\\data\\%s.sig", pLevel->Desc.szName );
|
|
|
|
// See if we have a cached sig.
|
|
pLevel->bIsPreCached = ( GetFileAttributes( szBuffer ) != DWORD( -1 ));
|
|
|
|
// Open sig file for reading and writing.
|
|
pLevel->hSigFile = CreateFile( szBuffer,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ |
|
|
FILE_SHARE_WRITE |
|
|
FILE_SHARE_DELETE,
|
|
NULL,
|
|
OPEN_ALWAYS,
|
|
dwFlags,
|
|
NULL );
|
|
if( pLevel->hSigFile == INVALID_HANDLE_VALUE )
|
|
return EndOpenLevel( pLevel, BadOpen );
|
|
|
|
// Cache is corrupted if sig file is not the right size. NOTE: The sig file is HD_SECTOR_SIZE in size. The actual
|
|
// signature calculated by XCalculateSignature* is much smaller that HD_SECTOR_SIZE, but we must write at least
|
|
// HD_SECTOR_SIZE to use DMA on the hard disk.
|
|
pLevel->bIsCacheCorrupted = pLevel->bIsPreCached && ::GetFileSize( pLevel->hSigFile, NULL ) != HD_SECTOR_SIZE;
|
|
|
|
// If the sig file is corrupted or not saved, resize it.
|
|
if( !pLevel->bIsPreCached || pLevel->bIsCacheCorrupted )
|
|
{
|
|
// Set file size for faster write.
|
|
SetFilePointer( pLevel->hSigFile, HD_SECTOR_SIZE, NULL, FILE_BEGIN );
|
|
SetEndOfFile( pLevel->hSigFile );
|
|
|
|
// Clear Sig magic number.
|
|
BYTE apyBuffer[HD_SECTOR_SIZE];
|
|
*(DWORD*)(apyBuffer) = ~SIG_MAGIC;
|
|
SetCurrentFile( pLevel->hSigFile );
|
|
if( FAILED( DoIO( Write, apyBuffer, HD_SECTOR_SIZE )))
|
|
return EndOpenLevel( pLevel, BadSigMagicWrite );
|
|
|
|
// Wait for IO completion for sig magic writes.
|
|
while( HasIOCompleted() != S_OK );
|
|
}
|
|
|
|
|
|
// CACHED file.
|
|
sprintf( szBuffer, "Z:\\data\\%s", pLevel->Desc.szName );
|
|
|
|
// See if we have a cached file.
|
|
pLevel->bIsPreCached = pLevel->bIsPreCached && ( GetFileAttributes( szBuffer ) != DWORD( -1 ));
|
|
|
|
// Open cached file for reading and writing.
|
|
pLevel->hHDFile = CreateFile( szBuffer,
|
|
GENERIC_WRITE | GENERIC_READ,
|
|
FILE_SHARE_READ |
|
|
FILE_SHARE_WRITE |
|
|
FILE_SHARE_DELETE,
|
|
NULL,
|
|
OPEN_ALWAYS,
|
|
dwFlags,
|
|
NULL );
|
|
|
|
if( pLevel->hHDFile == INVALID_HANDLE_VALUE )
|
|
return EndOpenLevel( pLevel, BadOpen );
|
|
|
|
// Cache is corrupted if cache file is not the right size
|
|
pLevel->bIsCacheCorrupted = pLevel->bIsPreCached && (( ::GetFileSize( pLevel->hHDFile, NULL ) != pLevel->dwDVDFileSize ) || pLevel->bIsCacheCorrupted );
|
|
|
|
// If the cache file is corrupted or not saved, resize it.
|
|
if( !pLevel->bIsPreCached || pLevel->bIsCacheCorrupted )
|
|
{
|
|
// Set file size for faster write.
|
|
DWORD rv = SetFilePointer( pLevel->hHDFile, pLevel->dwDVDFileSize, NULL, FILE_BEGIN );
|
|
if( rv == INVALID_SET_FILE_POINTER )
|
|
{
|
|
DWORD last_error = GetLastError();
|
|
printf( "Last error: %x\n", last_error );
|
|
}
|
|
SetEndOfFile( pLevel->hHDFile );
|
|
}
|
|
return EndOpenLevel( pLevel, FilesOpened );
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: AsyncStreamLevel
|
|
// Desc: Loads all the resources from the given XPR asynchronously using DMA
|
|
//-----------------------------------------------------------------------------
|
|
VOID CLevelLoader::AsyncStreamLevel( DWORD dwLevel )
|
|
{
|
|
Dbg_Assert( IsIdle() );
|
|
Dbg_Assert( dwLevel < m_dwNumLevels );
|
|
|
|
// We are no longer idle.
|
|
m_IOState = Begin;
|
|
|
|
// set current level
|
|
m_pCurrentLevel = &m_pLevels[dwLevel];
|
|
m_dwCurrentLevel = dwLevel;
|
|
|
|
// Start timer.
|
|
m_dStartTime = Tmr::GetTime();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: StreamCurrentBundle()
|
|
// Desc: updates the streaming state, returning S_OK when finished
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CLevelLoader::StreamCurrentLevel()
|
|
{
|
|
HRESULT hr;
|
|
|
|
switch(m_IOState)
|
|
{
|
|
case Begin:
|
|
{
|
|
// reset IO
|
|
ResetStreaming();
|
|
|
|
// if we are reading from the cache, get the signature
|
|
if( IsCurrentCacheGood())
|
|
{
|
|
SetCurrentFile( m_pCurrentLevel->hSigFile );
|
|
if( FAILED( hr = DoIO( Read, m_pFileSig, HD_SECTOR_SIZE )))
|
|
return EndStreamLevel( BadRead );
|
|
}
|
|
m_IOState = LoadSig;
|
|
}
|
|
|
|
case LoadSig:
|
|
{
|
|
// wait for previous IO to complete
|
|
if( FAILED( hr = HasIOCompleted()))
|
|
return EndStreamLevel( hr == E_PENDING ? Pending : BadRead );
|
|
|
|
// Reading cached file, so a signature exists
|
|
if( IsCurrentCacheGood())
|
|
{
|
|
// Look for sig magic number.
|
|
DWORD dwSig;
|
|
dwSig = *(DWORD*)( m_pFileSig );
|
|
if( dwSig != SIG_MAGIC )
|
|
{
|
|
if( dwSig == ~SIG_MAGIC )
|
|
return EndStreamLevel( NoSigMagic );
|
|
else
|
|
return EndStreamLevel( BadSig );
|
|
}
|
|
|
|
// Check date and time.
|
|
dwSig = *(((DWORD*)m_pFileSig ) + 1 );
|
|
if( dwSig != Crc::GenerateCRCFromString( p_date_string ))
|
|
{
|
|
return EndStreamLevel( BadSig );
|
|
}
|
|
dwSig = *(((DWORD*)m_pFileSig ) + 2 );
|
|
if( dwSig != Crc::GenerateCRCFromString( p_time_string ))
|
|
{
|
|
return EndStreamLevel( BadSig );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Set sig magic number so it is written out.
|
|
*(((DWORD*)m_pFileSig ) + 0 ) = SIG_MAGIC;
|
|
*(((DWORD*)m_pFileSig ) + 1 ) = Crc::GenerateCRCFromString( p_date_string );
|
|
*(((DWORD*)m_pFileSig ) + 2 ) = Crc::GenerateCRCFromString( p_time_string );
|
|
}
|
|
|
|
m_IOState = LoadSysMem;
|
|
}
|
|
|
|
case LoadSysMem:
|
|
{
|
|
// Wait for previous IO to complete.
|
|
if( FAILED( hr = HasIOCompleted()))
|
|
return EndStreamLevel( hr == E_PENDING ? Pending : BadRead );
|
|
|
|
// Read initial system memory buffer.
|
|
if( IsCurrentCacheGood())
|
|
{
|
|
// At this point we have decided the sig file is fine, which indicates that the cached file is fine also.
|
|
// As such, no further work required for this file.
|
|
// SetCurrentFile( m_pCurrentLevel->hHDFile );
|
|
m_pCurrentLevel->bIsPreCached = TRUE;
|
|
m_pCurrentLevel->bIsCacheCorrupted = FALSE;
|
|
return EndStreamLevel( Finished );
|
|
}
|
|
else
|
|
{
|
|
// Set file pointer to start of HD file.
|
|
SetCurrentFile( m_pCurrentLevel->hHDFile );
|
|
|
|
// Set file pointer to start of DVD file.
|
|
SetCurrentFile( m_pCurrentLevel->hDVDFile );
|
|
}
|
|
|
|
// At this point we want to loop through reading the buffer and writing it until teh file is fully written.
|
|
DWORD total_bytes_read = 0;
|
|
DWORD total_bytes_written = 0;
|
|
int bytes_remaining = m_pCurrentLevel->dwDVDFileSize;
|
|
for( ;; )
|
|
{
|
|
// Set the DVD file, but don't reset the file pointer.
|
|
SetCurrentFile( m_pCurrentLevel->hDVDFile, FALSE );
|
|
|
|
// DWORD bytes_to_transfer = ( bytes_remaining >= m_dwSysMemSize ) ? m_dwSysMemSize : bytes_remaining;
|
|
DWORD bytes_to_transfer = ( bytes_remaining >= (int)m_dwSysMemSize ) ? m_dwSysMemSize : m_dwSysMemSize;
|
|
|
|
if( FAILED( DoIO( Read, m_pSysMemData, bytes_to_transfer )))
|
|
return EndStreamLevel( BadRead );
|
|
|
|
total_bytes_read += bytes_to_transfer;
|
|
|
|
// Set the HD file, but don't reset the file pointer.
|
|
SetCurrentFile( m_pCurrentLevel->hHDFile, FALSE );
|
|
|
|
if( FAILED( DoIO( Write, m_pSysMemData, bytes_to_transfer )))
|
|
return EndStreamLevel( BadWrite );
|
|
|
|
total_bytes_written += bytes_to_transfer;
|
|
bytes_remaining -= bytes_to_transfer;
|
|
|
|
if( bytes_remaining <= 0 )
|
|
{
|
|
DWORD rv = SetFilePointer( m_pCurrentLevel->hHDFile, m_pCurrentLevel->dwDVDFileSize, NULL, FILE_BEGIN );
|
|
if( rv == INVALID_SET_FILE_POINTER )
|
|
{
|
|
DWORD last_error = GetLastError();
|
|
printf( "Last error: %x\n", last_error );
|
|
}
|
|
|
|
SetEndOfFile( m_pCurrentLevel->hHDFile );
|
|
|
|
m_IOState = WriteSig;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
case WriteSig:
|
|
{
|
|
// Wait for previous IO to complete.
|
|
if( FAILED( hr = HasIOCompleted() ) )
|
|
return EndStreamLevel( hr == E_PENDING ? Pending : BadRead );
|
|
|
|
// We are now loaded.
|
|
m_dLoadTime = Tmr::GetTime() - m_dStartTime;
|
|
|
|
// We write the sig file last. If the box is turned off during a cache operation, the sig file will be missing.
|
|
// If the box is turned off during the write of the sig file, it won't have a header or will not match the level
|
|
// data. In either case, the level will be re-cached.
|
|
SetCurrentFile( m_pCurrentLevel->hSigFile );
|
|
if( FAILED( hr = DoIO( Write, m_pFileSig, HD_SECTOR_SIZE) ) )
|
|
return EndStreamLevel( BadWrite );
|
|
|
|
m_IOState = End;
|
|
}
|
|
|
|
case End:
|
|
{
|
|
// Wait for previous IO to complete.
|
|
if( FAILED( hr = HasIOCompleted() ) )
|
|
return EndStreamLevel( hr == E_PENDING ? Pending : BadRead );
|
|
|
|
// Record cache time.
|
|
m_dCacheTime = Tmr::GetTime() - m_dStartTime;
|
|
|
|
// We are now cached.
|
|
m_pCurrentLevel->bIsPreCached = TRUE;
|
|
m_pCurrentLevel->bIsCacheCorrupted = FALSE;
|
|
|
|
return EndStreamLevel( Finished );
|
|
}
|
|
}
|
|
|
|
// should never reach here
|
|
Dbg_Assert( FALSE );
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: EndOpenLevel()
|
|
// Desc: called when IO has completed or there is an error
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CLevelLoader::EndOpenLevel( SLevelState* pLevel, FileOpenStatus Status)
|
|
{
|
|
Dbg_Assert(pLevel);
|
|
|
|
if(Status == BadOpen || Status == BadSigMagicWrite)
|
|
{
|
|
// close all file handles
|
|
CloseHandle( pLevel->hDVDFile );
|
|
CloseHandle( pLevel->hHDFile );
|
|
CloseHandle( pLevel->hSigFile );
|
|
pLevel->hDVDFile = INVALID_HANDLE_VALUE;
|
|
pLevel->hHDFile = INVALID_HANDLE_VALUE;
|
|
pLevel->hSigFile = INVALID_HANDLE_VALUE;
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
// level is now opened
|
|
pLevel->bIsOpen = TRUE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: EndStreamLevel()
|
|
// Desc: called when IO has completed or there is an error
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CLevelLoader::EndStreamLevel( FileIOStatus Status )
|
|
{
|
|
// if IO is not pending, we are finished
|
|
if(Status != Pending)
|
|
{
|
|
// close the signature if needed
|
|
if( m_hSignature != INVALID_HANDLE_VALUE )
|
|
{
|
|
XCalculateSignatureEnd( m_hSignature, NULL );
|
|
m_hSignature = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
// determine new state
|
|
switch(Status)
|
|
{
|
|
case BadRead:
|
|
case BadHeader:
|
|
case BadSig:
|
|
case SigMismatch:
|
|
m_pCurrentLevel->bIsCacheCorrupted = TRUE;
|
|
m_IOState = Begin; // retry streaming the level
|
|
return E_FAIL;
|
|
case NoSigMagic:
|
|
m_pCurrentLevel->bIsPreCached = FALSE;
|
|
m_IOState = Begin; // retry streaming the level
|
|
return E_FAIL;
|
|
case BadWrite: // if we have a bad write, finish anyways since the cache will be corrupt next time around
|
|
m_pCurrentLevel->bIsCacheCorrupted = TRUE;
|
|
case Finished:
|
|
m_IOState = Idle; // done with IO, so we are now idle
|
|
|
|
// close all file handles
|
|
CloseHandle( m_pCurrentLevel->hDVDFile );
|
|
CloseHandle( m_pCurrentLevel->hHDFile );
|
|
CloseHandle( m_pCurrentLevel->hSigFile );
|
|
m_pCurrentLevel->hDVDFile = INVALID_HANDLE_VALUE;
|
|
m_pCurrentLevel->hHDFile = INVALID_HANDLE_VALUE;
|
|
m_pCurrentLevel->hSigFile = INVALID_HANDLE_VALUE;
|
|
|
|
return S_OK;
|
|
case Pending:
|
|
return E_PENDING;
|
|
}
|
|
|
|
// should never reach here
|
|
Dbg_Assert( FALSE );
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: CRestIO()
|
|
// Desc: Records last state for current level and get ready for IO
|
|
//-----------------------------------------------------------------------------
|
|
VOID CLevelLoader::ResetStreaming()
|
|
{
|
|
// record last state
|
|
m_pCurrentLevel->bWasPreCached = m_pCurrentLevel->bIsPreCached;
|
|
m_pCurrentLevel->bWasCacheCorrupted = m_pCurrentLevel->bIsCacheCorrupted;
|
|
|
|
// begin signature
|
|
Dbg_Assert( m_hSignature == INVALID_HANDLE_VALUE );
|
|
m_hSignature = XCalculateSignatureBegin( XCALCSIG_FLAG_NON_ROAMABLE );
|
|
Dbg_Assert( m_hSignature != INVALID_HANDLE_VALUE );
|
|
|
|
// create file sig buffer
|
|
// Dbg_Assert( m_pFileSig == NULL );
|
|
// Dbg_Assert( XCALCSIG_SIGNATURE_SIZE + sizeof(SIG_MAGIC) <= HD_SECTOR_SIZE );
|
|
// m_pFileSig = new BYTE[HD_SECTOR_SIZE];
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: IOProc
|
|
// Desc: Thread proc for IO
|
|
//-----------------------------------------------------------------------------
|
|
DWORD WINAPI IOProc( LPVOID lpParameter )
|
|
{
|
|
CThreadedLevelLoader* pLoader = (CThreadedLevelLoader*)lpParameter;
|
|
Dbg_Assert( pLoader );
|
|
|
|
// Wait for the IO event to be signaled.
|
|
WaitForSingleObject( pLoader->m_hEvent, INFINITE );
|
|
|
|
for( ;; )
|
|
{
|
|
|
|
// NOTE: In the threaded version of the loader, we are able to open
|
|
// files and set file sizes mid-game without noticing a severe
|
|
// frame "glitch." If you use overlapped IO, you must remember
|
|
// that OpenFile, SetEndOfFile, etc. are blocking
|
|
|
|
// open the current level
|
|
// If we can't open files, we are in trouble, so in the release build, we just keep trying
|
|
// while( FAILED( pLoader->OpenLevel( pLoader->m_pCurrentLevel, FILE_FLAG_NO_BUFFERING | FILE_FLAG_SEQUENTIAL_SCAN )))
|
|
while( FAILED( pLoader->OpenLevel( pLoader->m_pCurrentLevel, FILE_FLAG_SEQUENTIAL_SCAN )))
|
|
{
|
|
// break in the debug build to find out what the problem is
|
|
Dbg_Assert( FALSE );
|
|
}
|
|
|
|
// Keep trying to load and cache the level until we are successful.
|
|
while( FAILED( pLoader->StreamCurrentLevel()));
|
|
|
|
// Level loaded and cached.
|
|
++pLoader->m_dwCurrentLevel;
|
|
if( pLoader->m_dwCurrentLevel < pLoader->m_dwNumLevels )
|
|
{
|
|
pLoader->m_pCurrentLevel = &pLoader->m_pLevels[pLoader->m_dwCurrentLevel];
|
|
pLoader->m_IOState = CThreadedLevelLoader::Begin;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
// If the loaded has closed the thread we are finished. Signal the boolean value passed in.
|
|
*( pLoader->m_pOkayToUseUtilityDrive ) = true;
|
|
|
|
ExitThread( 0 );
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: CThreadedLevelLoader()
|
|
// Desc: Constructor
|
|
//-----------------------------------------------------------------------------
|
|
CThreadedLevelLoader::CThreadedLevelLoader() : m_bKillThread( FALSE )
|
|
{
|
|
// Create event that is used to block the thread when it is not being used for IO.
|
|
m_hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
|
|
|
// Create IO thread.
|
|
m_hThread = CreateThread( NULL, 0, IOProc, this, 0, NULL);
|
|
|
|
SetCurrentFile( INVALID_HANDLE_VALUE );
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: ~CThreadedLevelLoader()
|
|
// Desc: Destructor
|
|
//-----------------------------------------------------------------------------
|
|
CThreadedLevelLoader::~CThreadedLevelLoader()
|
|
{
|
|
Dbg_Assert( IsIdle() );
|
|
|
|
// Kill the thread.
|
|
|
|
// signal thread
|
|
m_bKillThread = TRUE;
|
|
SetEvent( m_hEvent );
|
|
|
|
// wait for thread to exit
|
|
DWORD dwExitStatus;
|
|
do
|
|
{
|
|
SwitchToThread(); // try to get the thread to execute
|
|
GetExitCodeThread( m_hThread, &dwExitStatus );
|
|
}
|
|
while( dwExitStatus == STILL_ACTIVE );
|
|
|
|
// close handle to thread and event
|
|
CloseHandle( m_hThread );
|
|
CloseHandle( m_hEvent );
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: DoIO
|
|
// Desc: wrapper for ::ReadFile and ::WriteFile that checks parameters
|
|
//-----------------------------------------------------------------------------
|
|
HRESULT CThreadedLevelLoader::DoIO( IOType Type, VOID* pBuffer, DWORD dwNumBytes )
|
|
{
|
|
// Check DMA alignment
|
|
# ifdef _DEBUG
|
|
Check_DMA_Alignment( m_hFile, pBuffer, SetFilePointer( m_hFile, 0, NULL, FILE_CURRENT ), dwNumBytes );
|
|
# endif
|
|
|
|
// Do IO
|
|
DWORD dwNumBytesIO;
|
|
BOOL bSuccess;
|
|
if(Type == Read)
|
|
{
|
|
bSuccess = ::ReadFile( m_hFile, pBuffer, dwNumBytes, &dwNumBytesIO, NULL );
|
|
}
|
|
else
|
|
bSuccess = ::WriteFile( m_hFile, pBuffer, dwNumBytes, &dwNumBytesIO, NULL );
|
|
|
|
// If IO is successful and we have the number of expected bytes, return success.
|
|
// NOTE: If the file size is X*SECTORSIZE + Y where Y is less than SECTORSIZE, we still request SECTORSIZE bytes when reading Y
|
|
// in order to get DMA. The file system will return success, and the number of bytes read/written is Y.
|
|
if( !bSuccess || ( dwNumBytesIO != dwNumBytes && SetFilePointer( m_hFile, 0, NULL, FILE_CURRENT ) != GetFileSize( m_hFile, NULL )))
|
|
// if( !bSuccess )
|
|
return E_FAIL;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: SetCurrentFile
|
|
// Desc: Sets the current file to be used during IO
|
|
//-----------------------------------------------------------------------------
|
|
VOID CThreadedLevelLoader::SetCurrentFile( HANDLE hFile, BOOL to_start )
|
|
{
|
|
if(( hFile != INVALID_HANDLE_VALUE ) && to_start )
|
|
{
|
|
// Reset file pointer to beginning.
|
|
SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
|
|
}
|
|
m_hFile = hFile;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: AsyncStreamLevel
|
|
// Desc: Begins streaming of the level. returns immediately
|
|
//-----------------------------------------------------------------------------
|
|
VOID CThreadedLevelLoader::AsyncStreamLevel( DWORD dwLevel )
|
|
{
|
|
CLevelLoader::AsyncStreamLevel( dwLevel );
|
|
|
|
// Signal thread to start IO.
|
|
SetEvent( m_hEvent );
|
|
}
|