diff options
Diffstat (limited to 'utils/xbox/xbox_loader')
33 files changed, 4320 insertions, 0 deletions
diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/LegalScreen_english.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/LegalScreen_english.tga Binary files differnew file mode 100644 index 0000000..5c61f95 --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/LegalScreen_english.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/LegalScreen_french.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/LegalScreen_french.tga Binary files differnew file mode 100644 index 0000000..0116d18 --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/LegalScreen_french.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/LegalScreen_german.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/LegalScreen_german.tga Binary files differnew file mode 100644 index 0000000..629524e --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/LegalScreen_german.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/LegalScreen_italian.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/LegalScreen_italian.tga Binary files differnew file mode 100644 index 0000000..c37b3a2 --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/LegalScreen_italian.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/LegalScreen_spanish.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/LegalScreen_spanish.tga Binary files differnew file mode 100644 index 0000000..a935072 --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/LegalScreen_spanish.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/LoadingIcon.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/LoadingIcon.tga Binary files differnew file mode 100644 index 0000000..ae2134d --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/LoadingIcon.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/SourceScreen.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/SourceScreen.tga Binary files differnew file mode 100644 index 0000000..4acd064 --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/SourceScreen.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/Tahoma_16.abc b/utils/xbox/xbox_loader/LoaderMedia_Source/Tahoma_16.abc Binary files differnew file mode 100644 index 0000000..bcbc925 --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/Tahoma_16.abc diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/Tahoma_16.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/Tahoma_16.tga Binary files differnew file mode 100644 index 0000000..103fb95 --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/Tahoma_16.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/footer.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/footer.tga Binary files differnew file mode 100644 index 0000000..340f4a5 --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/footer.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/loader_icon.bmp b/utils/xbox/xbox_loader/LoaderMedia_Source/loader_icon.bmp Binary files differnew file mode 100644 index 0000000..29e905e --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/loader_icon.bmp diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/loader_icon.rdf b/utils/xbox/xbox_loader/LoaderMedia_Source/loader_icon.rdf new file mode 100644 index 0000000..e31fa3a --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/loader_icon.rdf @@ -0,0 +1,7 @@ +Texture MyTex +{ + Source loader_icon.bmp + Format D3DFMT_DXT1 + Width 128 + Height 128 +} diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow1_english.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow1_english.tga Binary files differnew file mode 100644 index 0000000..b45b3a1 --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow1_english.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow1_french.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow1_french.tga Binary files differnew file mode 100644 index 0000000..2963e36 --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow1_french.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow1_german.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow1_german.tga Binary files differnew file mode 100644 index 0000000..2636bec --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow1_german.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow1_italian.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow1_italian.tga Binary files differnew file mode 100644 index 0000000..a09efae --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow1_italian.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow1_spanish.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow1_spanish.tga Binary files differnew file mode 100644 index 0000000..ade8343 --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow1_spanish.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow2.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow2.tga Binary files differnew file mode 100644 index 0000000..52e952b --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow2.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow3.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow3.tga Binary files differnew file mode 100644 index 0000000..76b3700 --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow3.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow4.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow4.tga Binary files differnew file mode 100644 index 0000000..1bc591d --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow4.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow5.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow5.tga Binary files differnew file mode 100644 index 0000000..040a8ac --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow5.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow6.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow6.tga Binary files differnew file mode 100644 index 0000000..1edbb6e --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow6.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow7.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow7.tga Binary files differnew file mode 100644 index 0000000..4f47769 --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow7.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow8.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow8.tga Binary files differnew file mode 100644 index 0000000..30dabe8 --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow8.tga diff --git a/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow9.tga b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow9.tga Binary files differnew file mode 100644 index 0000000..a9cfb82 --- /dev/null +++ b/utils/xbox/xbox_loader/LoaderMedia_Source/slideshow9.tga diff --git a/utils/xbox/xbox_loader/loader.rdf b/utils/xbox/xbox_loader/loader.rdf new file mode 100644 index 0000000..33e7552 --- /dev/null +++ b/utils/xbox/xbox_loader/loader.rdf @@ -0,0 +1,173 @@ +// List of resources to bundle. +// +// The output will be a header file (.h) used at compile time, +// and a packed resource file (.xpr) used at runtime. + +out_packedresource LoaderMedia\loader.xpr +out_error loader.err + +Texture Font +{ + Source LoaderMedia_Source\Tahoma_16.tga + Format D3DFMT_A4R4G4B4 + Levels 1 +} + +UserData FontData +{ + DataFile LoaderMedia_Source\Tahoma_16.abc +} + +Texture Footer +{ + Source LoaderMedia_Source\Footer.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture MainLegal_english +{ + Source LoaderMedia_Source\LegalScreen_english.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture MainLegal_french +{ + Source LoaderMedia_Source\LegalScreen_french.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture MainLegal_italian +{ + Source LoaderMedia_Source\LegalScreen_italian.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture MainLegal_german +{ + Source LoaderMedia_Source\LegalScreen_german.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture MainLegal_spanish +{ + Source LoaderMedia_Source\LegalScreen_spanish.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture SourceLegal +{ + Source LoaderMedia_Source\SourceScreen.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture LoadingIcon +{ + Source LoaderMedia_Source\LoadingIcon.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture SlideShow1_english +{ + Source LoaderMedia_Source\SlideShow1_english.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture SlideShow1_french +{ + Source LoaderMedia_Source\SlideShow1_french.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture SlideShow1_italian +{ + Source LoaderMedia_Source\SlideShow1_italian.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture SlideShow1_german +{ + Source LoaderMedia_Source\SlideShow1_german.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture SlideShow1_spanish +{ + Source LoaderMedia_Source\SlideShow1_spanish.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture SlideShow2 +{ + Source LoaderMedia_Source\SlideShow2.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture SlideShow3 +{ + Source LoaderMedia_Source\SlideShow3.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture SlideShow4 +{ + Source LoaderMedia_Source\SlideShow4.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture SlideShow5 +{ + Source LoaderMedia_Source\SlideShow5.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture SlideShow6 +{ + Source LoaderMedia_Source\SlideShow6.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture SlideShow7 +{ + Source LoaderMedia_Source\SlideShow7.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture SlideShow8 +{ + Source LoaderMedia_Source\SlideShow8.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + +Texture SlideShow9 +{ + Source LoaderMedia_Source\SlideShow9.tga + Format D3DFMT_LIN_A8R8G8B8 + Levels 1 +} + + + + + + + diff --git a/utils/xbox/xbox_loader/xbox_fileCopy.cpp b/utils/xbox/xbox_loader/xbox_fileCopy.cpp new file mode 100644 index 0000000..39d0c65 --- /dev/null +++ b/utils/xbox/xbox_loader/xbox_fileCopy.cpp @@ -0,0 +1,595 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Copies a file using overlapped async IO. +// +// Stub executeable +//=====================================================================================// +#include "xbox_loader.h" + +#define BUFFER_SIZE (1*1024*1024) +#define NUM_BUFFERS 4 +#define ALIGN(x,y) (((x)+(y)-1) & ~((y)-1)) + +struct CopyFile_t +{ + // source file + HANDLE m_hSrcFile; + DWORD m_srcFileSize; + int m_readBufferSize; + unsigned int m_numReadCycles; + + // target file + HANDLE m_hDstFile; + DWORD m_dstFileSize; + + // source file gets decompressed + bool m_bInflate; + unsigned char *m_pInflateBuffer; + int m_inflateBufferSize; + + bool m_bCopyError; + CopyStats_t *m_pCopyStats; +}; + +struct Buffer_t +{ + unsigned char *pData; + DWORD dwSize; + Buffer_t* pNext; + int id; +}; + +Buffer_t *g_pReadBuffers = NULL; +Buffer_t *g_pWriteBuffers = NULL; + +CRITICAL_SECTION g_criticalSection; +HANDLE g_hReadEvent; +HANDLE g_hWriteEvent; +DWORD *g_pNumReadBuffers; +DWORD *g_pNumWriteBuffers; + +//----------------------------------------------------------------------------- +// CreateFilePath +// +// Create full path to specified file. +//----------------------------------------------------------------------------- +bool CreateFilePath( const char *inPath ) +{ + char* ptr; + char dirPath[MAX_PATH]; + BOOL bSuccess; + + // prime and skip to first seperator after the drive path + strcpy( dirPath, inPath ); + ptr = strchr( dirPath, '\\' ); + while ( ptr ) + { + ptr = strchr( ptr+1, '\\' ); + if ( ptr ) + { + *ptr = '\0'; + bSuccess = ::CreateDirectory( dirPath, NULL ); + *ptr = '\\'; + } + } + + // ensure read-only is cleared + SetFileAttributes( inPath, FILE_ATTRIBUTE_NORMAL ); + + return true; +} + +//----------------------------------------------------------------------------- +// LockBufferForRead +// +//----------------------------------------------------------------------------- +Buffer_t *LockBufferForRead() +{ + if ( !g_pReadBuffers ) + { + // out of data, wait for it + WaitForSingleObject( g_hReadEvent, INFINITE ); + } + else + { + ResetEvent( g_hReadEvent ); + } + + EnterCriticalSection( &g_criticalSection ); + + Buffer_t *pBuffer = g_pReadBuffers; + g_pReadBuffers = pBuffer->pNext; + + (*g_pNumReadBuffers)--; + + LeaveCriticalSection( &g_criticalSection ); + + return pBuffer; +} + +//----------------------------------------------------------------------------- +// LockBufferForWrite +// +//----------------------------------------------------------------------------- +Buffer_t* LockBufferForWrite() +{ + if ( !g_pWriteBuffers ) + { + // out of data, wait for more + WaitForSingleObject( g_hWriteEvent, INFINITE ); + } + else + { + ResetEvent( g_hWriteEvent ); + } + + EnterCriticalSection( &g_criticalSection ); + + Buffer_t *pBuffer = g_pWriteBuffers; + g_pWriteBuffers = pBuffer->pNext; + + (*g_pNumWriteBuffers)--; + + LeaveCriticalSection( &g_criticalSection ); + + return pBuffer; +} + +//----------------------------------------------------------------------------- +// AddBufferForRead +// +//----------------------------------------------------------------------------- +void AddBufferForRead( Buffer_t *pBuffer ) +{ + EnterCriticalSection( &g_criticalSection ); + + // add to end of list + Buffer_t *pCurrent = g_pReadBuffers; + while ( pCurrent && pCurrent->pNext ) + { + pCurrent = pCurrent->pNext; + } + if ( pCurrent ) + { + pBuffer->pNext = pCurrent->pNext; + pCurrent->pNext = pBuffer; + } + else + { + pBuffer->pNext = NULL; + g_pReadBuffers = pBuffer; + } + + (*g_pNumReadBuffers)++; + + LeaveCriticalSection( &g_criticalSection ); + + SetEvent( g_hReadEvent ); +} + +//----------------------------------------------------------------------------- +// AddBufferForWrite +// +//----------------------------------------------------------------------------- +void AddBufferForWrite( Buffer_t *pBuffer ) +{ + EnterCriticalSection( &g_criticalSection ); + + // add to end of list + Buffer_t* pCurrent = g_pWriteBuffers; + while ( pCurrent && pCurrent->pNext ) + { + pCurrent = pCurrent->pNext; + } + if ( pCurrent ) + { + pBuffer->pNext = pCurrent->pNext; + pCurrent->pNext = pBuffer; + } + else + { + pBuffer->pNext = NULL; + g_pWriteBuffers = pBuffer; + } + + (*g_pNumWriteBuffers)++; + + LeaveCriticalSection( &g_criticalSection ); + + SetEvent( g_hWriteEvent ); +} + +//----------------------------------------------------------------------------- +// ReadFileThread +// +//----------------------------------------------------------------------------- +DWORD WINAPI ReadFileThread( LPVOID lParam ) +{ + CopyFile_t *pCopyFile; + OVERLAPPED overlappedRead = {0}; + DWORD startTime; + DWORD dwBytesRead; + DWORD dwError; + BOOL bResult; + Buffer_t *pBuffer; + + pCopyFile = (CopyFile_t*)lParam; + + // Copy from the buffer to the Hard Drive + for ( unsigned int readCycle = 0; readCycle < pCopyFile->m_numReadCycles; ++readCycle ) + { + pBuffer = LockBufferForRead(); + + startTime = GetTickCount(); + dwBytesRead = 0; + + int numAttempts = 0; +retry: + // read file from DVD + bResult = ReadFile( pCopyFile->m_hSrcFile, pBuffer->pData, pCopyFile->m_readBufferSize, NULL, &overlappedRead ); + dwError = GetLastError(); + if ( !bResult && dwError != ERROR_IO_PENDING ) + { + if ( dwError == ERROR_HANDLE_EOF ) + { + // nothing more to read + break; + } + + numAttempts++; + if ( numAttempts == 3 ) + { + // error + pCopyFile->m_bCopyError = true; + break; + } + else + { + goto retry; + } + } + else + { + // Wait for the operation to finish + GetOverlappedResult( pCopyFile->m_hSrcFile, &overlappedRead, &dwBytesRead, TRUE ); + overlappedRead.Offset += dwBytesRead; + } + + if ( !dwBytesRead ) + { + pCopyFile->m_bCopyError = true; + break; + } + + pCopyFile->m_pCopyStats->m_bufferReadSize = dwBytesRead; + pCopyFile->m_pCopyStats->m_bufferReadTime = GetTickCount() - startTime; + pCopyFile->m_pCopyStats->m_totalReadSize += pCopyFile->m_pCopyStats->m_bufferReadSize; + pCopyFile->m_pCopyStats->m_totalReadTime += pCopyFile->m_pCopyStats->m_bufferReadTime; + + pBuffer->dwSize = dwBytesRead; + AddBufferForWrite( pBuffer ); + } + + return 0; +} + +//----------------------------------------------------------------------------- +// WriteFileThread +// +//----------------------------------------------------------------------------- +DWORD WINAPI WriteFileThread( LPVOID lParam ) +{ + CopyFile_t *pCopyFile; + OVERLAPPED overlappedWrite = {0}; + DWORD startTime; + DWORD dwBytesWrite; + DWORD dwWriteSize; + DWORD dwError; + BOOL bResult; + Buffer_t *pBuffer; + unsigned char *pWriteBuffer; + + pCopyFile = (CopyFile_t*)lParam; + + while ( overlappedWrite.Offset < pCopyFile->m_dstFileSize ) + { + // wait for wake-up event + pBuffer = LockBufferForWrite(); + + if ( pCopyFile->m_bInflate ) + { + startTime = GetTickCount(); + + DWORD dwSkip = overlappedWrite.Offset ? 0 : sizeof( xCompressHeader ); + dwWriteSize = JCALG1_Decompress_Formatted_Buffer( pBuffer->dwSize - dwSkip, pBuffer->pData + dwSkip, pCopyFile->m_inflateBufferSize, pCopyFile->m_pInflateBuffer ); + if ( dwWriteSize == (DWORD)-1 ) + { + pCopyFile->m_bCopyError = true; + break; + } + + pCopyFile->m_pCopyStats->m_inflateSize = dwWriteSize; + pCopyFile->m_pCopyStats->m_inflateTime = GetTickCount() - startTime; + + pWriteBuffer = pCopyFile->m_pInflateBuffer; + } + else + { + // straight copy + dwWriteSize = pBuffer->dwSize; + pWriteBuffer = pBuffer->pData; + } + + if ( overlappedWrite.Offset + dwWriteSize >= pCopyFile->m_dstFileSize ) + { + // last buffer, ensure all data is written + dwWriteSize = ALIGN( dwWriteSize, 512 ); + } + + startTime = GetTickCount(); + dwBytesWrite = 0; + + int numAttempts = 0; +retry: + // write file to HDD + bResult = WriteFile( pCopyFile->m_hDstFile, pWriteBuffer, (dwWriteSize/512) * 512, NULL, &overlappedWrite ); + dwError = GetLastError(); + if ( !bResult && dwError != ERROR_IO_PENDING ) + { + numAttempts++; + if ( numAttempts == 3 ) + { + // error + pCopyFile->m_bCopyError = true; + break; + } + else + { + goto retry; + } + } + else + { + // Wait for the operation to finish + GetOverlappedResult( pCopyFile->m_hDstFile, &overlappedWrite, &dwBytesWrite, TRUE ); + overlappedWrite.Offset += dwBytesWrite; + } + + if ( dwBytesWrite ) + { + // track expected size + pCopyFile->m_pCopyStats->m_bytesCopied += dwBytesWrite; + pCopyFile->m_pCopyStats->m_writeSize += dwBytesWrite; + } + else + { + pCopyFile->m_bCopyError = true; + break; + } + + pCopyFile->m_pCopyStats->m_bufferWriteSize = dwBytesWrite; + pCopyFile->m_pCopyStats->m_bufferWriteTime = GetTickCount() - startTime; + pCopyFile->m_pCopyStats->m_totalWriteSize += pCopyFile->m_pCopyStats->m_bufferWriteSize; + pCopyFile->m_pCopyStats->m_totalWriteTime += pCopyFile->m_pCopyStats->m_bufferWriteTime; + + AddBufferForRead( pBuffer ); + } + + return 0; +} + +//----------------------------------------------------------------------------- +// CopyFileInit +// +//----------------------------------------------------------------------------- +void CopyFileInit() +{ + static bool init = false; + if ( !init ) + { + InitializeCriticalSection( &g_criticalSection ); + g_hReadEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + g_hWriteEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + init = true; + } + else + { + // expected startup state + ResetEvent( g_hReadEvent ); + ResetEvent( g_hWriteEvent ); + + g_pReadBuffers = NULL; + g_pWriteBuffers = NULL; + } +} + +//----------------------------------------------------------------------------- +// CopyFileOverlapped +// +//----------------------------------------------------------------------------- +bool CopyFileOverlapped( const char *pSrcFilename, const char *pDstFilename, xCompressHeader *pxcHeader, CopyStats_t *pCopyStats ) +{ + CopyFile_t copyFile = {0}; + Buffer_t buffers[NUM_BUFFERS] = {0}; + HANDLE hReadThread = NULL; + HANDLE hWriteThread = NULL; + bool bSuccess = false; + DWORD startCopyTime; + DWORD dwResult; + int i; + + startCopyTime = GetTickCount(); + + CopyFileInit(); + + g_pNumReadBuffers = &pCopyStats->m_numReadBuffers; + g_pNumWriteBuffers = &pCopyStats->m_numWriteBuffers; + + strcpy( pCopyStats->m_srcFilename, pSrcFilename ); + strcpy( pCopyStats->m_dstFilename, pDstFilename ); + + copyFile.m_hSrcFile = INVALID_HANDLE_VALUE; + copyFile.m_hDstFile = INVALID_HANDLE_VALUE; + copyFile.m_pCopyStats = pCopyStats; + copyFile.m_bCopyError = false; + + // validate the source file + copyFile.m_hSrcFile = CreateFile( pSrcFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING, NULL ); + if ( copyFile.m_hSrcFile == INVALID_HANDLE_VALUE ) + { + // failure + goto cleanUp; + } + + copyFile.m_srcFileSize = GetFileSize( copyFile.m_hSrcFile, NULL ); + if ( copyFile.m_srcFileSize == (DWORD)-1 ) + { + // failure + goto cleanUp; + } + + // ensure the target file path exists + CreateFilePath( pDstFilename ); + + // validate the target file + copyFile.m_hDstFile = CreateFile( pDstFilename, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_OVERLAPPED|FILE_FLAG_NO_BUFFERING, NULL ); + if ( copyFile.m_hDstFile == INVALID_HANDLE_VALUE ) + { + // failure + goto cleanUp; + } + + pCopyStats->m_readSize = copyFile.m_srcFileSize; + pCopyStats->m_writeSize = 0; + + if ( pxcHeader ) + { + // read in chunks of compressed blocks + copyFile.m_readBufferSize = pxcHeader->nReadBlockSize; + copyFile.m_dstFileSize = pxcHeader->nUncompressedFileSize; + } + else + { + // setup for copy + copyFile.m_readBufferSize = BUFFER_SIZE; + copyFile.m_dstFileSize = copyFile.m_srcFileSize; + } + + // setup read buffers + for ( i=0; i<NUM_BUFFERS; i++) + { + buffers[i].pData = new unsigned char[copyFile.m_readBufferSize]; + buffers[i].dwSize = 0; + buffers[i].pNext = NULL; + AddBufferForRead( &buffers[i] ); + } + copyFile.m_numReadCycles = (copyFile.m_srcFileSize + copyFile.m_readBufferSize - 1)/copyFile.m_readBufferSize; + + // setup write buffer + if ( pxcHeader ) + { + copyFile.m_pInflateBuffer = new unsigned char[pxcHeader->nDecompressionBufferSize]; + copyFile.m_inflateBufferSize = pxcHeader->nDecompressionBufferSize; + copyFile.m_bInflate = true; + } + else + { + copyFile.m_bInflate = false; + } + + // pre-size the target file in aligned buffers + DWORD dwAligned = ALIGN( copyFile.m_dstFileSize, 512 ); + dwResult = SetFilePointer( copyFile.m_hDstFile, dwAligned, NULL, FILE_BEGIN ); + if ( dwResult == INVALID_SET_FILE_POINTER ) + { + // failure + goto cleanUp; + } + SetEndOfFile( copyFile.m_hDstFile ); + + // start the read thread + hReadThread = CreateThread( 0, 0, &ReadFileThread, ©File, 0, 0 ); + if ( !hReadThread ) + { + // failure + goto cleanUp; + } + + // wait for buffers to populate + + // start the write thread + hWriteThread = CreateThread( 0, 0, &WriteFileThread, ©File, 0, 0 ); + if ( !hWriteThread ) + { + // failure + goto cleanUp; + } + + // wait for write thread to finish + WaitForSingleObject( hWriteThread, INFINITE ); + WaitForSingleObject( hReadThread, INFINITE ); + + if ( copyFile.m_bCopyError ) + { + goto cleanUp; + } + + // Fixup the file size + CloseHandle( copyFile.m_hDstFile ); + copyFile.m_hDstFile = INVALID_HANDLE_VALUE; + + if ( copyFile.m_dstFileSize % 512 ) + { + // re-open file as non-buffered to adjust to correct file size + HANDLE hFile = CreateFile( pDstFilename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); + SetFilePointer( hFile, copyFile.m_dstFileSize, NULL, FILE_BEGIN ); + SetEndOfFile( hFile ); + CloseHandle( hFile ); + } + + // finished + bSuccess = true; + +cleanUp: + if ( copyFile.m_hSrcFile != INVALID_HANDLE_VALUE ) + { + CloseHandle( copyFile.m_hSrcFile ); + } + + if ( copyFile.m_hDstFile != INVALID_HANDLE_VALUE ) + { + CloseHandle( copyFile.m_hDstFile ); + } + + if ( hReadThread ) + { + CloseHandle( hReadThread ); + } + + if ( hWriteThread ) + { + CloseHandle( hWriteThread ); + } + + for ( i=0; i<NUM_BUFFERS; i++ ) + { + if ( buffers[i].pData ) + { + delete [] buffers[i].pData; + } + } + + if ( copyFile.m_pInflateBuffer ) + { + delete [] copyFile.m_pInflateBuffer; + } + + if ( !bSuccess ) + { + pCopyStats->m_copyErrors++; + } + + pCopyStats->m_copyTime = GetTickCount() - startCopyTime; + + return bSuccess; +} diff --git a/utils/xbox/xbox_loader/xbox_loader.cpp b/utils/xbox/xbox_loader/xbox_loader.cpp new file mode 100644 index 0000000..7cbeb77 --- /dev/null +++ b/utils/xbox/xbox_loader/xbox_loader.cpp @@ -0,0 +1,2019 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// APPLOADER.CPP +// +// Stub executeable +//=====================================================================================// +#include "xbox_loader.h" + +struct installData_t +{ + char **m_ppSrcFiles; + char **m_ppDstFiles; + DWORD *m_pDstFileSizes; + int m_numFiles; + DWORD m_totalSize; + xCompressHeader **m_ppxcHeaders; +}; + +DWORD g_installStartTime; +DWORD g_installElapsedTime; +installData_t g_installData; +CopyStats_t g_copyStats; +int g_activeDevice; +__int64 g_loaderStartTime; + + +//----------------------------------------------------------------------------- +// GetLocalizedLoadingString +//----------------------------------------------------------------------------- +const wchar_t *GetLocalizedLoadingString() +{ + switch( XGetLanguage() ) + { + case XC_LANGUAGE_FRENCH: + return L"CHARGEMENT..."; + case XC_LANGUAGE_ITALIAN: + return L"CARICAMENTO..."; + case XC_LANGUAGE_GERMAN: + return L"L�DT..."; + case XC_LANGUAGE_SPANISH: + return L"CARGANDO..."; + } + return L"LOADING..."; +} + +//----------------------------------------------------------------------------- +// GetNextLangauge +// Start at -1 +//----------------------------------------------------------------------------- +int GetNextLanguage( int languageID ) +{ + if ( languageID < 0 ) + return XC_LANGUAGE_ENGLISH; + + // cycle to end + switch ( languageID ) + { + case XC_LANGUAGE_ENGLISH: + return XC_LANGUAGE_FRENCH; + case XC_LANGUAGE_FRENCH: + return XC_LANGUAGE_ITALIAN; + case XC_LANGUAGE_ITALIAN: + return XC_LANGUAGE_GERMAN; + case XC_LANGUAGE_GERMAN: + return XC_LANGUAGE_SPANISH; + case XC_LANGUAGE_SPANISH: + return -1; + } + + return -1; +} + +//----------------------------------------------------------------------------- +// GetLanguageString +//----------------------------------------------------------------------------- +const char *GetLanguageString( int languageID ) +{ + switch( languageID ) + { + case XC_LANGUAGE_FRENCH: + return "french"; + case XC_LANGUAGE_ITALIAN: + return "italian"; + case XC_LANGUAGE_GERMAN: + return "german"; + case XC_LANGUAGE_SPANISH: + return "spanish"; + } + return "english"; +} + +//----------------------------------------------------------------------------- +// FixupNamespaceFilename +//----------------------------------------------------------------------------- +bool FixupNamespaceFilename( const char *pFilename, char *pOutFilename, int languageID ) +{ + char newFilename[MAX_PATH]; + + bool bFixup = false; + int dstLen = 0; + int srcLen = strlen( pFilename ); + for ( int i=0; i<srcLen+1; i++ ) + { + // replace every occurrence of % with language + if ( pFilename[i] == '%' ) + { + int len = strlen( GetLanguageString( languageID ) ); + memcpy( newFilename + dstLen, GetLanguageString( languageID ), len ); + dstLen += len; + bFixup = true; + } + else + { + newFilename[dstLen] = pFilename[i]; + dstLen++; + } + } + + strcpy( pOutFilename, newFilename ); + return bFixup; +} + +//----------------------------------------------------------------------------- +// DeleteOtherLocalizedFiles +//----------------------------------------------------------------------------- +void DeleteOtherLocalizedFiles( const char *pFilename, int languageIDToKeep ) +{ + char newFilename[MAX_PATH]; + char mrkFilename[MAX_PATH]; + bool bFixup; + + int languageID = -1; + while ( 1 ) + { + languageID = GetNextLanguage( languageID ); + if ( languageID == -1 ) + { + // cycled through + break; + } + + if ( languageID == languageIDToKeep ) + { + // skip + continue; + } + + bFixup = FixupNamespaceFilename( pFilename, newFilename, languageID ); + if ( !bFixup ) + { + // nothing to do + continue; + } + + SetFileAttributes( newFilename, FILE_ATTRIBUTE_NORMAL ); + DeleteFile( newFilename ); + + // delete marker + strcpy( mrkFilename, newFilename ); + strcat( mrkFilename, ".mrk" ); + SetFileAttributes( mrkFilename, FILE_ATTRIBUTE_NORMAL ); + DeleteFile( mrkFilename ); + } +} + +//----------------------------------------------------------------------------- +// LerpColor +//----------------------------------------------------------------------------- +unsigned int LerpColor( unsigned int c0, unsigned int c1, float t ) +{ + int i; + float a; + float b; + unsigned char* c; + unsigned int newcolor; + + if ( t <= 0.0f ) + return c0; + else if ( t >= 1.0f ) + return c1; + + // lerp each component + c = (unsigned char*)&newcolor; + for ( i=0; i<4; i++ ) + { + a = (float)(c0 & 0xFF); + b = (float)(c1 & 0xFF); + *c++ = (unsigned char)(a + t*(b-a)); + + // next color component + c0 >>= 8; + c1 >>= 8; + } + + return newcolor; +} + +//----------------------------------------------------------------------------- +// ConvertToWideString +//----------------------------------------------------------------------------- +void ConvertToWideString( wchar_t *pDst, const char *pSrc ) +{ + int len = strlen( pSrc )+1; + for (int i=0; i<len; i++) + { + pDst[i] = pSrc[i]; + } +} + +//----------------------------------------------------------------------------- +// CopyString +//----------------------------------------------------------------------------- +char *CopyString( const char *pString ) +{ + char *pNewString = (char *)malloc( strlen( pString ) + 1 ); + strcpy( pNewString, pString ); + + return pNewString; +} + +//----------------------------------------------------------------------------- +// FixFilename +//----------------------------------------------------------------------------- +void FixFilename( char *pPath ) +{ + int len = strlen( pPath ); + for (int i=0; i<len; ++i) + { + if ( pPath[i] == '/') + pPath[i] = '\\'; + } +} + +//----------------------------------------------------------------------------- +// StripQuotes +//----------------------------------------------------------------------------- +void StripQuotes( char *pToken ) +{ + int len = strlen( pToken ); + if ( pToken[0] == '"' && pToken[len-1] == '"' ) + { + memcpy( pToken, pToken+1, len-2 ); + pToken[len-2] = '\0'; + } +} + +//----------------------------------------------------------------------------- +// DoesFileExist +//----------------------------------------------------------------------------- +bool DoesFileExist( const char *pFilename, DWORD *pSize ) +{ + HANDLE hFile = CreateFile( pFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); + if ( hFile != INVALID_HANDLE_VALUE ) + { + if ( pSize ) + { + *pSize = GetFileSize( hFile, NULL ); + if ( *pSize == (DWORD)-1 ) + { + *pSize = 0; + } + } + + // exists + CloseHandle( hFile ); + return true; + } + + // not present + return false; +} + +//----------------------------------------------------------------------------- +// NormalizePath +// +//----------------------------------------------------------------------------- +void NormalizePath( char* path, bool forceToLower ) +{ + int i; + int srclen; + + srclen = strlen( path ); + for ( i=0; i<srclen; i++ ) + { + if ( path[i] == '/' ) + path[i] = '\\'; + else if ( forceToLower && ( path[i] >= 'A' && path[i] <= 'Z' ) ) + path[i] = path[i] - 'A' + 'a'; + } +} + +//----------------------------------------------------------------------------- +// DeleteAllFiles +// +//----------------------------------------------------------------------------- +void DeleteAllFiles( const char* pDirectory, int level, bool bRecurse ) +{ + HANDLE hFind; + WIN32_FIND_DATA findData; + char basepath[MAX_PATH]; + char searchpath[MAX_PATH]; + char filename[MAX_PATH]; + + TL_AddSeperatorToPath( (char*)pDirectory, basepath ); + strcpy( searchpath, basepath ); + strcat( searchpath, "*.*" ); + + hFind = FindFirstFile( searchpath, &findData ); + if ( hFind != INVALID_HANDLE_VALUE ) + { + do + { + if ( !stricmp( findData.cFileName, "." ) || !stricmp( findData.cFileName, ".." ) ) + { + continue; + } + + strcpy( filename, basepath ); + strcat( filename, findData.cFileName ); + NormalizePath( filename, false ); + + if ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) + { + if ( bRecurse ) + { + DeleteAllFiles( filename, level+1, true ); + RemoveDirectory( filename ); + continue; + } + } + + SetFileAttributes( filename, FILE_ATTRIBUTE_NORMAL ); + DeleteFile( filename ); + } + while ( FindNextFile( hFind, &findData ) ); + FindClose( hFind ); + } +} + +//----------------------------------------------------------------------------- +// GetXCompressedHeader +//----------------------------------------------------------------------------- +bool GetXCompressedHeader( const char *pFilename, xCompressHeader *pHeader ) +{ + HANDLE hFile = CreateFile( pFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); + if ( hFile != INVALID_HANDLE_VALUE ) + { + DWORD dwBytesRead = 0; + ReadFile( hFile, pHeader, sizeof( xCompressHeader ), &dwBytesRead, NULL ); + CloseHandle( hFile ); + if ( pHeader->nMagic == xCompressHeader::MAGIC && pHeader->nVersion == xCompressHeader::VERSION ) + { + // valid + return true; + } + } + + // invalid + return false; +} + +//----------------------------------------------------------------------------- +// IsTargetFileValid +// +// Optional non-zero expected source file size must matcg +//----------------------------------------------------------------------------- +bool IsTargetFileValid( const char *pFilename, DWORD dwSrcFileSize ) +{ + char mrkFilename[MAX_PATH]; + DWORD dwTargetFileSize = 0; + DWORD dwSize = 0; + DWORD dwAttributes; + + // all valid target files are non-zero + if ( !DoesFileExist( pFilename, &dwTargetFileSize ) || !dwTargetFileSize ) + { + return false; + } + + dwAttributes = GetFileAttributes( pFilename ); + if ( dwAttributes != (DWORD)-1 && ( dwAttributes & FILE_ATTRIBUTE_READONLY ) ) + { + // target files marked read only don't get overwritten + return true; + } + + // all valid target files must have marker file + // presence ensures file was successfully copied + strcpy( mrkFilename, pFilename ); + strcat( mrkFilename, ".mrk" ); + if ( !DoesFileExist( mrkFilename, NULL ) ) + { + // cannot rely on contents, regardless of size match + DeleteFile( pFilename ); + return false; + } + + if ( dwSrcFileSize && dwSrcFileSize != dwTargetFileSize ) + { + DeleteFile( pFilename ); + return false; + } + + // assume valid + return true; +} + +//----------------------------------------------------------------------------- +// Constructor for CXBoxLoader class +//----------------------------------------------------------------------------- +CXBoxLoader::CXBoxLoader() : CXBApplication() +{ + // need a persistent time base, use the RTC + // all other tick counters reset across relaunch + FILETIME fileTime; + GetSystemTimeAsFileTime( &fileTime ); + g_loaderStartTime = ((ULARGE_INTEGER*)&fileTime)->QuadPart; + + m_contextCode = 0; + m_pLastMovieFrame = NULL; + m_pVB = NULL; + m_bAllowAttractAbort = false; + m_numFiles = 0; + m_bLaunch = false; + m_dwLoading = 0; + m_bDrawLegal = false; + m_LegalTime = 0; + m_installThread = NULL; + m_State = 0; + m_bDrawLoading = false; + m_bDrawProgress = false; + m_bInstallComplete = false; + m_FrameCounter = 0; + m_MovieCount = 0; + m_bMovieErrorIsFatal = false; + m_bDrawDebug = false; + m_LoadingBarStartTime = 0; + m_LoadingBarEndTime = 0; + m_LegalStartTime = 0; + m_bCaptureLastMovieFrame = 0; + m_bDrawSlideShow = false; + m_SlideShowStartTime = 0; + m_pLogData = NULL; + m_pDefaultTrueTypeFont = NULL; +} + +//----------------------------------------------------------------------------- +// FatalMediaError +//----------------------------------------------------------------------------- +void CXBoxLoader::FatalMediaError() +{ + m_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL, 0x00000000, 1.0f, 0L ); + + LPDIRECT3DSURFACE8 pBackBuffer; + m_pd3dDevice->GetBackBuffer( 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer ); + + LPCWSTR pLine1; + LPCWSTR pLine2; + switch( XGetLanguage() ) + { + case XC_LANGUAGE_FRENCH: + pLine1 = L"Le disque utilis� pr�sente une anomalie."; + pLine2 = L"Il est peut-�tre sale ou endommag�."; + break; + case XC_LANGUAGE_ITALIAN: + pLine1 = L"Il disco in uso ha qualche problema."; + pLine2 = L"Potrebbe essere sporco o danneggiato."; + break; + case XC_LANGUAGE_GERMAN: + pLine1 = L"Bei der benutzten CD ist ein Problem aufgetreten."; + pLine2 = L"M�glicherweise ist sie verschmutzt oder besch�digt."; + break; + case XC_LANGUAGE_SPANISH: + pLine1 = L"Hay un problema con el disco que est� usando."; + pLine2 = L"Puede estar sucio o da�ado."; + break; + default: + pLine1 = L"There is a problem with the disc you are using."; + pLine2 = L"It may be dirty or damaged."; + break; + } + + if ( m_pDefaultTrueTypeFont ) + { + m_pDefaultTrueTypeFont->SetTextAlignment( XFONT_CENTER|XFONT_TOP ); + m_pDefaultTrueTypeFont->TextOut( pBackBuffer, pLine1, (unsigned)-1, 320, 240-15 ); + m_pDefaultTrueTypeFont->TextOut( pBackBuffer, pLine2, (unsigned)-1, 320, 240+15 ); + } + + // Present the scene + m_pd3dDevice->Present( NULL, NULL, NULL, NULL ); + + pBackBuffer->Release(); + + // forever + while (1); +} + +//----------------------------------------------------------------------------- +// LoadTexture +//----------------------------------------------------------------------------- +D3DTexture *CXBoxLoader::LoadTexture( int resourceID ) +{ + // Get access to the texture + return ( m_xprResource.GetTexture( resourceID ) ); +} + +//----------------------------------------------------------------------------- +// LoadFont +//----------------------------------------------------------------------------- +HRESULT CXBoxLoader::LoadFont( CXBFont *pFont, int resourceID ) +{ + return pFont->Create( m_xprResource.GetTexture( resourceID ), + m_xprResource.GetData( resourceID + sizeof(D3DTexture) ) ); +} + +//----------------------------------------------------------------------------- +// DrawRect +//----------------------------------------------------------------------------- +void CXBoxLoader::DrawRect( int x, int y, int w, int h, DWORD color ) +{ + // Set states + D3DDevice::SetTexture( 0, NULL ); + D3DDevice::SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_DISABLE ); + D3DDevice::SetRenderState( D3DRS_ZENABLE, FALSE ); + D3DDevice::SetRenderState( D3DRS_FOGENABLE, FALSE ); + D3DDevice::SetRenderState( D3DRS_FOGTABLEMODE, D3DFOG_NONE ); + D3DDevice::SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID ); + D3DDevice::SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); + D3DDevice::SetRenderState( D3DRS_ALPHATESTENABLE, FALSE ); + D3DDevice::SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); + D3DDevice::SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); + D3DDevice::SetVertexShader( D3DFVF_XYZRHW|D3DFVF_DIFFUSE ); + + FLOAT fX1 = x; + FLOAT fY1 = y; + FLOAT fX2 = x + w - 1; + FLOAT fY2 = y + h - 1; + + D3DDevice::Begin( D3DPT_QUADLIST ); + D3DDevice::SetVertexDataColor( D3DVSDE_DIFFUSE, color ); + D3DDevice::SetVertexData4f( D3DVSDE_VERTEX, fX1, fY1, 1.0f, 1.0f ); + D3DDevice::SetVertexData4f( D3DVSDE_VERTEX, fX2, fY1, 1.0f, 1.0f ); + D3DDevice::SetVertexData4f( D3DVSDE_VERTEX, fX2, fY2, 1.0f, 1.0f ); + D3DDevice::SetVertexData4f( D3DVSDE_VERTEX, fX1, fY2, 1.0f, 1.0f ); + D3DDevice::End(); +} + + +//----------------------------------------------------------------------------- +// DrawTexture +//----------------------------------------------------------------------------- +void CXBoxLoader::DrawTexture( D3DTexture *pD3DTexture, int x, int y, int w, int h, int color ) +{ + struct VERTEX { D3DXVECTOR4 p; D3DCOLOR c; FLOAT tu, tv; }; + if ( !m_pVB ) + { + // Create a vertex buffer for rendering the help screen + D3DDevice::CreateVertexBuffer( 4*sizeof(VERTEX), D3DUSAGE_WRITEONLY, 0L, D3DPOOL_DEFAULT, &m_pVB ); + } + + VERTEX* v; + m_pVB->Lock( 0, 0, (BYTE**)&v, 0L ); + + // Calculate vertex positions + FLOAT fLeft = x + 0.5f; + FLOAT fTop = y + 0.5f; + FLOAT fRight = x+w - 0.5f; + FLOAT fBottom = y+h - 0.5f; + + // position + v[0].p = D3DXVECTOR4( fLeft, fTop, 0, 0 ); + v[1].p = D3DXVECTOR4( fRight, fTop, 0, 0 ); + v[2].p = D3DXVECTOR4( fRight, fBottom, 0, 0 ); + v[3].p = D3DXVECTOR4( fLeft, fBottom, 0, 0 ); + + // color + v[0].c = color; + v[1].c = color; + v[2].c = color; + v[3].c = color; + + D3DSURFACE_DESC desc; + pD3DTexture->GetLevelDesc( 0, &desc ); + + // linear texcoords + v[0].tu = 0; + v[0].tv = 0; + v[1].tu = desc.Width; + v[1].tv = 0; + v[2].tu = desc.Width; + v[2].tv = desc.Height; + v[3].tu = 0; + v[3].tv = desc.Height; + + m_pVB->Unlock(); + + // Set state to render the image + D3DDevice::SetTexture( 0, pD3DTexture ); + D3DDevice::SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE ); + D3DDevice::SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); + D3DDevice::SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); + D3DDevice::SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE ); + D3DDevice::SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE ); + D3DDevice::SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE ); + D3DDevice::SetTextureStageState( 0, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP ); + D3DDevice::SetTextureStageState( 0, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP ); + D3DDevice::SetTextureStageState( 0, D3DTSS_ADDRESSW, D3DTADDRESS_CLAMP ); + D3DDevice::SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR ); + D3DDevice::SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR ); + D3DDevice::SetRenderState( D3DRS_ZENABLE, FALSE ); + D3DDevice::SetRenderState( D3DRS_ZWRITEENABLE, FALSE ); + D3DDevice::SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_ALWAYS ); + D3DDevice::SetRenderState( D3DRS_FOGENABLE, FALSE ); + D3DDevice::SetRenderState( D3DRS_FOGTABLEMODE, D3DFOG_NONE ); + D3DDevice::SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID ); + D3DDevice::SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW ); + D3DDevice::SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); + D3DDevice::SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA ); + D3DDevice::SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA ); + D3DDevice::SetRenderState( D3DRS_ALPHATESTENABLE, FALSE ); + D3DDevice::SetVertexShader( D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1 ); + + // Render the image + D3DDevice::SetStreamSource( 0, m_pVB, sizeof(VERTEX) ); + D3DDevice::DrawPrimitive( D3DPT_QUADLIST, 0, 1 ); +} + +//----------------------------------------------------------------------------- +// StartVideo +// +// May take a few ms. +//----------------------------------------------------------------------------- +HRESULT CXBoxLoader::StartVideo( const CHAR* strFilename, bool bFromMemory, bool bFatalOnError ) +{ + HRESULT hr; + if ( bFromMemory ) + { + // play from memory, so as no to interfere with disc access + hr = m_player.OpenMovieFromMemory( strFilename, D3DFMT_LIN_A8R8G8B8, m_pd3dDevice, TRUE ); + } + else + { + // play from disc + hr = m_player.OpenFile( strFilename, D3DFMT_LIN_A8R8G8B8, m_pd3dDevice, TRUE ); + } + + // can fail anytime + m_bMovieErrorIsFatal = bFatalOnError; + + m_MovieCount++; + + if ( FAILED( hr ) ) + { + OUTPUT_DEBUG_STRING( "Video playback failed!\n" ); + + if ( bFatalOnError ) + { + FatalMediaError(); + } + } + + return hr; +} + +//----------------------------------------------------------------------------- +// StopVideo +// +// May take a few ms. +//----------------------------------------------------------------------------- +void CXBoxLoader::StopVideo() +{ + m_player.TerminatePlayback(); +} + +//----------------------------------------------------------------------------- +// LoadInstallScript +// +// Parse filenames to be copied +//----------------------------------------------------------------------------- +bool CXBoxLoader::LoadInstallScript() +{ + void *pFileData = NULL; + DWORD fileSize = 0; + DWORD dwSrcSize; + HRESULT hr; + char srcFile[MAX_PATH]; + char dstFile[MAX_PATH]; + char localizedFile[MAX_PATH]; + HANDLE hFind; + char sourceFilename[MAX_PATH]; + char sourcePath[MAX_PATH]; + char targetFilename[MAX_PATH]; + char filename[MAX_PATH]; + WIN32_FIND_DATA findData; + bool bCompressed; + xCompressHeader xcHeader; + char *pVersion; + bool bTargetIsLocalized; + int languageID; + + memset( &g_installData, 0, sizeof( installData_t ) ); + + hr = XBUtil_LoadFile( "D:\\LoaderMedia\\install.txt", &pFileData, &fileSize ); + if ( hr != S_OK || !fileSize ) + { + return false; + } + + languageID = XGetLanguage(); + + // full re-install + bool bForce = true; + + // scan + TL_SetScriptData( (char*)pFileData, fileSize ); + while ( 1 ) + { + char *pToken = TL_GetToken( true ); + if ( !pToken || !pToken[0] ) + break; + StripQuotes( pToken ); + strcpy( srcFile, pToken); + + pToken = TL_GetToken( true ); + if ( !pToken || !pToken[0] ) + break; + StripQuotes( pToken ); + strcpy( dstFile, pToken); + + // replace with language token + FixupNamespaceFilename( srcFile, srcFile, languageID ); + bTargetIsLocalized = FixupNamespaceFilename( dstFile, localizedFile, languageID ); + + if ( bTargetIsLocalized ) + { + // localized files are allowed to change without requiring a full re-install + bool bDeleteMapCache = false; + if ( !IsTargetFileValid( localizedFile, 0 ) ) + { + // must delete map cache to ensure localized files have enough room + bDeleteMapCache = true; + } + + // only allowing one localized file of this type, delete all others + DeleteOtherLocalizedFiles( dstFile, languageID ); + strcpy( dstFile, localizedFile ); + + if ( bDeleteMapCache ) + { + char mapPath[MAX_PATH]; + strcpy( mapPath, localizedFile ); + TL_StripFilename( mapPath ); + TL_AddSeperatorToPath( mapPath, mapPath ); + strcat( mapPath, "maps\\" ); + DeleteAllFiles( mapPath, 0, false ); + } + } + + pVersion = strstr( dstFile, "version_" ); + if ( pVersion ) + { + if ( m_numFiles ) + { + // version statement out of sequence + return false; + } + + m_Version = atoi( pVersion + strlen( "version_" ) ); + + if ( IsTargetFileValid( dstFile, 0 ) ) + { + // version file exists, files should be same + bForce = false; + } + + if ( bForce ) + { + // delete all files at the specified directory + strcpy( targetFilename, dstFile ); + TL_StripFilename( targetFilename ); + DeleteAllFiles( targetFilename, 0, true ); + } + } + + // source file could be wildcard, get path only + strcpy( sourcePath, srcFile ); + TL_StripFilename( sourcePath ); + + hFind = FindFirstFile( srcFile, &findData ); + if ( hFind != INVALID_HANDLE_VALUE ) + { + do + { + if ( !stricmp( findData.cFileName, "." ) || !stricmp( findData.cFileName, ".." ) ) + { + continue; + } + if ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) + { + continue; + } + + TL_AddSeperatorToPath( sourcePath, sourceFilename ); + strcat( sourceFilename, findData.cFileName ); + NormalizePath( sourceFilename, false ); + + // target filename may be path or absolute file + strcpy( targetFilename, dstFile ); + + TL_StripPath( dstFile, filename ); + if ( !filename[0] ) + { + // target filename is path only + TL_AddSeperatorToPath( dstFile, targetFilename ); + strcat( targetFilename, findData.cFileName ); + NormalizePath( targetFilename, false ); + } + + if ( !DoesFileExist( sourceFilename, &dwSrcSize ) ) + { + // can't validate source + return false; + } + + if ( strstr( sourceFilename, ".xz_" ) && strstr( targetFilename, ".xzp" ) ) + { + bCompressed = true; + if ( GetXCompressedHeader( sourceFilename, &xcHeader ) ) + { + g_installData.m_totalSize += xcHeader.nUncompressedFileSize; + + if ( !bForce && IsTargetFileValid( targetFilename, xcHeader.nUncompressedFileSize ) ) + { + // target already exists, no need to recopy + g_copyStats.m_bytesCopied += xcHeader.nUncompressedFileSize; + continue; + } + } + else + { + // invalid + return false; + } + } + else + { + g_installData.m_totalSize += dwSrcSize; + + bCompressed = false; + if ( !bForce && IsTargetFileValid( targetFilename, dwSrcSize ) ) + { + // target already exists, no need to recopy + g_copyStats.m_bytesCopied += dwSrcSize; + continue; + } + } + + if ( m_numFiles < MAX_FILES ) + { + m_fileSrc[m_numFiles] = CopyString( sourceFilename ); + m_fileDest[m_numFiles] = CopyString( targetFilename ); + + if ( bCompressed ) + { + xCompressHeader *pxcHeader = new xCompressHeader; + memcpy( pxcHeader, &xcHeader, sizeof( xCompressHeader ) ); + m_fileCompressionHeaders[m_numFiles] = pxcHeader; + m_fileDestSizes[m_numFiles] = pxcHeader->nUncompressedFileSize; + } + else + { + m_fileCompressionHeaders[m_numFiles] = NULL; + m_fileDestSizes[m_numFiles] = dwSrcSize; + } + + m_numFiles++; + } + } + while ( FindNextFile( hFind, &findData ) ); + FindClose( hFind ); + } + else + { + // source file not found, invalid + return false; + } + } + + // finsihed with install script + free( pFileData ); + + g_installData.m_ppSrcFiles = m_fileSrc; + g_installData.m_ppDstFiles = m_fileDest; + g_installData.m_ppxcHeaders = m_fileCompressionHeaders; + g_installData.m_pDstFileSizes = m_fileDestSizes; + g_installData.m_numFiles = m_numFiles; + + return true; +} + +//----------------------------------------------------------------------------- +// Copies all install files to the hard drive +//----------------------------------------------------------------------------- +DWORD WINAPI InstallThreadFunc( LPVOID lpParam ) +{ + char mrkFilename[MAX_PATH]; + bool bSuccess; + HANDLE hFile; + + g_installStartTime = GetTickCount(); + + // started loading + *(DWORD*)lpParam = 1; + + for ( int i = 0; i < g_installData.m_numFiles; ++i ) + { + DWORD bytesCopied = g_copyStats.m_bytesCopied; + + // install has already validated, if it's in the list, copy it + bSuccess = CopyFileOverlapped( g_installData.m_ppSrcFiles[i], g_installData.m_ppDstFiles[i], g_installData.m_ppxcHeaders[i], &g_copyStats ); + + strcpy( mrkFilename, g_installData.m_ppDstFiles[i] ); + strcat( mrkFilename, ".mrk" ); + + SetFileAttributes( mrkFilename, FILE_ATTRIBUTE_NORMAL ); + if ( bSuccess ) + { + // add marker + hFile = CreateFile( mrkFilename, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); + if ( hFile != INVALID_HANDLE_VALUE ) + { + CloseHandle( hFile ); + } + } + else + { + // remove marker + DeleteFile( mrkFilename ); + DeleteFile( g_installData.m_ppDstFiles[i] ); + + // errors can't stop install + // snap progress to expected completion + g_copyStats.m_bytesCopied = bytesCopied + g_installData.m_pDstFileSizes[i]; + } + } + + g_installElapsedTime = GetTickCount() - g_installStartTime; + + // finished loading + *(DWORD*)lpParam = 0; + + return 0; +} + +//----------------------------------------------------------------------------- +// Verify disk space +//----------------------------------------------------------------------------- +bool CXBoxLoader::VerifyInstall( void ) +{ + memset( &g_copyStats, 0, sizeof( CopyStats_t ) ); + + LoadLogFile(); + + if ( !LoadInstallScript() ) + { + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Look for possible forensic log file +//----------------------------------------------------------------------------- +void CXBoxLoader::LoadLogFile( void ) +{ +#if defined( XBOX_FORENSIC_LOG ) + HRESULT hr; + char *pFileData = NULL; + DWORD fileSize = 0; + + hr = XBUtil_LoadFile( "Z:\\hl2fatal.log", (void**)&pFileData, &fileSize ); + if ( hr != S_OK || !fileSize ) + { + return; + } + + // copy and null terminate + m_pLogData = (char *)malloc( fileSize+1 ); + + int j = 0; + for (int i=0; i<(int)fileSize; i++) + { + if ( pFileData[i] == 0x0D ) + continue; + m_pLogData[j++] = pFileData[i]; + } + m_pLogData[j] = '\0'; + + free( pFileData ); +#endif +} + +//----------------------------------------------------------------------------- +// Starts installation to disk +//----------------------------------------------------------------------------- +bool CXBoxLoader::StartInstall( void ) +{ + // Start the install thread + m_installThread = CreateThread( NULL, 0, &InstallThreadFunc, &m_dwLoading, 0, 0 ); + if ( !m_installThread ) + { + // failed + return false; + } + + // success + return true; +} + +//----------------------------------------------------------------------------- +// Shows the legal text +//----------------------------------------------------------------------------- +void CXBoxLoader::StartLegalScreen( int legal ) +{ + m_bDrawLegal = true; + m_LegalTime = GetTickCount(); + m_LegalStartTime = 0; + + switch ( legal ) + { + case LEGAL_MAIN: + m_pLegalTexture = m_pMainLegalTexture; + break; + case LEGAL_SOURCE: + m_pLegalTexture = m_pSourceLegalTexture; + break; + } +} + +//----------------------------------------------------------------------------- +// DrawLegals +//----------------------------------------------------------------------------- +void CXBoxLoader::DrawLegals() +{ + unsigned int color; + float t; + + if ( !m_bDrawLegal ) + return; + + if ( !m_LegalStartTime ) + { + m_LegalStartTime = GetTickCount(); + } + + // fade legals + t = (float)(GetTickCount() - m_LegalStartTime)/LEGAL_DISPLAY_TIME; + if ( t < 0.1f ) + { + // fade up + color = LerpColor( 0xFF000000, 0xFFFFFFFF, t*10.0f ); + } + else if ( t < 0.9f ) + { + // hold + color = 0xFFFFFFFF; + } + else + { + // fade out + color = LerpColor( 0xFFFFFFFF, 0xFF000000, (t-0.9f)*10.0f ); + } + + DrawTexture( m_pLegalTexture, 0, 0, 640, 480, color ); +} + +//----------------------------------------------------------------------------- +// DrawSlideshow +//----------------------------------------------------------------------------- +void CXBoxLoader::DrawSlideshow() +{ + float t; + + if ( !m_bDrawSlideShow ) + return; + + if ( !m_SlideShowStartTime ) + { + m_SlideShowStartTime = GetTickCount(); + m_SlideShowCount = -1; + m_bFinalSlide = false; + } + + if ( !m_bInstallComplete && ( GetTickCount() - m_SlideShowStartTime > SLIDESHOW_SLIDETIME ) ) + { + // next slide + m_SlideShowCount++; + m_SlideShowStartTime = GetTickCount(); + } + + t = ( GetTickCount() - m_SlideShowStartTime )/(float)SLIDESHOW_FLIPTIME; + if ( t >= 1.0f ) + t = 1.0f; + + if ( m_bInstallComplete && !m_bFinalSlide && t >= 1.0f ) + { + // wait for current slide to complete + // final slide must transition back to transition screen + m_SlideShowStartTime = GetTickCount(); + m_bFinalSlide = true; + t = 0; + } + + if ( !m_bFinalSlide ) + { + // fade next slide in + unsigned int fadeInColor = LerpColor( 0x00FFFFFF, 0xFFFFFFFF, t ); + if ( fadeInColor != 0xFFFFFFFF && m_SlideShowCount != -1 ) + DrawTexture( m_pSlideShowTextures[m_SlideShowCount % MAX_SLIDESHOW_TEXTURES], 0, 0, 640, 480, 0xFFFFFFFF ); + DrawTexture( m_pSlideShowTextures[(m_SlideShowCount + 1) % MAX_SLIDESHOW_TEXTURES], 0, 0, 640, 480, fadeInColor ); + } + else + { + // fade last slide out + unsigned int fadeInColor = LerpColor( 0xFFFFFFFF, 0x00FFFFFF, t ); + DrawTexture( m_pSlideShowTextures[(m_SlideShowCount + 1) % MAX_SLIDESHOW_TEXTURES], 0, 0, 640, 480, fadeInColor ); + } + + if ( m_bInstallComplete && m_bFinalSlide && t >= 1.0f ) + { + // end of slideshow + m_bDrawSlideShow = false; + } +} + +//----------------------------------------------------------------------------- +// DrawDebug +//----------------------------------------------------------------------------- +void CXBoxLoader::DrawDebug() +{ +#ifndef _RETAIL + if ( !m_bDrawDebug ) + return; + + DrawRect( 0, 0, 640, 480, 0xC0000000 ); + + m_Font.Begin(); + m_Font.SetScaleFactors( 0.8f, 0.8f ); + + int xPos = SCREEN_WIDTH/2; + int yPos = SCREEN_HEIGHT/4; + float rate; + + wchar_t textBuffer[256]; + swprintf( textBuffer, L"Version: %d", m_Version ); + m_Font.DrawText( 40, 40, 0xffffffff, textBuffer, 0 ); + + wchar_t srcFilename[MAX_PATH]; + wchar_t dstFilename[MAX_PATH]; + ConvertToWideString( srcFilename, g_copyStats.m_srcFilename ); + ConvertToWideString( dstFilename, g_copyStats.m_dstFilename ); + swprintf( textBuffer, L"From: %s (%.2f MB)", srcFilename, (float)g_copyStats.m_readSize/(1024.0f*1024.0f) ); + m_Font.DrawText( xPos, yPos + 20, 0xffffffff, textBuffer, XBFONT_CENTER_X ); + swprintf( textBuffer, L"To: %s (%.2f MB)", dstFilename, (float)g_copyStats.m_writeSize/(1024.0f*1024.0f) ); + m_Font.DrawText( xPos, yPos + 40, 0xffffffff, textBuffer, XBFONT_CENTER_X ); + + if ( g_copyStats.m_bufferReadTime && m_dwLoading ) + rate = ( g_copyStats.m_bufferReadSize/(1024.0f*1024.0f) ) / ( g_copyStats.m_bufferReadTime * 0.001f ); + else + rate = 0; + swprintf( textBuffer, L"Buffer Read: %.2f MB (%.2f MB/s) (%d)", g_copyStats.m_bufferReadSize/(1024.0f*1024.0f), rate, g_copyStats.m_numReadBuffers ); + m_Font.DrawText( xPos, yPos + 80, 0xffffffff, textBuffer, XBFONT_CENTER_X ); + + rate = g_copyStats.m_inflateTime && m_dwLoading ? (float)g_copyStats.m_inflateSize/(g_copyStats.m_inflateTime * 0.001f) : 0; + swprintf( textBuffer, L"Inflate: %.2f MB (%.2f MB/s)", g_copyStats.m_inflateSize/(1024.0f*1024.0f), rate/(1024.0f*1024.0f) ); + m_Font.DrawText( xPos, yPos + 100, 0xffffffff, textBuffer, XBFONT_CENTER_X ); + + if ( g_copyStats.m_bufferWriteTime && m_dwLoading ) + rate = ( g_copyStats.m_bufferWriteSize/(1024.0f*1024.0f) ) / ( g_copyStats.m_bufferWriteTime * 0.001f ); + else + rate = 0; + swprintf( textBuffer, L"Buffer Write: %.2f MB (%.2f MB/s) (%d)", g_copyStats.m_bufferWriteSize/(1024.0f*1024.0f), rate, g_copyStats.m_numWriteBuffers ); + m_Font.DrawText( xPos, yPos + 120, 0xffffffff, textBuffer, XBFONT_CENTER_X ); + + rate = g_copyStats.m_totalReadTime && m_dwLoading ? (float)g_copyStats.m_totalReadSize/(g_copyStats.m_totalReadTime * 0.001f) : 0; + swprintf( textBuffer, L"Total Read: %d MB (%.2f MB/s)", g_copyStats.m_totalReadSize/(1024*1024), rate/(1024.0f*1024.0f) ); + m_Font.DrawText( xPos, yPos + 160, 0xffffffff, textBuffer, XBFONT_CENTER_X ); + + rate = g_copyStats.m_totalWriteTime && m_dwLoading ? (float)g_copyStats.m_totalWriteSize/(g_copyStats.m_totalWriteTime * 0.001f) : 0; + swprintf( textBuffer, L"Total Write: %d MB (%.2f MB/s)", g_copyStats.m_totalWriteSize/(1024*1024), rate/(1024.0f*1024.0f) ); + m_Font.DrawText( xPos, yPos + 180, 0xffffffff, textBuffer, XBFONT_CENTER_X ); + + float elapsed = (float)(GetTickCount() - g_installStartTime) * 0.001f; + if ( m_dwLoading ) + { + if ( elapsed ) + rate = g_copyStats.m_totalWriteSize/elapsed; + else + rate = 0; + } + else + { + if ( g_installElapsedTime ) + rate = g_copyStats.m_totalWriteSize/(g_installElapsedTime * 0.001f); + else + rate = 0; + } + swprintf( textBuffer, L"Progress: %d/%d MB Elapsed: %d secs (%.2f MB/s)", g_copyStats.m_bytesCopied/(1024*1024), g_installData.m_totalSize/(1024*1024), (int)elapsed, rate/(1024.0f*1024.0f) ); + m_Font.DrawText( xPos, yPos + 220, 0xffffffff, textBuffer, XBFONT_CENTER_X ); + + swprintf( textBuffer, L"Errors: %d", g_copyStats.m_copyErrors ); + m_Font.DrawText( xPos, yPos + 240, 0xffffffff, textBuffer, XBFONT_CENTER_X ); + + m_Font.End(); +#endif +} + +void CXBoxLoader::DrawLog() +{ +#if defined( XBOX_FORENSIC_LOG ) + wchar_t textBuffer[1024]; + int numChars; + + if ( !m_pLogData ) + return; + + DrawRect( 0, 0, 640, 480, 0xC0000000 ); + + m_Font.Begin(); + m_Font.SetScaleFactors( 0.8f, 0.8f ); + + char *pStart = m_pLogData; + char *pEnd = pStart; + int yPos = 40; + for (int i=0; i<20; i++) + { + pEnd = strstr( pStart, "\n" ); + if ( !pEnd ) + numChars = strlen( pStart ); + else + numChars = pEnd-pStart; + + if ( numChars ) + { + for (int j=0; j<numChars; j++) + { + textBuffer[j] = pStart[j]; + } + textBuffer[j] = 0; + m_Font.DrawText( 40, yPos, 0xffffffff, textBuffer, 0 ); + } + + if ( !pEnd ) + break; + + // next line + pStart = pEnd+1; + yPos += 10; + } + + m_Font.End(); +#endif +} + +//----------------------------------------------------------------------------- +// DrawLoadingMarquee +//----------------------------------------------------------------------------- +void CXBoxLoader::DrawLoadingMarquee() +{ + if ( !m_bDrawLoading ) + return; + + int y = 0.80f*480; + DrawTexture( m_pLoadingIconTexture, (640-64)/2, y-64, 64, 64, 0xFFFFFFFF ); + + // draw loading text + m_Font.Begin(); + m_Font.SetScaleFactors( 0.8f, 0.8f ); + m_Font.DrawText( 320, y, PROGRESS_TEXT_COLOR, GetLocalizedLoadingString(), XBFONT_CENTER_X ); + m_Font.End(); +} + +//----------------------------------------------------------------------------- +// DrawProgressBar +//----------------------------------------------------------------------------- +void CXBoxLoader::DrawProgressBar() +{ + if ( !m_bDrawProgress ) + return; + + if ( !m_LoadingBarStartTime ) + { + m_LoadingBarStartTime = GetTickCount(); + } + + // slide the loading bar up + float tUp = (float)(GetTickCount() - m_LoadingBarStartTime)/LOADINGBAR_UPTIME; + if ( tUp > 1.0f) + tUp = 1.0f; + float y = 480.0f + tUp*((float)PROGRESS_Y - 480.0f); + + float t0 = 0; + float t1 = 0; + int numSegments = 0; + if ( tUp == 1.0f ) + { + // loading bar is up + // don't snap, animate progress to current level of completion + t0 = (float)g_copyStats.m_bytesCopied/(float)g_installData.m_totalSize; + if ( t0 > 1.0f ) + t0 = 1.0f; + t1 = (float)(GetTickCount() - m_LoadingBarStartTime - LOADINGBAR_WAITTIME)/LOADINGBAR_SLIDETIME; + if ( t1 < 0.0f ) + t1 = 0.0f; + else if ( t1 > 1.0f) + t1 = 1.0f; + numSegments = t0 * t1 * (float)SEGMENT_COUNT; + } + +#if 0 + float tDown = 0; + if ( t0 == 1.0f && t1 == 1.0f && !m_dwLoading ) + { + // loading anim and copying of data are finished + // slide the loading bar down + if ( !m_LoadingBarEndTime ) + { + m_LoadingBarEndTime = GetTickCount(); + } + tDown = (float)(GetTickCount() - m_LoadingBarEndTime - LOADINGBAR_WAITTIME)/LOADINGBAR_UPTIME; + if ( tDown < 0.0f ) + tDown = 0.0f; + else if ( tDown > 1.0f) + tDown = 1.0f; + y = PROGRESS_Y + tDown*(480.0f - (float)PROGRESS_Y); + } + + if ( tDown == 1.0f ) + { + // loading bar is offscreen + m_bInstallComplete = true; + } +#else + if ( t0 == 1.0f && t1 == 1.0f && !m_dwLoading ) + { + m_bInstallComplete = true; + } +#endif + + int x = (640-FOOTER_W)/2; + DrawTexture( m_pFooterTexture, x, y, FOOTER_W, 480 - PROGRESS_Y, PROGRESS_FOOTER_COLOR ); + x += FOOTER_W - 35; + + // draw left justified loading text + m_Font.Begin(); + m_Font.SetScaleFactors( 0.8f, 0.8f ); + int textWidth = m_Font.GetTextWidth( GetLocalizedLoadingString() ); + x -= SEGMENT_W + textWidth; + m_Font.DrawText( x, y+20, PROGRESS_TEXT_COLOR, GetLocalizedLoadingString(), XBFONT_LEFT ); + m_Font.End(); + + // draw progess bar + x -= SEGMENT_W + PROGRESS_W; + DrawRect( x, y+25, PROGRESS_W, PROGRESS_H, PROGRESS_INSET_COLOR ); + for ( int i =0; i<numSegments; i++ ) + { + DrawRect( x, y+25+2, SEGMENT_W, PROGRESS_H-4, PROGRESS_SEGMENT_COLOR ); + x += SEGMENT_W+SEGMENT_GAP; + } + +} + +//----------------------------------------------------------------------------- +// Name: PlayVideoFrame() +// Desc: Plays one frame of video if a movie is currently open and if there is +// a frame available. This function is safe to call at any time. +//----------------------------------------------------------------------------- +BOOL CXBoxLoader::PlayVideoFrame() +{ + if ( !m_player.IsPlaying() ) + return FALSE; + + const FLOAT fMovieWidth = FLOAT( m_player.GetWidth() ); + const FLOAT fMovieHeight = FLOAT( m_player.GetHeight() ); + + // Move to the next frame. + LPDIRECT3DTEXTURE8 pTexture = 0; + pTexture = m_player.AdvanceFrameForTexturing( m_pd3dDevice ); + + // See if the movie is over now. + if ( !m_player.IsPlaying() ) + { + if ( m_bCaptureLastMovieFrame ) + { + m_bCaptureLastMovieFrame = false; + + // Copy Texture + if ( m_pLastMovieFrame ) + { + m_pLastMovieFrame->Release(); + m_pLastMovieFrame = NULL; + } + + if ( pTexture ) + { + // copy the last frame + D3DSURFACE_DESC d3dSurfaceDesc; + D3DLOCKED_RECT srcRect; + D3DLOCKED_RECT dstRect; + pTexture->GetLevelDesc( 0, &d3dSurfaceDesc ); + m_pd3dDevice->CreateTexture( d3dSurfaceDesc.Width, d3dSurfaceDesc.Height, 0, 0, d3dSurfaceDesc.Format, 0, &m_pLastMovieFrame ); + pTexture->LockRect( 0, &srcRect, NULL, D3DLOCK_READONLY | D3DLOCK_NOOVERWRITE ); + m_pLastMovieFrame->LockRect( 0, &dstRect, NULL, D3DLOCK_READONLY | D3DLOCK_NOOVERWRITE ); + memcpy( dstRect.pBits, srcRect.pBits, srcRect.Pitch*d3dSurfaceDesc.Height ); + } + } + + // Clean up the movie, then return. + m_player.Destroy(); + return FALSE; + } + + // If no texture is ready, return TRUE to indicate that a movie is playing, + // but don't render anything yet. + if ( !pTexture ) + return TRUE; + + const FLOAT fSizeY = 480.0f; + const FLOAT fOriginX = 320.0f - ( fSizeY * .5f * fMovieWidth / fMovieHeight ); + const FLOAT fOriginY = 240.0f - fSizeY * .5f; + + // Draw the texture. + m_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID ); + m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW ); + m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, FALSE ); + + // Draw the texture as a quad. + m_pd3dDevice->SetTexture( 0, pTexture ); + m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 ); + m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); + + // Wrapping isn't allowed on linear textures. + m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP ); + m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP ); + + FLOAT fLeft = fOriginX + 0.5f; + FLOAT fRight = fOriginX + ( fSizeY * fMovieWidth) / fMovieHeight - 0.5f; + FLOAT fTop = fOriginY + 0.5f; + FLOAT fBottom = fOriginY + fSizeY - 0.5f; + + // On linear textures the texture coordinate range is from 0,0 to width,height, instead + // of 0,0 to 1,1. + m_pd3dDevice->SetVertexShader( D3DFVF_XYZRHW|D3DFVF_TEX1 ); + m_pd3dDevice->Begin( D3DPT_QUADLIST ); + m_pd3dDevice->SetVertexData2f( D3DVSDE_TEXCOORD0, 0, fMovieHeight ); + m_pd3dDevice->SetVertexData4f( D3DVSDE_VERTEX, fLeft, fBottom, 0.0f, 1.0f ); + m_pd3dDevice->SetVertexData2f( D3DVSDE_TEXCOORD0, 0, 0 ); + m_pd3dDevice->SetVertexData4f( D3DVSDE_VERTEX, fLeft, fTop, 0.0f, 1.0f ); + m_pd3dDevice->SetVertexData2f( D3DVSDE_TEXCOORD0, fMovieWidth, 0 ); + m_pd3dDevice->SetVertexData4f( D3DVSDE_VERTEX, fRight, fTop, 0.0f, 1.0f ); + m_pd3dDevice->SetVertexData2f( D3DVSDE_TEXCOORD0, fMovieWidth, fMovieHeight ); + m_pd3dDevice->SetVertexData4f( D3DVSDE_VERTEX, fRight, fBottom, 0.0f, 1.0f ); + m_pd3dDevice->End(); + + return TRUE; +} + +//----------------------------------------------------------------------------- +// LaunchHL2 +//----------------------------------------------------------------------------- +void CXBoxLoader::LaunchHL2( unsigned int contextCode ) +{ + LAUNCH_DATA launchData; + RelaunchHeader_t *pRelaunch; + const char *pHL2Name; + + memset( &launchData, 0, sizeof( LAUNCH_DATA ) ); + + // build the relaunch structure that HL2 uses + pRelaunch = GetRelaunchHeader( launchData.Data ); + + pRelaunch->magicNumber = RELAUNCH_MAGIC_NUMBER; + pRelaunch->nBytesRelaunchData = 0; + + if ( ( contextCode & CONTEXTCODE_MAGICMASK ) == CONTEXTCODE_HL2MAGIC ) + { + // ok to re-establish persistent data + pRelaunch->bRetail = (contextCode & CONTEXTCODE_RETAIL_MODE) > 0; + pRelaunch->bInDebugger = (contextCode & CONTEXTCODE_INDEBUGGER) > 0; + } + else + { + // ensure we launch under retail conditions + contextCode = CONTEXTCODE_NO_XBDM; + g_activeDevice = -1; + pRelaunch->bRetail = true; + pRelaunch->bInDebugger = false; + } + + pRelaunch->contextCode = contextCode; + pRelaunch->activeDevice = g_activeDevice; + pRelaunch->startTime = g_loaderStartTime; + + // launch the xbe that is expected + if ( contextCode & CONTEXTCODE_DEBUG_XBE ) + { + // debug xbe + pHL2Name = "D:\\hl2d_xbox.xbe"; + } + else if ( contextCode & CONTEXTCODE_RELEASE_XBE ) + { + // release xbe + pHL2Name = "D:\\hl2r_xbox.xbe"; + } + else + { + // default launch to retail xbe + pHL2Name = "D:\\hl2_xbox.xbe"; + } + + XLaunchNewImage( pHL2Name, &launchData ); + + // failed + FatalMediaError(); +} + +//----------------------------------------------------------------------------- +// Performs initialization +//----------------------------------------------------------------------------- +HRESULT CXBoxLoader::Initialize() +{ + DWORD launchType; + LAUNCH_DATA launchData; + + // no active device until set + g_activeDevice = -1; + + // get launch info and command line params needed for early setting of systems + LPSTR pCmdLine = ""; + DWORD retVal = XGetLaunchInfo( &launchType, &launchData ); + if ( retVal == ERROR_SUCCESS ) + { + if ( launchType == LDT_FROM_DASHBOARD ) + { + // launched from dashboard + LD_FROM_DASHBOARD *pLaunchFromDashboard = (LD_FROM_DASHBOARD *)(&launchData); + m_contextCode = pLaunchFromDashboard->dwContext; + } + else if ( launchType == LDT_TITLE ) + { + // launched directly from HL2 to do something + LAUNCH_DATA* pLaunchData = (LAUNCH_DATA *)(&launchData); + pCmdLine = (char *)pLaunchData->Data; + + RelaunchHeader_t *pHeader = GetRelaunchHeader( pLaunchData->Data ); + if ( pHeader->magicNumber == RELAUNCH_MAGIC_NUMBER ) + { + m_contextCode = pHeader->contextCode; + g_activeDevice = pHeader->activeDevice; + } + } + else if ( launchType == LDT_FROM_DEBUGGER_CMDLINE ) + { + // launched from the debugger + LAUNCH_DATA* pLaunchData = (LAUNCH_DATA *)(&launchData); + pCmdLine = (char *)pLaunchData->Data; + strlwr( pCmdLine ); + + // assume retail mode + m_contextCode |= CONTEXTCODE_HL2MAGIC; + m_contextCode |= CONTEXTCODE_RETAIL_MODE; + + if ( strstr( pCmdLine, "-indebugger" ) ) + { + m_contextCode |= CONTEXTCODE_INDEBUGGER; + } +#ifndef _RETAIL + if ( DmIsDebuggerPresent() ) + { + m_contextCode |= CONTEXTCODE_INDEBUGGER; + } +#endif + if ( strstr( pCmdLine, "-debug" ) ) + { + // launch to debug xbe + m_contextCode |= CONTEXTCODE_DEBUG_XBE; + } + else if ( strstr( pCmdLine, "-release" ) ) + { + // launch to release xbe + m_contextCode |= CONTEXTCODE_RELEASE_XBE; + } + else + { + // default launch to retail xbe + m_contextCode |= CONTEXTCODE_RETAIL_XBE|CONTEXTCODE_NO_XBDM; + if ( strstr( pCmdLine, "-dev" ) ) + { + // force dev link + m_contextCode &= ~CONTEXTCODE_NO_XBDM; + } + } + + if ( strstr( pCmdLine, "-attract" ) ) + { + // running the attract sequence + m_contextCode |= CONTEXTCODE_ATTRACT; + } + } + } + + if ( ( m_contextCode & CONTEXTCODE_MAGICMASK ) != CONTEXTCODE_HL2MAGIC ) + { + // unknown, run the install normally + // 0 is a special indicator, due to lack of valid magic + m_contextCode = 0; + } + else + { + if ( m_contextCode & CONTEXTCODE_DASHBOARD ) + { + // coming from dashboard, back to HL2 - immediately! + LaunchHL2( m_contextCode ); + return S_OK; + } + } + + if ( FAILED( XFONT_OpenDefaultFont( &m_pDefaultTrueTypeFont ) ) ) + { + return XBAPPERR_MEDIANOTFOUND; + } + + // load install resources for context + // Load resource file + if ( FAILED( m_xprResource.Create( "D:\\LoaderMedia\\loader.xpr" ) ) ) + { + return XBAPPERR_MEDIANOTFOUND; + } + + if ( FAILED( LoadFont( &m_Font, loader_Font_OFFSET ) ) ) + { + return XBAPPERR_MEDIANOTFOUND; + } + + if ( !( m_contextCode & CONTEXTCODE_ATTRACT ) ) + { + m_pFooterTexture = LoadTexture( loader_Footer_OFFSET ); + if ( !m_pFooterTexture ) + { + return XBAPPERR_MEDIANOTFOUND; + } + + switch( XGetLanguage() ) + { + case XC_LANGUAGE_FRENCH: + m_pMainLegalTexture = LoadTexture( loader_MainLegal_french_OFFSET ); + break; + case XC_LANGUAGE_ITALIAN: + m_pMainLegalTexture = LoadTexture( loader_MainLegal_italian_OFFSET ); + break; + case XC_LANGUAGE_GERMAN: + m_pMainLegalTexture = LoadTexture( loader_MainLegal_german_OFFSET ); + break; + case XC_LANGUAGE_SPANISH: + m_pMainLegalTexture = LoadTexture( loader_MainLegal_spanish_OFFSET ); + break; + default: + m_pMainLegalTexture = LoadTexture( loader_MainLegal_english_OFFSET ); + break; + } + if ( !m_pMainLegalTexture ) + { + return XBAPPERR_MEDIANOTFOUND; + } + + m_pSourceLegalTexture = LoadTexture( loader_SourceLegal_OFFSET ); + if ( !m_pSourceLegalTexture ) + { + return XBAPPERR_MEDIANOTFOUND; + } + + switch( XGetLanguage() ) + { + case XC_LANGUAGE_FRENCH: + m_pSlideShowTextures[0] = LoadTexture( loader_SlideShow1_french_OFFSET ); + break; + case XC_LANGUAGE_ITALIAN: + m_pSlideShowTextures[0] = LoadTexture( loader_SlideShow1_italian_OFFSET ); + break; + case XC_LANGUAGE_GERMAN: + m_pSlideShowTextures[0] = LoadTexture( loader_SlideShow1_german_OFFSET ); + break; + case XC_LANGUAGE_SPANISH: + m_pSlideShowTextures[0] = LoadTexture( loader_SlideShow1_spanish_OFFSET ); + break; + default: + m_pSlideShowTextures[0] = LoadTexture( loader_SlideShow1_english_OFFSET ); + break; + } + m_pSlideShowTextures[1] = LoadTexture( loader_SlideShow2_OFFSET ); + m_pSlideShowTextures[2] = LoadTexture( loader_SlideShow3_OFFSET ); + m_pSlideShowTextures[3] = LoadTexture( loader_SlideShow4_OFFSET ); + m_pSlideShowTextures[4] = LoadTexture( loader_SlideShow5_OFFSET ); + m_pSlideShowTextures[5] = LoadTexture( loader_SlideShow6_OFFSET ); + m_pSlideShowTextures[6] = LoadTexture( loader_SlideShow7_OFFSET ); + m_pSlideShowTextures[7] = LoadTexture( loader_SlideShow8_OFFSET ); + m_pSlideShowTextures[8] = LoadTexture( loader_SlideShow9_OFFSET ); + for ( int i=0; i<MAX_SLIDESHOW_TEXTURES; i++ ) + { + if ( !m_pSlideShowTextures ) + return XBAPPERR_MEDIANOTFOUND; + } + + if ( !VerifyInstall() ) + { + OUTPUT_DEBUG_STRING( "Install failed!\n" ); + return -1; + } + } + + m_pLoadingIconTexture = LoadTexture( loader_LoadingIcon_OFFSET ); + if ( !m_pLoadingIconTexture ) + { + return XBAPPERR_MEDIANOTFOUND; + } + + return S_OK; +} + +//----------------------------------------------------------------------------- +// Performs per-frame video checks +//----------------------------------------------------------------------------- +void CXBoxLoader::TickVideo() +{ + if ( m_bMovieErrorIsFatal && m_player.IsFailed() ) + { + FatalMediaError(); + } +} + +//----------------------------------------------------------------------------- +// Performs per-frame updates +//----------------------------------------------------------------------------- +HRESULT CXBoxLoader::FrameMove() +{ + bool bFailed = false; + + TickVideo(); + + if ( m_State >= 10 && g_copyStats.m_copyErrors ) + { + FatalMediaError(); + } + + if ( m_State >= 10 && ( m_DefaultGamepad.wPressedButtons & XINPUT_GAMEPAD_START ) ) + { + m_bDrawDebug ^= 1; + } + + if ( m_bAllowAttractAbort && ( m_DefaultGamepad.bPressedAnalogButtons[XINPUT_GAMEPAD_A] || ( m_DefaultGamepad.wPressedButtons & XINPUT_GAMEPAD_START ) ) ) + { + StopVideo(); + + // allow only once, until reset + m_bAllowAttractAbort = false; + } + + switch ( m_State ) + { + case 0: + if ( m_contextCode & CONTEXTCODE_ATTRACT ) + { + // play attract mode + m_State = 1; + } + else + { + // normal installation + m_State = 9; + } + break; + + case 1: + // Play the attract video + if ( FAILED( StartVideo( "D:\\LoaderMedia\\Demo_Attract.xmv", false, false ) ) ) + { + // jump to finish + m_State = 4; + } + else + { + m_State = 2; + } + break; + + case 2: + if ( m_player.IsPlaying() || m_player.IsFailed() ) + { + // attract is playing, wait for finish + m_State = 3; + m_bAllowAttractAbort = true; + } + break; + + case 3: + if ( !m_player.IsPlaying() || m_player.IsFailed() ) + { + // attract is over or aborted + m_State = 4; + } + break; + + case 4: + // place loading + m_bDrawLoading = true; + m_FrameCounter = 0; + m_State = 5; + break; + + case 5: + // wait for two frames to pass to ensure loading is rendered + if ( m_FrameCounter > 2 ) + { + m_bLaunch = true; + m_State = 6; + } + break; + + case 6: + // idle + m_State = 6; + break; + + case 9: + // Play the opening Valve video + StartVideo( "D:\\LoaderMedia\\Valve_Leader.xmv", true, true ); + + // Start the async installation process + if ( !StartInstall() ) + { + OUTPUT_DEBUG_STRING( "Install failed!\n" ); + bFailed = true; + break; + } + m_State = 10; + break; + + case 10: + if ( m_player.IsPlaying() ) + { + // intro is playing, wait for finish + m_State = 15; + } + break; + + case 15: + if ( !m_player.IsPlaying() ) + { + // intro is over + m_State = 20; + } + break; + + case 20: + // start legals + StartLegalScreen( LEGAL_SOURCE ); + m_State = 25; + break; + + case 25: + if ( m_bDrawLegal && GetTickCount() - m_LegalTime > LEGAL_DISPLAY_TIME ) + { + // advance to next legal + StartLegalScreen( LEGAL_MAIN ); + m_State = 30; + } + break; + + case 30: + if ( m_bDrawLegal && GetTickCount() - m_LegalTime > LEGAL_DISPLAY_TIME ) + { + // end of all legals + if ( IsTargetFileValid( "Z:\\LoaderMedia\\Title_Load.xmv", 0 ) ) + { + m_bDrawLegal = false; + m_State = 40; + } + } + break; + + case 40: + // play the gordon/alyx hl2 game movie + m_bCaptureLastMovieFrame = true; + StartVideo( "Z:\\LoaderMedia\\Title_Load.xmv", true, true ); + m_State = 50; + break; + + case 50: + if ( m_player.IsPlaying() ) + { + // title movie is playing, wait for finish + m_State = 60; + } + break; + + case 60: + if ( m_player.GetElapsedTime() >= 8000 && !m_bDrawProgress ) + { + // wait for known audio click, then put up progress bar + // start the loading bar animation + m_bDrawProgress = true; + } + + if ( m_bInstallComplete && m_bDrawProgress ) + { + // install has completed + if ( m_player.IsPlaying() ) + { + // force the movie to end + m_player.TerminatePlayback(); + } + m_State = 70; + } + else if ( !m_bInstallComplete && !m_player.IsPlaying() ) + { + // intro movie has finished, but install is still running + // start up slideshow cycler + m_bDrawSlideShow = true; + m_State = 70; + } + break; + + case 70: + // wait for movie or slideshow to stop + if ( !m_player.IsPlaying() && !m_bDrawSlideShow ) + { + m_bLaunch = true; + m_State = 80; + } + break; + + case 80: + // idle + break; + } + + + if ( bFailed ) + { + FatalMediaError(); + } + + return S_OK; +} + +//----------------------------------------------------------------------------- +// Renders the scene +//----------------------------------------------------------------------------- +HRESULT CXBoxLoader::Render() +{ + m_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL, 0x00000000, 1.0f, 0L ); + + // Play a frame from a video. + BOOL bPlayedFrame = PlayVideoFrame(); + + // hold the last frame of the title movie + if ( m_State >= 60 && !bPlayedFrame ) + { + DrawTexture( m_pLastMovieFrame, 0, 0, 640, 480, 0xFFFFFFFF ); + } + + DrawSlideshow(); + DrawLoadingMarquee(); + DrawProgressBar(); + DrawLegals(); + DrawDebug(); + DrawLog(); + + if ( m_bLaunch ) + { + // The installation has finished + // Persist the image before launching hl2 + m_pd3dDevice->Present( NULL, NULL, NULL, NULL ); + m_pd3dDevice->PersistDisplay(); + + // Make sure the installation thread has completely exited + if ( m_installThread ) + { + WaitForSingleObject( m_installThread, INFINITE ); + CloseHandle( m_installThread ); + } + + LaunchHL2( m_contextCode ); + return S_OK; + } + + // Present the scene + m_pd3dDevice->Present( NULL, NULL, NULL, NULL ); + + m_FrameCounter++; + + return S_OK; +} + +//----------------------------------------------------------------------------- +// Entry point to the program. +//----------------------------------------------------------------------------- +VOID __cdecl main() +{ + CXBoxLoader xbApp; + if ( FAILED( xbApp.Create() ) ) + { + xbApp.FatalMediaError(); + } + xbApp.Run(); +} + diff --git a/utils/xbox/xbox_loader/xbox_loader.h b/utils/xbox/xbox_loader/xbox_loader.h new file mode 100644 index 0000000..b37b2d5 --- /dev/null +++ b/utils/xbox/xbox_loader/xbox_loader.h @@ -0,0 +1,171 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// XBOX_LOADER.H +// +// Master Include +//=====================================================================================// + +#pragma once + +#include <xtl.h> +#include <XBApp.h> +#include <XBFont.h> +#include <XBHelp.h> +#include <xgraphics.h> +#include <xfont.h> +#include <xmv.h> +#include <xbdm.h> +#include <math.h> +#include "XBResource.h" +#include "xmvhelper.h" +#include "toollib.h" +#include "scriplib.h" +#include "loader.h" +#include "jcalg1.h" +#include "xbox/xbox_launch.h" + +#define XBOX_FORENSIC_LOG + +#define SCREEN_WIDTH 640 +#define SCREEN_HEIGHT 480 +#define MAX_FILES 500 +#define MAX_SLIDESHOW_TEXTURES 9 + +#define LEGAL_DISPLAY_TIME 6000 +#define LOADINGBAR_UPTIME 500.0f // slid up or down +#define LOADINGBAR_SLIDETIME 1500.0f // progress speed +#define LOADINGBAR_WAITTIME 500.0f // delay after up to begin slide +#define SLIDESHOW_SLIDETIME 7000 +#define SLIDESHOW_FLIPTIME 1000 + +#define LEGAL_MAIN 0 +#define LEGAL_SOURCE 1 + +#define FOOTER_W 512 + +#define SEGMENT_W 10 +#define SEGMENT_GAP 1 +#define SEGMENT_COUNT 26 + +#define PROGRESS_Y 405 +#define PROGRESS_W (SEGMENT_COUNT*(SEGMENT_W+SEGMENT_GAP)) +#define PROGRESS_H 15 +#define PROGRESS_X 124 + +#define PROGRESS_FOOTER_COLOR 0x88FFFFFF +#define PROGRESS_INSET_COLOR 0xFF222222 +#define PROGRESS_SEGMENT_COLOR 0xFFCC6C00 +#define PROGRESS_TEXT_COLOR 0xFFFFFFFF + +//----------------------------------------------------------------------------- +// Main class to run this application. Most functionality is inherited +// from the CXBApplication base class. +//----------------------------------------------------------------------------- +class CXBoxLoader : public CXBApplication +{ +public: + CXBoxLoader(); + + virtual HRESULT Initialize( void ); + virtual HRESULT Render( void ); + virtual HRESULT FrameMove( void ); + + void DrawRect( int x, int y, int w, int h, DWORD color ); + void DrawLegals(); + void DrawDebug(); + BOOL PlayVideoFrame(); + HRESULT StartVideo( const CHAR* strFilename, bool bFromMemory, bool bFatalOnError ); + void StopVideo(); + bool StartInstall( void ); + bool LoadInstallScript( void ); + D3DTexture *LoadTexture( int resourceID ); + HRESULT LoadFont( CXBFont *pFont, int resourceID ); + void DrawTexture( D3DTexture *pD3DTexture, int x, int y, int w, int h, int color ); + void StartLegalScreen( int legal ); + void DrawProgressBar(); + void DrawLoadingMarquee(); + void DrawSlideshow(); + bool VerifyInstall(); + void StartDashboard( bool bGotoMemory ); + void LoadLogFile(); + void DrawLog(); + void FatalMediaError(); + void LaunchHL2( unsigned int contextCode ); + void TickVideo(); + +private: + IDirect3DTexture8 *m_pLastMovieFrame; + D3DTexture *m_pFooterTexture; + D3DTexture *m_pLoadingIconTexture; + D3DTexture *m_pMainLegalTexture; + D3DTexture *m_pSourceLegalTexture; + D3DTexture *m_pLegalTexture; + D3DTexture *m_pSlideShowTextures[MAX_SLIDESHOW_TEXTURES]; + + CXMVPlayer m_player; + + D3DVertexBuffer *m_pVB; + CXBPackedResource m_xprResource; + + CXBFont m_Font; + + int m_contextCode; + + char *m_fileSrc[MAX_FILES]; + char *m_fileDest[MAX_FILES]; + xCompressHeader *m_fileCompressionHeaders[MAX_FILES]; + DWORD m_fileDestSizes[MAX_FILES]; + int m_numFiles; + + bool m_bAllowAttractAbort; + bool m_bDrawLoading; + bool m_bDrawProgress; + bool m_bDrawDebug; + bool m_bLaunch; + DWORD m_dwLoading; + bool m_bDrawLegal; + HANDLE m_installThread; + DWORD m_LegalTime; + int m_State; + DWORD m_LoadingBarStartTime; + DWORD m_LoadingBarEndTime; + DWORD m_LegalStartTime; + bool m_bInstallComplete; + int m_Version; + int m_FrameCounter; + int m_MovieCount; + bool m_bMovieErrorIsFatal; + bool m_bCaptureLastMovieFrame; + DWORD m_SlideShowStartTime; + bool m_bDrawSlideShow; + int m_SlideShowCount; + bool m_bFinalSlide; + char *m_pLogData; + XFONT* m_pDefaultTrueTypeFont; +}; + +struct CopyStats_t +{ + char m_srcFilename[MAX_PATH]; + char m_dstFilename[MAX_PATH]; + DWORD m_readSize; + DWORD m_writeSize; + DWORD m_bytesCopied; + DWORD m_totalReadTime; + DWORD m_totalWriteTime; + DWORD m_totalReadSize; + DWORD m_totalWriteSize; + DWORD m_bufferReadSize; + DWORD m_bufferWriteSize; + DWORD m_bufferReadTime; + DWORD m_bufferWriteTime; + DWORD m_inflateSize; + DWORD m_inflateTime; + DWORD m_copyTime; + DWORD m_copyErrors; + DWORD m_numReadBuffers; + DWORD m_numWriteBuffers; +}; + +extern bool CopyFileOverlapped( const char *pSrc, const char *pDest, xCompressHeader *pxcHeader, CopyStats_t *pCopyStats ); +extern bool CreateFilePath( const char *inPath ); diff --git a/utils/xbox/xbox_loader/xbox_loader.sln b/utils/xbox/xbox_loader/xbox_loader.sln new file mode 100644 index 0000000..e702a04 --- /dev/null +++ b/utils/xbox/xbox_loader/xbox_loader.sln @@ -0,0 +1,24 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xbox_loader", "xbox_loader.vcproj", "{652C7D60-BC02-4E09-96DD-9300FFFF3403}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug_XBox = Debug_XBox + Release_XBox = Release_XBox + Retail_XBox = Retail_XBox + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {652C7D60-BC02-4E09-96DD-9300FFFF3403}.Debug_XBox.ActiveCfg = Debug_XBox|Xbox + {652C7D60-BC02-4E09-96DD-9300FFFF3403}.Debug_XBox.Build.0 = Debug_XBox|Xbox + {652C7D60-BC02-4E09-96DD-9300FFFF3403}.Release_XBox.ActiveCfg = Release_XBox|Xbox + {652C7D60-BC02-4E09-96DD-9300FFFF3403}.Release_XBox.Build.0 = Release_XBox|Xbox + {652C7D60-BC02-4E09-96DD-9300FFFF3403}.Retail_XBox.ActiveCfg = Retail_XBox|Xbox + {652C7D60-BC02-4E09-96DD-9300FFFF3403}.Retail_XBox.Build.0 = Retail_XBox|Xbox + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/utils/xbox/xbox_loader/xbox_loader.vcproj b/utils/xbox/xbox_loader/xbox_loader.vcproj new file mode 100644 index 0000000..f4a3fef --- /dev/null +++ b/utils/xbox/xbox_loader/xbox_loader.vcproj @@ -0,0 +1,366 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="7.10" + Name="xbox_loader" + ProjectGUID="{652C7D60-BC02-4E09-96DD-9300FFFF3403}" + RootNamespace="xbox_loader" + SccProjectName="" + SccLocalPath=""> + <Platforms> + <Platform + Name="Xbox"/> + </Platforms> + <Configurations> + <Configuration + Name="Debug_XBox|Xbox" + OutputDirectory=".\Debug_XBox" + IntermediateDirectory=".\Debug_XBox" + ConfigurationType="1" + UseOfMFC="0" + ATLMinimizesCRunTimeLibraryUsage="FALSE"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + OptimizeForProcessor="2" + AdditionalIncludeDirectories="..\..\..\public;..\..\..\common;..\Common\include;..\toollib" + PreprocessorDefinitions="_USE_XGMATH,_XBOX,XBOX_SAMPLE,_DEBUG," + ExceptionHandling="FALSE" + RuntimeLibrary="1" + EnableEnhancedInstructionSet="1" + UsePrecompiledHeader="0" + PrecompiledHeaderThrough="xtl.h" + PrecompiledHeaderFile="$(IntDir)/apploader.pch" + AssemblerListingLocation="$(IntDir)/" + ObjectFile="$(IntDir)/" + ProgramDataBaseFileName="$(IntDir)/" + WarningLevel="3" + SuppressStartupBanner="TRUE" + DebugInformationFormat="4" + CompileAs="0" + DisableSpecificWarnings="4244"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLinkerTool" + AdditionalOptions="/MACHINE:I386 /FIXED:NO" + AdditionalDependencies="xperf.lib xbdm.lib xapilibd.lib d3d8d.lib d3dx8d.lib xmv.lib xgraphicsd.lib dsoundd.lib xboxkrnl.lib" + OutputFile="$(OutDir)/xbox_loader.exe" + LinkIncremental="2" + SuppressStartupBanner="TRUE" + GenerateDebugInformation="TRUE" + ProgramDatabaseFile="$(OutDir)/xbox_loader.pdb" + GenerateMapFile="TRUE"/> + <Tool + Name="VCPostBuildEventTool" + Description="Copying LoaderMedia to \\Fileserver" + CommandLine="copy LoaderMedia\*.* \\FileServer\user\XBox\DVD\LoaderMedia + +"/> + <Tool + Name="VCPreBuildEventTool" + CommandLine="bundler LoaderMedia_Source\loader_icon.rdf -o LoaderMedia_Source\loader_icon.xbx"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="XboxDeploymentTool" + RemotePath="xe:\hl2\default.xbe" + AdditionalFiles=""/> + <Tool + Name="XboxImageTool" + StackSize="0x10000" + IncludeDebugInfo="TRUE" + LimitAvailableMemoryTo64MB="TRUE" + SuppressStartupBanner="TRUE" + NoLibWarn="TRUE" + TitleID="0x45410091" + TitleName="Half - Life 2 (Debug Build)" + TitleImage="LoaderMedia_Source\loader_icon.xbx" + XBEVersion="4096"/> + </Configuration> + <Configuration + Name="Release_XBox|Xbox" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + UseOfMFC="0" + ATLMinimizesCRunTimeLibraryUsage="FALSE"> + <Tool + Name="VCCLCompilerTool" + OmitFramePointers="TRUE" + OptimizeForProcessor="2" + AdditionalIncludeDirectories="..\..\..\public;..\..\..\common;..\Common\include;..\toollib" + PreprocessorDefinitions="_USE_XGMATH;_XBOX;NDEBUG" + StringPooling="TRUE" + ExceptionHandling="FALSE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="TRUE" + EnableEnhancedInstructionSet="1" + UsePrecompiledHeader="0" + PrecompiledHeaderThrough="" + PrecompiledHeaderFile="" + AssemblerListingLocation="$(IntDir)/" + ObjectFile="$(IntDir)/" + ProgramDataBaseFileName="$(IntDir)/" + WarningLevel="3" + SuppressStartupBanner="TRUE" + DisableSpecificWarnings="4244"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLinkerTool" + AdditionalOptions="/MACHINE:I386 /FIXED:NO" + AdditionalDependencies="xapilib.lib d3d8.lib d3dx8.lib xgraphics.lib dsound.lib xmv.lib xboxkrnl.lib" + OutputFile="$(OutDir)/xbox_loader.exe" + SuppressStartupBanner="TRUE" + GenerateDebugInformation="TRUE" + ProgramDatabaseFile="$(OutDir)/xbox_loader.pdb" + GenerateMapFile="TRUE" + MapExports="TRUE" + MapLines="TRUE" + OptimizeReferences="2" + EnableCOMDATFolding="2" + SetChecksum="TRUE"/> + <Tool + Name="VCPostBuildEventTool" + Description="Copying LoaderMedia to \\Fileserver" + CommandLine="copy LoaderMedia\*.* \\FileServer\user\XBox\DVD\LoaderMedia +copy $(TargetDir)$(TargetName).xbe \\FileServer\user\XBox\DVD\default.xbe +"/> + <Tool + Name="VCPreBuildEventTool" + CommandLine="bundler LoaderMedia_Source\loader_icon.rdf -o LoaderMedia_Source\loader_icon.xbx"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="XboxDeploymentTool" + RemotePath="xe:\hl2\default.xbe" + AdditionalFiles=""/> + <Tool + Name="XboxImageTool" + StackSize="0x10000" + LimitAvailableMemoryTo64MB="TRUE" + SuppressStartupBanner="TRUE" + TitleID="0x45410091" + TitleName="Half - Life 2 (Release Build)" + TitleImage="LoaderMedia_Source\loader_icon.xbx" + XBEVersion="4096"/> + </Configuration> + <Configuration + Name="Retail_XBox|Xbox" + OutputDirectory="$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + UseOfMFC="0" + ATLMinimizesCRunTimeLibraryUsage="FALSE"> + <Tool + Name="VCCLCompilerTool" + OmitFramePointers="TRUE" + OptimizeForProcessor="2" + AdditionalIncludeDirectories="..\..\..\public;..\..\..\common;..\Common\include;..\toollib" + PreprocessorDefinitions="_USE_XGMATH;_XBOX;NDEBUG;_RETAIL" + StringPooling="TRUE" + ExceptionHandling="FALSE" + RuntimeLibrary="0" + EnableFunctionLevelLinking="TRUE" + EnableEnhancedInstructionSet="1" + UsePrecompiledHeader="0" + PrecompiledHeaderThrough="xtl.h" + PrecompiledHeaderFile="$(IntDir)/apploader.pch" + AssemblerListingLocation="$(IntDir)/" + ObjectFile="$(IntDir)/" + ProgramDataBaseFileName="$(IntDir)/" + WarningLevel="3" + SuppressStartupBanner="TRUE" + DisableSpecificWarnings="4244"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLinkerTool" + AdditionalOptions="/MACHINE:I386 /FIXED:NO" + AdditionalDependencies="xapilib.lib d3d8.lib d3dx8.lib xgraphics.lib dsound.lib xmv.lib xboxkrnl.lib" + OutputFile="$(OutDir)/xbox_loader.exe" + SuppressStartupBanner="TRUE" + GenerateDebugInformation="TRUE" + ProgramDatabaseFile="$(OutDir)/xbox_loader.pdb" + GenerateMapFile="TRUE" + MapExports="TRUE" + MapLines="TRUE" + OptimizeReferences="2" + EnableCOMDATFolding="2" + SetChecksum="TRUE"/> + <Tool + Name="VCPostBuildEventTool" + Description="Copying LoaderMedia to \\Fileserver" + CommandLine="copy LoaderMedia\*.* \\FileServer\user\XBox\DVD\LoaderMedia +copy $(TargetDir)$(TargetName).xbe \\FileServer\user\XBox\DVD\default.xbe +"/> + <Tool + Name="VCPreBuildEventTool" + CommandLine="bundler LoaderMedia_Source\loader_icon.rdf -o LoaderMedia_Source\loader_icon.xbx"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="XboxDeploymentTool" + RemotePath="xe:\hl2\default.xbe" + AdditionalFiles=""/> + <Tool + Name="XboxImageTool" + StackSize="0x10000" + LimitAvailableMemoryTo64MB="TRUE" + SuppressStartupBanner="TRUE" + TitleID="0x45410091" + TitleName="Half - Life 2" + TitleImage="LoaderMedia_Source\loader_icon.xbx" + XBEVersion="4096"/> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm"> + <File + RelativePath=".\xbox_fileCopy.cpp"> + </File> + <File + RelativePath=".\xbox_loader.cpp"> + </File> + <File + RelativePath=".\xmvhelper.cpp"> + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc"> + <File + RelativePath=".\font.h"> + </File> + <File + RelativePath="..\..\..\public\jcalg1.h"> + </File> + <File + RelativePath=".\loader.h"> + </File> + <File + RelativePath="..\..\..\common\xbox\xbox_launch.h"> + </File> + <File + RelativePath=".\xbox_loader.h"> + </File> + <File + RelativePath=".\xmvhelper.h"> + </File> + </Filter> + <Filter + Name="Common Files" + Filter=""> + <Filter + Name="Source Files" + Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm"> + <File + RelativePath="..\toollib\scriplib.cpp"> + </File> + <File + RelativePath="..\toollib\scriplib.h"> + </File> + <File + RelativePath="..\toollib\toollib.cpp"> + </File> + <File + RelativePath="..\toollib\toollib.h"> + </File> + <File + RelativePath="..\Common\src\XBApp.cpp"> + </File> + <File + RelativePath="..\Common\src\XBFont.cpp"> + </File> + <File + RelativePath="..\Common\src\XBInput.cpp"> + </File> + <File + RelativePath="..\Common\src\XBMesh.cpp"> + </File> + <File + RelativePath="..\Common\src\XBResource.cpp"> + </File> + <File + RelativePath="..\Common\src\XBUtil.cpp"> + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc"> + <File + RelativePath="..\Common\include\XBApp.h"> + </File> + <File + RelativePath="..\Common\include\XBFont.h"> + </File> + <File + RelativePath="..\Common\include\XBHelp.h"> + </File> + <File + RelativePath="..\Common\include\XBInput.h"> + </File> + <File + RelativePath="..\Common\include\XBMesh.h"> + </File> + <File + RelativePath="..\Common\include\XBProfiling.h"> + </File> + <File + RelativePath="..\Common\include\XBResource.h"> + </File> + <File + RelativePath="..\Common\include\XBUtil.h"> + </File> + </Filter> + </Filter> + <Filter + Name="Resources" + Filter="*.rdf"> + <File + RelativePath=".\loader.rdf"> + <FileConfiguration + Name="Debug_XBox|Xbox"> + <Tool + Name="VCCustomBuildTool" + Description="bundler "$(InputPath)"" + CommandLine="bundler "$(InputPath)"" + Outputs="$(ProjectDir)Media\$(InputName).xpr;$(ProjectDir)$(InputName).h"/> + </FileConfiguration> + <FileConfiguration + Name="Release_XBox|Xbox"> + <Tool + Name="VCCustomBuildTool" + Description="bundler "$(InputPath)"" + CommandLine="bundler "$(InputPath)"" + Outputs="$(ProjectDir)Media\$(InputName).xpr;$(ProjectDir)$(InputName).h"/> + </FileConfiguration> + <FileConfiguration + Name="Retail_XBox|Xbox"> + <Tool + Name="VCCustomBuildTool" + Description="bundler "$(InputPath)"" + CommandLine="bundler "$(InputPath)"" + Outputs="$(ProjectDir)Media\$(InputName).xpr;$(ProjectDir)$(InputName).h"/> + </FileConfiguration> + </File> + <File + RelativePath=".\LoaderMedia_Source\loader_icon.rdf"> + </File> + </Filter> + <Filter + Name="Libraries" + Filter=""> + <File + RelativePath="..\..\..\lib\public\jcalg1_static.lib"> + </File> + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/utils/xbox/xbox_loader/xmvhelper.cpp b/utils/xbox/xbox_loader/xmvhelper.cpp new file mode 100644 index 0000000..74060de --- /dev/null +++ b/utils/xbox/xbox_loader/xmvhelper.cpp @@ -0,0 +1,788 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +//----------------------------------------------------------------------------- +// File: WMVPlayer.cpp +// +// Desc: This helper class provides simple WMV decoding and playback +// functionality. It will be expanded as new playback methods are +// exposed +// +// Hist: 2.7.03 - Created, based on work by Jeff Sullivan +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//----------------------------------------------------------------------------- + +#include "xbox_loader.h" +#include <xtl.h> +#include "XMVHelper.h" +#include "XBUtil.h" +#include <stdio.h> + + +// Funtion Prototypes for packet loading functions for loading from a file. +HRESULT CALLBACK GetNextPacket( DWORD dwContext, + void **ppPacket, + DWORD* pOffsetToNextPacket ); + +HRESULT CALLBACK ReleasePreviousPacket( DWORD dwContext, + LONGLONG llNextReadByteOffset, + DWORD dwNextPacketSize ); + +// Funtion Prototypes for packet loading functions for loading from a block of memory. +HRESULT CALLBACK GetNextMemoryPacket( DWORD dwContext, + void **ppPacket, + DWORD* pOffsetToNextPacket ); + +HRESULT CALLBACK ReleasePreviousMemoryPacket( DWORD dwContext, + LONGLONG llNextReadByteOffset, + DWORD dwNextPacketSize ); + + + + +//----------------------------------------------------------------------------- +// Name: CXMVPlayer() +// Desc: Constructor for CXMVPlayer +//----------------------------------------------------------------------------- +CXMVPlayer::CXMVPlayer() +{ + m_pXMVDecoder = NULL; + ZeroMemory( &m_VideoDesc, sizeof( m_VideoDesc ) ); + ZeroMemory( &m_AudioDesc, sizeof( m_AudioDesc ) ); + for ( UINT i=0; i<XMVPLAYER_NUMTEXTURES; i++ ) + { + m_pTextures[i] = NULL; + } + + m_dwCurrentFrame = -1; // Will be zero after we decode the first frame. + + m_bPlaying = FALSE; + m_bOverlaysEnabled = FALSE; + + m_loadContext.hFile = INVALID_HANDLE_VALUE; + m_loadContext.pInputBuffer = 0; + m_physicalBuffer = 0; + m_bError = FALSE; +} + + + + +//----------------------------------------------------------------------------- +// Name: ~CXMVPlayer() +// Desc: Destructor for CXMVPlayer +//----------------------------------------------------------------------------- +CXMVPlayer::~CXMVPlayer() +{ + Destroy(); +} + + + + +//----------------------------------------------------------------------------- +// Name: Destroy() +// Desc: Free all resources and clear are resource pointers and handles. +//----------------------------------------------------------------------------- +HRESULT CXMVPlayer::Destroy() +{ + // Disable overlays if we were using them. + if ( m_bOverlaysEnabled ) + { + m_pDevice->EnableOverlay( FALSE ); + m_bOverlaysEnabled = FALSE; + } + + // Free the XMV decoder. + if ( NULL != m_pXMVDecoder ) + { + m_pXMVDecoder->CloseDecoder(); + m_pXMVDecoder = NULL; + } + + ZeroMemory( &m_VideoDesc, sizeof( m_VideoDesc ) ); + ZeroMemory( &m_AudioDesc, sizeof( m_AudioDesc ) ); + + // Release our textures. + for ( UINT i=0; i<XMVPLAYER_NUMTEXTURES; i++ ) + { + if ( m_pTextures[i] ) + m_pTextures[i]->Release(); + m_pTextures[i] = 0; + } + + m_dwCurrentFrame = -1; + m_dwStartTime = 0; + + m_bPlaying = FALSE; + + // Release any file handles we were using. + if( INVALID_HANDLE_VALUE != m_loadContext.hFile ) + { + CloseHandle( m_loadContext.hFile ); + m_loadContext.hFile = INVALID_HANDLE_VALUE; + } + + // Free up memory used for playing a movie from memory. + if ( m_loadContext.pInputBuffer ) + { + free( m_loadContext.pInputBuffer ); + m_loadContext.pInputBuffer = 0; + } + + // Be sure to release the physical memory last! + if( m_physicalBuffer ) + { + XPhysicalFree( m_physicalBuffer ); + m_physicalBuffer = 0; + } + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: FinishOpeningFile() +// Desc: Helper function for the three Open functions. Enables the audio streams, +// initializes the video descriptor, and allocates textures if needed. +//----------------------------------------------------------------------------- +HRESULT CXMVPlayer::FinishOpeningFile( D3DFORMAT format, LPDIRECT3DDEVICE8 pDevice, BOOL bAllocateTextures ) +{ + assert( format == D3DFMT_YUY2 || format == D3DFMT_LIN_A8R8G8B8 ); + assert( XMVPLAYER_NUMTEXTURES >= 2); + + HRESULT hr = S_OK; + + m_pXMVDecoder->GetVideoDescriptor( &m_VideoDesc ); + + // Enable the audio streams + for ( unsigned i=0; i < m_VideoDesc.AudioStreamCount; i++ ) + { + m_pXMVDecoder->GetAudioDescriptor( i, &m_AudioDesc ); + hr = m_pXMVDecoder->EnableAudioStream( i, 0, NULL, NULL); + if ( FAILED( hr ) ) + { + XBUtil_DebugPrint( "Unable to enable audio stream 0 (error %x)\n", hr ); + Destroy(); + return hr; + } + } + + for ( int i = 0; i < XMVPLAYER_NUMTEXTURES; i++ ) + { + m_pTextures[i] = 0; + if ( bAllocateTextures ) + { + hr = pDevice->CreateTexture( m_VideoDesc.Width, m_VideoDesc.Height, 1, 0, format, 0, &m_pTextures[i] ); + if ( FAILED( hr ) ) + { + XBUtil_DebugPrint( "Unable to create texture %d (error %x)\n", i, hr ); + Destroy(); + return hr; + } + } + } + + // Initialize what texture we are decoding to, if decoding for texture mapping. + m_nDecodeTextureIndex = 0; + + // Initialize the various texture pointers for use when decoding for overlays. + pShowingTexture = m_pTextures[0]; + pDecodingTexture = m_pTextures[1]; + pSubmittedTexture = 0; + + m_bPlaying = TRUE; + m_dwStartTime = GetTickCount(); + + return hr; +} + + + + +//----------------------------------------------------------------------------- +// Name: OpenFile() +// Desc: Create an XMV decoder object that reads from a file. +//----------------------------------------------------------------------------- +HRESULT CXMVPlayer::OpenFile( const CHAR* lpFilename, D3DFORMAT format, LPDIRECT3DDEVICE8 pDevice, BOOL bAllocateTextures ) +{ + HRESULT hr = S_OK; + + m_bError = FALSE; + + if ( NULL == lpFilename || NULL == pDevice ) + { + XBUtil_DebugPrint( "Bad parameter to OpenFile()\n" ); + m_bError = TRUE; + return E_FAIL; + } + + hr = XMVDecoder_CreateDecoderForFile( XMVFLAG_SYNC_ON_NEXT_VBLANK, ( CHAR* )lpFilename, &m_pXMVDecoder ); + if ( FAILED( hr ) ) + { + XBUtil_DebugPrint( "Unable to create XMV Decoder for %s (error: %x)\n", lpFilename, hr ); + m_bError = TRUE; + return hr; + } + + hr = FinishOpeningFile( format, pDevice, bAllocateTextures ); + + if ( FAILED( hr ) ) + { + m_bError = TRUE; + } + + return hr; +} + + + + +//----------------------------------------------------------------------------- +// Name: OpenFileForPackets() +// Desc: Create an XMV decoder object that uses the packet reading interface. +// Currently this just reads from a file, but it can be altered to read from +// custom formats, start partway through a file, etc. +//----------------------------------------------------------------------------- +HRESULT CXMVPlayer::OpenFileForPackets( const CHAR* lpFilename, D3DFORMAT format, LPDIRECT3DDEVICE8 pDevice, BOOL bAllocateTextures ) +{ + HRESULT hr = S_OK; + + // We need to read in the first 4K of data for the XMV player to initialize + // itself from. This is most conveniently read as an array of DWORDS. + DWORD first4Kbytes[4096 / sizeof( DWORD )]; + + // Clear entire context struct to zero + ZeroMemory( &m_loadContext, sizeof( m_loadContext ) ); + + // Open the input file. + m_loadContext.hFile = CreateFile( lpFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, + NULL ); + + if( m_loadContext.hFile == INVALID_HANDLE_VALUE ) + { + Destroy(); + return E_INVALIDARG; + } + + // Read the first page from the file. We opened it for + // overlapped IO so we do a pair of reads. + m_loadContext.Overlapped.Offset = 0; + m_loadContext.Overlapped.OffsetHigh = 0; + + // Start the read. + if( 0 == ReadFile( m_loadContext.hFile, first4Kbytes, sizeof( first4Kbytes ), NULL, &m_loadContext.Overlapped ) ) + { + if( GetLastError() != ERROR_IO_PENDING ) + { + Destroy(); + return E_FAIL; + } + } + + // Wait for the read to finish. + DWORD dwBytesRead; + if( !GetOverlappedResult( m_loadContext.hFile, &m_loadContext.Overlapped, &dwBytesRead, TRUE ) ) + { + Destroy(); + return E_FAIL; + } + + // Check size to make sure input is a valid XMV file. + if( dwBytesRead != 4096 ) + { + Destroy(); + return E_FAIL; + } + + // Create an XMV decoder + hr = XMVDecoder_CreateDecoderForPackets( XMVFLAG_SYNC_ON_NEXT_VBLANK, first4Kbytes, ( DWORD )&m_loadContext, + GetNextPacket, ReleasePreviousPacket, &m_pXMVDecoder ); + if( FAILED( hr ) ) + { + Destroy(); + return E_FAIL; + } + + // The size of the first packet and the minimum size of the two packet buffers are stored in the + // second and third DWORDS of the file. From xmv.h: + // * DWORD NextPacketSize // The size of the next packet + // * DWORD ThisPacketSize // The size of this packet + // * DWORD MaxPacketSize // The size of the largest packet in the file + DWORD dwThisPacketSize = first4Kbytes[1]; + DWORD dwRequiredPacketSize = first4Kbytes[2]; + + // Check for illegal parameters. + if( dwThisPacketSize > dwRequiredPacketSize ) + { + Destroy(); + return E_FAIL; + } + + // XPhysicalAlloc is used so that 5.1 or compressed audio streams can be played. + m_physicalBuffer = ( BYTE* )XPhysicalAlloc( dwRequiredPacketSize * 2, MAXULONG_PTR, 0, PAGE_READWRITE ); + + // Save our information. + m_loadContext.dwPacketSize = dwRequiredPacketSize; + m_loadContext.pLoadingPacket = m_physicalBuffer; + m_loadContext.pDecodingPacket = m_physicalBuffer + dwRequiredPacketSize; + + // Read the first packet. We wind up re-reading the first 4096 + // bytes but it makes the logic for figuring out how much we read + // a little bit easier... + m_loadContext.Overlapped.Offset = 0; + m_loadContext.Overlapped.OffsetHigh = 0; + + if( 0 == ReadFile( m_loadContext.hFile, m_physicalBuffer, dwThisPacketSize, NULL, + &m_loadContext.Overlapped ) ) + { + if( GetLastError() != ERROR_IO_PENDING ) + { + Destroy(); + return E_FAIL; + } + } + + // Note - at this point the preceding read has *not* necessarily completed. + // Don't try reading anything from that buffer until GetNextPacket has been + // successfully called. + + hr = FinishOpeningFile( format, pDevice, bAllocateTextures ); + + return hr; +} + + + + +//----------------------------------------------------------------------------- +// Name: OpenMovieFromMemory() +// Desc: Create an XMV decoder object that uses the packet reading interface to +// read from a block of memory. To simplify the memory management this function +// also allocates this block of memory and initializes it from a file. +//----------------------------------------------------------------------------- +HRESULT CXMVPlayer::OpenMovieFromMemory( const CHAR* lpFilename, D3DFORMAT format, LPDIRECT3DDEVICE8 pDevice, BOOL bAllocateTextures ) +{ + HRESULT hr = S_OK; + + m_bError = FALSE; + + // Read the entire file into memory. + void* data; + hr = XBUtil_LoadFile( lpFilename, &data, &m_loadContext.inputSize ); + if ( FAILED( hr ) ) + { + m_bError = TRUE; + return hr; + } + + m_loadContext.pInputBuffer = ( BYTE* )data; + + // Check size to make sure input is a valid XMV file. + if( m_loadContext.inputSize < 4096 ) + { + Destroy(); + m_bError = TRUE; + return E_FAIL; + } + + // Get a DWORD pointer to the first 4K - needed by CreateDecoderForPackets + DWORD* first4Kbytes = ( DWORD* )data; + + // Create an XMV decoder + hr = XMVDecoder_CreateDecoderForPackets( XMVFLAG_SYNC_ON_NEXT_VBLANK, first4Kbytes, ( DWORD )&m_loadContext, GetNextMemoryPacket, + ReleasePreviousMemoryPacket, &m_pXMVDecoder ); + if ( FAILED( hr ) ) + { + Destroy(); + m_bError = TRUE; + return E_FAIL; + } + + // The size of the first packet and the minimum size of the two packet buffers are stored in the + // second and third DWORDS of the file. From xmv.h: + // * DWORD NextPacketSize // The size of the next packet + // * DWORD ThisPacketSize // The size of this packet + // * DWORD MaxPacketSize // The size of the largest packet in the file + DWORD dwThisPacketSize = first4Kbytes[1]; + DWORD dwRequiredPacketSize = first4Kbytes[2]; + + // Check for illegal parameters. + if( dwThisPacketSize > dwRequiredPacketSize ) + { + Destroy(); + m_bError = TRUE; + return E_FAIL; + } + + // XPhysicalAlloc is used so that 5.1 or compressed audio streams can be played. + m_physicalBuffer = ( BYTE* )XPhysicalAlloc( dwRequiredPacketSize * 2, MAXULONG_PTR, 0, PAGE_READWRITE ); + + // Save our information for the callback functions. + // The size of our two memory blocks. + m_loadContext.dwPacketSize = dwRequiredPacketSize; + // The addresses of our two memory blocks. + m_loadContext.pLoadingPacket = m_physicalBuffer; + m_loadContext.pDecodingPacket = m_physicalBuffer + dwRequiredPacketSize; + + // Information about the block of memory the movie is stored in. + m_loadContext.pInputBuffer = ( BYTE* )data; + m_loadContext.inputSize = m_loadContext.inputSize; + m_loadContext.readOffset = 0; + m_loadContext.currentPacketSize = dwThisPacketSize; + + hr = FinishOpeningFile( format, pDevice, bAllocateTextures ); + + if ( FAILED( hr ) ) + { + m_bError = TRUE; + } + + return hr; +} + + + + +//----------------------------------------------------------------------------- +// Name: AdvanceFrameForTexturing() +// Desc: Unpack the appropriate frames of data for use as textures. +//----------------------------------------------------------------------------- +LPDIRECT3DTEXTURE8 CXMVPlayer::AdvanceFrameForTexturing( LPDIRECT3DDEVICE8 pDevice ) +{ + // You must pass bAllocateTextures==TRUE to Open if you're going to use GetTexture/AdvanceFrame. + assert( m_pTextures[0] ); + + LPDIRECT3DSURFACE8 pSurface; + pDecodingTexture->GetSurfaceLevel( 0, &pSurface ); + + // Decode some information to the current draw texture. + XMVRESULT xr = XMV_NOFRAME; + m_pXMVDecoder->GetNextFrame( pSurface, &xr, NULL ); + + switch ( xr ) + { + case XMV_NOFRAME: + // Do nothing - we didn't get a frame. + break; + + case XMV_NEWFRAME: + ++m_dwCurrentFrame; + // GetNextFrame produced a new frame. So, the texture we were decoding + // to becomes available for drawing as a texture. + pShowingTexture = pDecodingTexture; + + // Setup for decoding to the next texture. + m_nDecodeTextureIndex = ( m_nDecodeTextureIndex + 1 ) % XMVPLAYER_NUMTEXTURES; + pDecodingTexture = m_pTextures[ m_nDecodeTextureIndex ]; + break; + + case XMV_ENDOFFILE: + m_bPlaying = FALSE; + break; + + case XMV_FAIL: + // Data corruption or file read error. We'll treat that the same as + // end of file. + m_bPlaying = FALSE; + m_bError = TRUE; + break; + } + + SAFE_RELEASE( pSurface ); + + // If we haven't decoded the first frame then return zero. + if ( m_dwCurrentFrame < 0 ) + return 0; + + return pShowingTexture; +} + + + + +//----------------------------------------------------------------------------- +// Name: AdvanceFrameForOverlays() +// Desc: Unpack the appropriate frames of data for use as an overlay. +//----------------------------------------------------------------------------- +LPDIRECT3DTEXTURE8 CXMVPlayer::AdvanceFrameForOverlays( LPDIRECT3DDEVICE8 pDevice ) +{ + // You must pass bAllocateTextures==TRUE to Open if you're going to use GetTexture/AdvanceFrame. + assert( m_pTextures[0] ); + + // You have to call CXMVPlayer::EnableOverlays() if you are going to use overlays. + assert( m_bOverlaysEnabled ); + + // If a texture has been submitted to be used as an overlay then we have to + // wait for GetUpdateOverlayState() to return TRUE before we can assume that + // the previous texture has *stopped* being displayed. Once GetUpdateOverlayState() + // returns TRUE then we know that pSubmittedTexture is being displayed, which + // means that, pShowingTexture is available as a decoding target. + if ( pSubmittedTexture ) + { + // If GetOverlayUpdateStatus() returns FALSE then we can still proceed and + // call GetNextFrame(), but we will pass NULL for the surface parameter. + // Some work will still be done, but none of the surfaces will be altered. + if ( pDevice->GetOverlayUpdateStatus() ) + { + // The call to UpdateOverlay() with pSubmittedTexture must have taken + // effect now, so pShowingTexture is available as a decoding target. + assert( !pDecodingTexture ); + pDecodingTexture = pShowingTexture; + pShowingTexture = pSubmittedTexture; + pSubmittedTexture = NULL; + } + } + + LPDIRECT3DSURFACE8 pSurface = NULL; + if ( pDecodingTexture ) + pDecodingTexture->GetSurfaceLevel( 0, &pSurface ); + + // Decode some information to the current draw texture, which may be NULL. + // pDecodingTexture will be NULL if one texture has been submitted as a new + // overlay but the other one is still being displayed as an overlay. + // If pSurface is NULL GetNextFrame() will still do some work. + XMVRESULT xr = XMV_NOFRAME; + m_pXMVDecoder->GetNextFrame( pSurface, &xr, NULL ); + + switch ( xr ) + { + case XMV_NOFRAME: + // Do nothing - we didn't get a frame. + break; + + case XMV_NEWFRAME: + ++m_dwCurrentFrame; + // GetNextFrame produced a new frame. So, the texture we were decoding + // to becomes available for displaying as an overlay. + + // The other texture is not ready to be a decoding target. It is still + // being displayed as an overlay. So, we assign the newly decoded + // texture to pSubmittedTexture for the program to submit as an overlay, + // but we don't yet move the previously submitted texture from pShowing + // to pDecoding. That happens on a subsequent call to this function, after + // GetOverlayUpdateStatus() returns TRUE to tell us that there are no + // overlay swaps pending. + assert( pDecodingTexture ); + assert( !pSubmittedTexture ); + pSubmittedTexture = pDecodingTexture; + pDecodingTexture = NULL; + break; + + case XMV_ENDOFFILE: + m_bPlaying = FALSE; + break; + + case XMV_FAIL: + // Data corruption or file read error. We'll treat that the same as + // end of file. + m_bPlaying = FALSE; + m_bError = TRUE; + break; + } + + SAFE_RELEASE( pSurface ); + + // If we just unpacked a new frame then we return that texture + // and the program must call UpdateOverlay() with the surface + // from that texture. + // If we didn't unpack a frame then the program should do nothing - + // the previous overlay will continue to be displayed. + if ( XMV_NEWFRAME == xr ) + return pSubmittedTexture; + + // No new frame to display. + return 0; +} + + + + +//----------------------------------------------------------------------------- +// Name: TerminatePlayback() +// Desc: Calls XMVDecoder::TerminatePlayback() +//----------------------------------------------------------------------------- +void CXMVPlayer::TerminatePlayback() +{ + m_pXMVDecoder->TerminatePlayback(); +} + + + + +//----------------------------------------------------------------------------- +// Name: Play() +// Desc: Calls XMVDecoder::Play() to play the entire movie. +//----------------------------------------------------------------------------- +HRESULT CXMVPlayer::Play( DWORD Flags, RECT* pRect ) +{ + // You have to call Open before calling Play. + assert( m_pXMVDecoder ); + + // Don't pass bAllocateTextures==TRUE to Open if you're going to use Play. + assert( !m_pTextures[0] ); + + return m_pXMVDecoder->Play( Flags, pRect ); +} + + + + +//----------------------------------------------------------------------------- +// Name: EnableOverlays() +// Desc: Enable the overlay planes for playing the movie in them, and record +// that the overlays should be disabled when Destroy() is called. +//----------------------------------------------------------------------------- +void CXMVPlayer::EnableOverlays( LPDIRECT3DDEVICE8 pDevice ) +{ + m_pDevice = pDevice; + pDevice->EnableOverlay( TRUE ); + m_bOverlaysEnabled = TRUE; +} + + + + +//----------------------------------------------------------------------------- +// Name: GetNextPacket() +// Desc: Callback function to get next packet from a file +//----------------------------------------------------------------------------- +static HRESULT CALLBACK GetNextPacket( DWORD dwContext, VOID** ppPacket, + DWORD* pOffsetToNextPacket ) +{ + LOAD_CONTEXT* pContext = ( LOAD_CONTEXT* )dwContext; + if( NULL == pContext ) + return E_FAIL; + + // If the next packet is fully loaded then return it, + // otherwise return NULL. + DWORD dwBytesRead; + if( GetOverlappedResult( pContext->hFile, &pContext->Overlapped, + &dwBytesRead, FALSE ) ) + { + // Make the old decoding packet pending. + pContext->pPendingReleasePacket = pContext->pDecodingPacket; + pContext->pDecodingPacket = pContext->pLoadingPacket; + pContext->pLoadingPacket = NULL; + + // Offset to the next packet. + *pOffsetToNextPacket = dwBytesRead; + + // Set *ppPacket to the data we just loaded. + *ppPacket = pContext->pDecodingPacket; + } + else + { + DWORD dwError = GetLastError(); + + // If we're waiting on the IO to finish, just do nothing. + if( dwError != ERROR_IO_INCOMPLETE ) + return HRESULT_FROM_WIN32( dwError ); + + *ppPacket = NULL; + *pOffsetToNextPacket = 0; + } + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: ReleasePreviousPacket() +// Desc: Callback function to release previous packet from a file +//----------------------------------------------------------------------------- +static HRESULT CALLBACK ReleasePreviousPacket( DWORD dwContext, LONGLONG llNextReadByteOffset, + DWORD dwNextPacketSize ) +{ + LOAD_CONTEXT* pContext = ( LOAD_CONTEXT* )dwContext; + if( NULL == pContext ) + return E_FAIL; + + if( dwNextPacketSize != 0 ) + { + // Start the next load. + pContext->Overlapped.Offset = ( DWORD )( llNextReadByteOffset & 0xFFFFFFFF ); + pContext->Overlapped.OffsetHigh = ( DWORD )( llNextReadByteOffset >> 32 ); + + // Check for bad input file - buffer overrun + if( dwNextPacketSize > pContext->dwPacketSize ) + return E_FAIL; + + pContext->pLoadingPacket = pContext->pPendingReleasePacket; + pContext->pPendingReleasePacket = NULL; + + if( 0 == ReadFile( pContext->hFile, pContext->pLoadingPacket, + dwNextPacketSize, NULL, &pContext->Overlapped ) ) + { + if( GetLastError() != ERROR_IO_PENDING ) + return HRESULT_FROM_WIN32( GetLastError() ); + } + } + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: GetNextMemoryPacket() +// Desc: Callback function to get next packet from a file, +// and setup for the next packet. +//----------------------------------------------------------------------------- +static HRESULT CALLBACK GetNextMemoryPacket( DWORD dwContext, VOID** ppPacket, + DWORD* pOffsetToNextPacket ) +{ + LOAD_CONTEXT* pContext = ( LOAD_CONTEXT* )dwContext; + if( NULL == pContext ) + return E_FAIL; + + DWORD dwBytesRead = pContext->inputSize - pContext->readOffset; + if ( pContext->currentPacketSize < dwBytesRead ) + dwBytesRead = pContext->currentPacketSize; + + memcpy( pContext->pLoadingPacket, pContext->pInputBuffer + pContext->readOffset , dwBytesRead ); + pContext->readOffset +=dwBytesRead; + + // Swap pointers so that next time we load it goes into the other packet block. + BYTE* temp = pContext->pLoadingPacket; + pContext->pLoadingPacket = pContext->pDecodingPacket; + pContext->pDecodingPacket = temp; + + // Offset to the next packet. + *pOffsetToNextPacket = dwBytesRead; + + // Set *ppPacket to the data we just loaded. + *ppPacket = pContext->pDecodingPacket; + + return S_OK; +} + + + + +//----------------------------------------------------------------------------- +// Name: ReleasePreviousMemoryPacket() +// Desc: Callback function to release previous packet from a block of memory, +// and setup for the next packet. +//----------------------------------------------------------------------------- +static HRESULT CALLBACK ReleasePreviousMemoryPacket( DWORD dwContext, LONGLONG llNextReadByteOffset, + DWORD dwNextPacketSize ) +{ + LOAD_CONTEXT* pContext = ( LOAD_CONTEXT* )dwContext; + if( NULL == pContext ) + return E_FAIL; + + // Check for bad input file - buffer overrun + if( dwNextPacketSize > pContext->dwPacketSize ) + return E_FAIL; + + // Record the size of the next packet we are supposed to read, for GetNextMemoryPacket. + pContext->currentPacketSize = dwNextPacketSize; + + return S_OK; +} diff --git a/utils/xbox/xbox_loader/xmvhelper.h b/utils/xbox/xbox_loader/xmvhelper.h new file mode 100644 index 0000000..59dd390 --- /dev/null +++ b/utils/xbox/xbox_loader/xmvhelper.h @@ -0,0 +1,177 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +//----------------------------------------------------------------------------- +// File: XMVHelper.h +// +// Desc: Definition of XMV playback helper class for playing XMVs in a number +// of different ways. +// +// Hist: 2.7.03 - Created, based on work by Jeff Sullivan +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//----------------------------------------------------------------------------- + +#ifndef _XMVHELPER_H_ +#define _XMVHELPER_H_ + +#include <xtl.h> +#include <xmv.h> + +// Number of textures to allocate for decoding. A larger number may help to +// smooth out any frame rate variations if you are decoding to a texture. +// When decoding to overlays two textures are always used. +const DWORD XMVPLAYER_NUMTEXTURES = 2; + + + + +//----------------------------------------------------------------------------- +// Name: LOAD_CONTEXT +// Desc: This structure is used to hold information used by the packet callbacks. +//----------------------------------------------------------------------------- +struct LOAD_CONTEXT +{ + // Memory available for loading packets - maximum packet size. + DWORD dwPacketSize; + + // Packet that XMV was decoding out of, and might still be, that + // we want to load into as soon as we can. + BYTE* pPendingReleasePacket; + // Packet that XMV is decoding out of. + BYTE* pDecodingPacket; + // Packet we are currently loading into. + BYTE* pLoadingPacket; + + + // Information for when reading packets out of a file. + // Handle to the file we are reading from. + HANDLE hFile; + + // Overlapped structure to control asynchronous reading. + OVERLAPPED Overlapped; + + + // Information for when copying packets out of a memory buffer. + // The size and location of the buffer we are reading from if we are + // playing a movie from memory. + BYTE* pInputBuffer; + DWORD inputSize; + // The offset in the memory buffer that we are currently reading from. + DWORD readOffset; + // The size of the packet that we have 'queued up' for reading. This + // is a packet that we have warned about via ReleasePreviousMemoryPacket + // but have not yet been asked to load. + DWORD currentPacketSize; +}; + + + + +class CXMVPlayer +{ +public: + CXMVPlayer(); + ~CXMVPlayer(); + + // Open a .wmv file and prepare it for playing. + // The format parameter specifies what type of texture it should be unpacked into ( if allocateTextures + // is TRUE ). Only YUY2, LIN_X8R8G8B8 and LIN_A8R8G8B8 are supported as formats. + // If you plan to play the movie with Play() or otherwise use overlays then format must be D3DFMT_YUY2 + // pDevice is needed in order to create textures. + // allocateTextures should be TRUE if you plan to play the movie manually with AdvanceFrame. + // It should be FALSE if you plan to play the movie with Play(). + // IsPlaying() will return TRUE if the movie opened successfully. + HRESULT OpenFile( const CHAR* lpFilename, D3DFORMAT format, LPDIRECT3DDEVICE8 pDevice, BOOL bAllocateTextures ); + + // Open a file using the packet interface. This method of opening a .wmv file can be easily + // customized to read the data from game specific file formats. The GetNextPacket and + // ReleasePreviousPacket functions from XMVHelper.cpp are used to read the packets. See above for + // information about the other parameters. + HRESULT OpenFileForPackets( const CHAR* lpFilename, D3DFORMAT format, LPDIRECT3DDEVICE8 pDevice, BOOL bAllocateTextures ); + + // Open a block of memory for playing using the packet interface. This method of opening a .wmv file can be easily + // customized to read the data from game specific file formats, or to retain the block of data in memory. + // The GetNextMemoryPacket and ReleasePreviousMemoryPacket functions from XMVHelper.cpp are used to read + // the packets. See above for information about the other parameters. + HRESULT OpenMovieFromMemory( const CHAR* lpFilename, D3DFORMAT format, LPDIRECT3DDEVICE8 pDevice, BOOL bAllocateTextures ); + + // Destroy frees up all the movie resources. IsPlaying() will return FALSE afterwards. Destroy() + // can be called during movie playback, but is not thread safe. + HRESULT Destroy(); + + // The AdvanceFrame functions move to the next frame if it is time for that. Call this + // function frequently - typically 30-60 times per second. + + // AdvanceFrameForTexturing returns the most recent frame decoded, for texturing. + // It will return zero if the first frame has not been decoded, but otherwise + // will always return a texture. + LPDIRECT3DTEXTURE8 AdvanceFrameForTexturing( LPDIRECT3DDEVICE8 pDevice ); + + // AdvanceFrameForOverlays returns newly decoded frames, for use as an overlays. + // You MUST call UpdateOverlay() with the texture's surface. + // It will return zero if there is not a *new* frame available. + LPDIRECT3DTEXTURE8 AdvanceFrameForOverlays( LPDIRECT3DDEVICE8 pDevice ); + + // Get information about the movie playing. + DWORD GetWidth() const { return m_VideoDesc.Width; } + DWORD GetHeight() const { return m_VideoDesc.Height; } + DWORD GetCurrentFrame() const { return m_dwCurrentFrame; } + DWORD GetElapsedTime() const { return m_bPlaying ? GetTickCount() - m_dwStartTime : 0; } + + // IsPlaying returns TRUE if a movie has been opened but has not ended or been destroyed. + BOOL IsPlaying() const { return m_bPlaying; } + BOOL IsFailed() const { return m_bError; } + + // Call TerminatePlayback on the decoder. It may take a few hundred ms before the movie stops. + // This function is thread safe, as long as you ensure that you don't call Destroy() in another + // thread simultaneously. + void TerminatePlayback(); + HRESULT Play( DWORD Flags, RECT* pRect ); + + // Call this to enable overlays if you will be playing the movie in an overlay using + // AdvanceFrame. The overlays will be disabled when Destroy() is called. + void EnableOverlays( LPDIRECT3DDEVICE8 pDevice ); + +private: + XMVDecoder* m_pXMVDecoder; // Pointer to the XMV decoder object. + XMVVIDEO_DESC m_VideoDesc; // Description of the .xmv files video data. + XMVAUDIO_DESC m_AudioDesc; // Description of the .xmv files audio data. + LPDIRECT3DTEXTURE8 m_pTextures[XMVPLAYER_NUMTEXTURES]; // Textures to cycle through. + DWORD m_nDecodeTextureIndex; // Index of the current texture to decode to. + + // These texture pointers are copies of the values in m_pTextures. They are used to track + // the decode/display/pending status. + // When displaying using textures pShowingTexture points to the texture to display, + // pDecodingTexture points to the texture to decode into, and pSubmittedTexture is always + // zero. + // When displaying using overlays pShowingTexture points to the texture that the hardware + // is currently displaying, pDecodingTexture points to the texture to decode into, and + // pSubmittedTexture is a surface that has been submitted to the overlay system, but not + // necessarily displayed yet. At any given time one of these is always zero. When + // pDecodingTexture is zero that means that pShowingTexture might be being displayed, or + // the hardware might have switched to pSubmittedTexture, we don't know yet. + LPDIRECT3DTEXTURE8 pShowingTexture; + LPDIRECT3DTEXTURE8 pDecodingTexture; + LPDIRECT3DTEXTURE8 pSubmittedTexture; + + int m_dwCurrentFrame; // Current frame number - minus one if not yet started. + DWORD m_dwStartTime; + + BOOL m_bPlaying; // Is a movie currently playing? + BOOL m_bOverlaysEnabled; // Are overlays enabled? For disabling them on destroy. + LPDIRECT3DDEVICE8 m_pDevice; // For use by Destroy to disable overlays. + BOOL m_bError; + + LOAD_CONTEXT m_loadContext; // Data for communicating with packet callback functions. + + BYTE* m_physicalBuffer; // Pointer to block of physical memory used for loading packets. + + // Helper function for opening files. + HRESULT FinishOpeningFile( D3DFORMAT Format, LPDIRECT3DDEVICE8 pDevice, BOOL bAllocateTextures ); + + // Copy constructor and assignment operator are private and unimplemented + // to prevent unintentional, unsupported, and disastrous copying. + CXMVPlayer( const CXMVPlayer& source ); + CXMVPlayer& operator=( const CXMVPlayer& source ); +}; + +#endif //#ifndef _XMVPLAYER_H_ |