summaryrefslogtreecommitdiff
path: root/utils/xbox/MakeGameData
diff options
context:
space:
mode:
Diffstat (limited to 'utils/xbox/MakeGameData')
-rw-r--r--utils/xbox/MakeGameData/MakeGameData.cpp1174
-rw-r--r--utils/xbox/MakeGameData/MakeGameData.h165
-rw-r--r--utils/xbox/MakeGameData/MakeGameData.vpc111
-rw-r--r--utils/xbox/MakeGameData/MakeMaps.cpp383
-rw-r--r--utils/xbox/MakeGameData/MakeMisc.cpp86
-rw-r--r--utils/xbox/MakeGameData/MakeModels.cpp614
-rw-r--r--utils/xbox/MakeGameData/MakeParticles.cpp37
-rw-r--r--utils/xbox/MakeGameData/MakeResources.cpp343
-rw-r--r--utils/xbox/MakeGameData/MakeScenes.cpp40
-rw-r--r--utils/xbox/MakeGameData/MakeScenesImage.vpc133
-rw-r--r--utils/xbox/MakeGameData/MakeShaders.cpp52
-rw-r--r--utils/xbox/MakeGameData/MakeSounds.cpp968
-rw-r--r--utils/xbox/MakeGameData/MakeTextures.cpp45
-rw-r--r--utils/xbox/MakeGameData/MakeZip.cpp593
-rw-r--r--utils/xbox/MakeGameData/XZipTool.h61
-rw-r--r--utils/xbox/MakeGameData/imaadpcm.cpp1531
-rw-r--r--utils/xbox/MakeGameData/imaadpcm.h153
-rw-r--r--utils/xbox/MakeGameData/resample.cpp381
-rw-r--r--utils/xbox/MakeGameData/resample.h18
-rw-r--r--utils/xbox/MakeGameData/sound_io.cpp68
20 files changed, 6956 insertions, 0 deletions
diff --git a/utils/xbox/MakeGameData/MakeGameData.cpp b/utils/xbox/MakeGameData/MakeGameData.cpp
new file mode 100644
index 0000000..91857b1
--- /dev/null
+++ b/utils/xbox/MakeGameData/MakeGameData.cpp
@@ -0,0 +1,1174 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Generates a file list based on command line wildcard spec and drives
+// conversion routines based on file extension. The conversion routines should be
+// !!!elsewhere!!! in libraries that the game also uses at runtime to convert data.
+// This tool as spec'd should just be file iteration.
+//
+//=====================================================================================//
+
+#include "MakeGameData.h"
+
+// MAKESCENESIMAGE is defined for the external tool. In general, it only
+// supports the -pcscenes option. This gets built into MakeScenesImage.exe.
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+class MakeGameDataApp : public CDefaultAppSystemGroup< CSteamAppSystemGroup >
+{
+public:
+ // Methods of IApplication
+ virtual bool Create();
+ virtual bool PreInit( );
+ virtual int Main();
+ virtual void PostShutdown();
+};
+
+DEFINE_CONSOLE_STEAM_APPLICATION_OBJECT( MakeGameDataApp );
+
+char g_szSourcePath[MAX_PATH];
+char g_targetPath[MAX_PATH];
+char g_zipPath[MAX_PATH];
+bool g_bForce;
+bool g_bTest;
+bool g_bMakeZip;
+CXZipTool g_MasterXZip;
+DiskWriteMode_t g_WriteModeForConversions;
+char g_szGamePath[MAX_PATH];
+char g_szModPath[MAX_PATH];
+bool g_bModPathIsValid;
+bool g_bQuiet;
+bool g_bMakeScenes;
+bool g_bMakeScenesPC;
+bool g_bIsPlatformZip;
+bool g_bUseMapList;
+
+IPhysicsCollision *g_pPhysicsCollision;
+CSysModule *g_pPhysicsModule;
+
+CUtlVector< CUtlString > g_ValidMapList;
+CUtlVector< errorList_t > g_errorList;
+
+const char *g_GameNames[] =
+{
+ "ep2",
+ "episodic",
+ "hl2",
+ "portal",
+ "platform",
+ "tf",
+ NULL
+};
+
+// all known languages
+const char *g_pLanguageSuffixes[] =
+{
+ "_dannish",
+ "_dutch",
+ "_english",
+ "_finnish",
+ "_french",
+ "_german",
+ "_italian",
+ "_japanese",
+ "_korean",
+ "_koreana",
+ "_norwegian",
+ "_polish",
+ "_portuguese",
+ "_russian",
+ "_russion_buka",
+ "_schinese",
+ "_spanish",
+ "_swedish",
+ "_tchinese",
+ "_thai",
+};
+
+// 360 is shipping with support for only these languages
+const char *g_pTargetLanguageSuffixes[] =
+{
+ "_english.",
+ "_french.",
+ "_german.",
+};
+
+// Master list of files that can go into the zip
+static char *s_AllowedExtensionsInZip[] =
+{
+ // Explicitly lacking from this list, thus excluded from the zip
+ // .ain - AINs are external to the zip
+ // .bsp - BSPs are external to the zip
+ // .mp3 - MP3s aren't supported
+
+ // Extensions with conversions
+ // Purposely using .360 encoding to ensure pc versions don't leak in
+ ".360.wav",
+ ".360.vtf",
+ ".360.mdl",
+ ".360.ani",
+ ".dx90.360.vtx",
+ ".360.vvd",
+ ".360.phy",
+ ".360.dat",
+ ".360.lst",
+ ".360.vcs",
+ ".360.image",
+ ".360.pcf",
+
+ // Extensions without conversions (taken as is)
+ ".rc",
+ ".txt",
+ ".cfg",
+ ".res",
+ ".vfe",
+ ".vbf",
+ ".vmt",
+ ".raw",
+ ".lst",
+ ".bns",
+};
+
+
+
+//-----------------------------------------------------------------------------
+// Determine game path
+//-----------------------------------------------------------------------------
+void GetGamePath()
+{
+ GetCurrentDirectory( sizeof( g_szGamePath ), g_szGamePath );
+
+ char szFullPath[MAX_PATH];
+ if ( _fullpath( szFullPath, g_szGamePath, sizeof( szFullPath ) ) )
+ {
+ strcpy( g_szGamePath, szFullPath );
+ }
+ V_AppendSlash( g_szGamePath, sizeof( g_szGamePath ) );
+
+ char *pGameDir = V_stristr( g_szGamePath, "game\\" );
+ if ( !pGameDir )
+ {
+ Msg( "ERROR: Failed to determine game directory from current path. Expecting 'game' in current path." );
+ exit( 1 );
+ }
+
+ // kill any trailing dirs
+ pGameDir[4] = '\0';
+}
+
+//-----------------------------------------------------------------------------
+// Determine mod path
+//-----------------------------------------------------------------------------
+bool GetModPath()
+{
+ char szDirectory[MAX_PATH];
+ char szLastDirectory[MAX_PATH];
+
+ // non destructively determine the mod directory
+ bool bFound = false;
+ szLastDirectory[0] = '\0';
+ GetCurrentDirectory( sizeof( szDirectory ), szDirectory );
+ while ( 1 )
+ {
+ V_ComposeFileName( szDirectory, "gameinfo.txt", g_szModPath, sizeof( g_szModPath ) );
+ struct _stat statBuf;
+ if ( _stat( g_szModPath, &statBuf ) != -1 )
+ {
+ bFound = true;
+ V_strncpy( g_szModPath, szDirectory, sizeof( g_szModPath ) );
+ break;
+ }
+
+ // previous dir
+ V_ComposeFileName( szDirectory, "..", g_szModPath, sizeof( g_szModPath ) );
+
+ char fullPath[MAX_PATH];
+ if ( _fullpath( fullPath, g_szModPath, sizeof( fullPath ) ) )
+ {
+ strcpy( szDirectory, fullPath );
+ }
+
+ if ( !V_stricmp( szDirectory, szLastDirectory ) )
+ {
+ // can back up no further
+ break;
+ }
+ strcpy( szLastDirectory, szDirectory );
+ }
+
+ if ( !bFound )
+ {
+ // use current directory instead
+ GetCurrentDirectory( sizeof( g_szModPath ), g_szModPath );
+ }
+
+ return bFound;
+}
+
+//-----------------------------------------------------------------------------
+// Setup File system and search paths
+//-----------------------------------------------------------------------------
+bool SetupFileSystem()
+{
+ if ( g_bModPathIsValid )
+ {
+ CFSSteamSetupInfo steamInfo;
+ steamInfo.m_pDirectoryName = g_szModPath;
+ steamInfo.m_bOnlyUseDirectoryName = true;
+ steamInfo.m_bToolsMode = true;
+ steamInfo.m_bSetSteamDLLPath = true;
+ steamInfo.m_bSteam = false;
+ if ( FileSystem_SetupSteamEnvironment( steamInfo ) != FS_OK )
+ return false;
+
+ CFSMountContentInfo fsInfo;
+ fsInfo.m_pFileSystem = g_pFullFileSystem;
+ fsInfo.m_bToolsMode = true;
+ fsInfo.m_pDirectoryName = steamInfo.m_GameInfoPath;
+ if ( FileSystem_MountContent( fsInfo ) != FS_OK )
+ return false;
+
+ // Finally, load the search paths for the "GAME" path.
+ CFSSearchPathsInit searchPathsInit;
+ searchPathsInit.m_pDirectoryName = steamInfo.m_GameInfoPath;
+ searchPathsInit.m_pFileSystem = fsInfo.m_pFileSystem;
+ if ( FileSystem_LoadSearchPaths( searchPathsInit ) != FS_OK )
+ return false;
+
+ char platform[MAX_PATH];
+ Q_strncpy( platform, steamInfo.m_GameInfoPath, MAX_PATH );
+ Q_StripTrailingSlash( platform );
+ Q_strncat( platform, "/../platform", MAX_PATH, MAX_PATH );
+ fsInfo.m_pFileSystem->AddSearchPath( platform, "PLATFORM" );
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Helper utility, read file into buffer
+//-----------------------------------------------------------------------------
+bool ReadFileToBuffer( const char *pSourceName, CUtlBuffer &buffer, bool bText, bool bNoOpenFailureWarning )
+{
+ return scriptlib->ReadFileToBuffer( pSourceName, buffer, bText, bNoOpenFailureWarning );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Helper utility, Write buffer to file
+//-----------------------------------------------------------------------------
+bool WriteBufferToFile( const char *pTargetName, CUtlBuffer &buffer, bool bWriteToZip, DiskWriteMode_t writeMode )
+{
+ if ( g_bTest )
+ return true;
+
+ bool bSuccess = scriptlib->WriteBufferToFile( pTargetName, buffer, writeMode );
+ bool bZipSuccess = true;
+
+ if ( bSuccess && g_bMakeZip && !g_bTest && bWriteToZip )
+ {
+ if ( !g_MasterXZip.AddBuffer( pTargetName, buffer, true ) )
+ {
+ Msg( "WriteBufferToFile(): Error adding file %s\n", pTargetName );
+ bZipSuccess = false;
+ }
+ }
+
+ return bSuccess && bZipSuccess;
+}
+
+//-----------------------------------------------------------------------------
+// Compress data
+//-----------------------------------------------------------------------------
+bool CompressCallback( CUtlBuffer &inputBuffer, CUtlBuffer &outputBuffer )
+{
+ if ( !inputBuffer.TellPut() )
+ {
+ // nothing to do
+ return false;
+ }
+
+ unsigned int compressedSize;
+ unsigned char *pCompressedOutput = LZMA_OpportunisticCompress( (unsigned char *)inputBuffer.Base() + inputBuffer.TellGet(), inputBuffer.TellPut() - inputBuffer.TellGet(), &compressedSize );
+ if ( pCompressedOutput )
+ {
+ outputBuffer.EnsureCapacity( compressedSize );
+ outputBuffer.Put( pCompressedOutput, compressedSize );
+ free( pCompressedOutput );
+ return true;
+ }
+
+ return false;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Some converters need to run a final pass.
+//-----------------------------------------------------------------------------
+void DoPostProcessingFunctions( bool bWriteToZip )
+{
+ if ( g_bMakeScenes || g_bMakeScenesPC )
+ {
+ // scenes are converted and aggregated into one image
+ CreateSceneImageFile( g_szModPath, bWriteToZip, g_bMakeScenesPC, g_bQuiet, g_WriteModeForConversions );
+ }
+
+ ProcessDXSupportConfig( bWriteToZip );
+}
+
+//-----------------------------------------------------------------------------
+// Startup any conversion modules.
+//-----------------------------------------------------------------------------
+bool InitConversionModules()
+{
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Shutdown and cleanup any conversion modules.
+//-----------------------------------------------------------------------------
+void ShutdownConversionModules()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Distribute to worker function
+//-----------------------------------------------------------------------------
+bool CreateTargetFile( const char *pSourceName, const char *pTargetName, fileType_e fileType, bool bWriteToZip )
+{
+ // resolve relative source to absolute path
+ // same workers use subdir name to determine conversion parameters
+ char fullSourcePath[MAX_PATH];
+ if ( _fullpath( fullSourcePath, pSourceName, sizeof( fullSourcePath ) ) )
+ {
+ pSourceName = fullSourcePath;
+ }
+
+ // distribute to actual worker
+ // workers can expect exact final decorated filenames
+ bool bSuccess = false;
+ switch ( fileType )
+ {
+ case FILETYPE_UNKNOWN:
+ break;
+
+ case FILETYPE_VTF:
+ bSuccess = CreateTargetFile_VTF( pSourceName, pTargetName, bWriteToZip );
+ break;
+
+ case FILETYPE_WAV:
+ bSuccess = CreateTargetFile_WAV( pSourceName, pTargetName, bWriteToZip );
+ break;
+
+ case FILETYPE_MDL:
+ case FILETYPE_ANI:
+ case FILETYPE_VTX:
+ case FILETYPE_VVD:
+ case FILETYPE_PHY:
+ bSuccess = CreateTargetFile_Model( pSourceName, pTargetName, bWriteToZip );
+ break;
+
+ case FILETYPE_BSP:
+ bSuccess = CreateTargetFile_BSP( pSourceName, pTargetName, bWriteToZip );
+ break;
+
+ case FILETYPE_AIN:
+ bSuccess = CreateTargetFile_AIN( pSourceName, pTargetName, bWriteToZip );
+ break;
+
+ case FILETYPE_CCDAT:
+ bSuccess = CreateTargetFile_CCDAT( pSourceName, pTargetName, bWriteToZip );
+ break;
+
+ case FILETYPE_MP3:
+ bSuccess = CreateTargetFile_MP3( pSourceName, pTargetName, bWriteToZip );
+ break;
+
+ case FILETYPE_RESLST:
+ bSuccess = CreateTargetFile_RESLST( pSourceName, pTargetName, bWriteToZip );
+ break;
+
+ case FILETYPE_PCF:
+ bSuccess = CreateTargetFile_PCF( pSourceName, pTargetName, bWriteToZip );
+ break;
+
+ // others...
+ }
+
+ return bSuccess;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Determine file type based on source extension. Generate .360.xxx
+// target name.
+//-----------------------------------------------------------------------------
+fileType_e ResolveFileType( const char *pSourceName, char *pTargetName, int targetNameSize )
+{
+ char szFullSourcePath[MAX_PATH];
+ _fullpath( szFullSourcePath, pSourceName, sizeof( szFullSourcePath ) );
+
+ char sourceExtension[MAX_PATH];
+ V_ExtractFileExtension( pSourceName, sourceExtension, sizeof( sourceExtension ) );
+
+ // default unrecognized
+ fileType_e fileType = FILETYPE_UNKNOWN;
+
+ if ( !V_stricmp( sourceExtension, "wav" ) )
+ {
+ fileType = FILETYPE_WAV;
+ }
+ else if ( !V_stricmp( sourceExtension, "vtf" ) )
+ {
+ fileType = FILETYPE_VTF;
+ }
+ else if ( !V_stricmp( sourceExtension, "mdl" ) )
+ {
+ fileType = FILETYPE_MDL;
+ }
+ else if ( !V_stricmp( sourceExtension, "ani" ) )
+ {
+ fileType = FILETYPE_ANI;
+ }
+ else if ( !V_stricmp( sourceExtension, "vvd" ) )
+ {
+ fileType = FILETYPE_VVD;
+ }
+ else if ( !V_stricmp( sourceExtension, "phy" ) )
+ {
+ fileType = FILETYPE_PHY;
+ }
+ else if ( !V_stricmp( sourceExtension, "bsp" ) )
+ {
+ fileType = FILETYPE_BSP;
+ }
+ else if ( !V_stricmp( sourceExtension, "ain" ) )
+ {
+ fileType = FILETYPE_AIN;
+ }
+ else if ( !V_stricmp( sourceExtension, "dat" ) )
+ {
+ if ( V_stristr( pSourceName, "closecaption_" ) )
+ {
+ // only want closecaption dat files, ignore all others
+ fileType = FILETYPE_CCDAT;
+ }
+ }
+ else if ( !V_stricmp( sourceExtension, "vtx" ) )
+ {
+ if ( V_stristr( pSourceName, ".dx90" ) )
+ {
+ // only want dx90 version, ignore all others
+ fileType = FILETYPE_VTX;
+ }
+ }
+ else if ( !V_stricmp( sourceExtension, "mp3" ) )
+ {
+ // mp3's are already pre-converted into .360.wav
+ // slam the expected name here to simplify the external logic which will do the right thing
+ V_StripExtension( pSourceName, pTargetName, targetNameSize );
+ V_strcat( pTargetName, ".360.wav", targetNameSize );
+ return FILETYPE_MP3;
+ }
+ else if ( !V_stricmp( sourceExtension, "lst" ) )
+ {
+ if ( V_stristr( szFullSourcePath, "reslists_xbox\\" ) )
+ {
+ // only want reslists map versions, due to special processing, ignore all others
+ fileType = FILETYPE_RESLST;
+ }
+ }
+ else if ( !V_stricmp( sourceExtension, "pcf" ) )
+ {
+ fileType = FILETYPE_PCF;
+ }
+ else if ( !V_stricmp( sourceExtension, "image" ) )
+ {
+ if ( V_stristr( szFullSourcePath, "scenes\\" ) )
+ {
+ // only want scene image, ignore all others
+ fileType = FILETYPE_SCENEIMAGE;
+ }
+ }
+
+ if ( fileType != FILETYPE_UNKNOWN && !V_stristr( pSourceName, ".360." ) )
+ {
+ char targetExtension[MAX_PATH];
+ sprintf( targetExtension, ".360.%s", sourceExtension );
+
+ V_StripExtension( pSourceName, pTargetName, targetNameSize );
+ V_strcat( pTargetName, targetExtension, targetNameSize );
+ }
+ else
+ {
+ // unknown or already converted, target is same as input
+ V_strncpy( pTargetName, pSourceName, targetNameSize );
+ }
+
+ return fileType;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns TRUE if file is a known localized file.
+//-----------------------------------------------------------------------------
+bool IsLocalizedFile( const char *pFileName )
+{
+ for ( int i = 0; i<ARRAYSIZE( g_pLanguageSuffixes ); i++ )
+ {
+ if ( V_stristr( pFileName, g_pLanguageSuffixes[i] ) )
+ {
+ // a localized file
+ return true;
+ }
+ }
+
+ // not a known localized file
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns TRUE if file is a supported localization.
+//-----------------------------------------------------------------------------
+bool IsLocalizedFileValid( const char *pFileName, const char *pLanguageSuffix )
+{
+ // file is a localized version
+ if ( pLanguageSuffix )
+ {
+ if ( V_stristr( pFileName, pLanguageSuffix ) )
+ {
+ // allow it
+ return true;
+ }
+
+ return false;
+ }
+
+ // must match the target supported languages
+ for ( int i = 0; i < ARRAYSIZE( g_pTargetLanguageSuffixes ); i++ )
+ {
+ if ( V_stristr( pFileName, g_pTargetLanguageSuffixes[i] ) )
+ {
+ // allow it
+ return true;
+ }
+ }
+
+ // does not match a target language, not allowed
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check against a list of allowed filetypes for inclusion in the zip
+//-----------------------------------------------------------------------------
+bool IncludeInZip( const char *pSourceName )
+{
+ if ( g_bIsPlatformZip )
+ {
+ // only allow known valid platform directories
+ if ( !V_stristr( pSourceName, "materials\\" ) &&
+ !V_stristr( pSourceName, "resource\\" ) &&
+ !V_stristr( pSourceName, "vgui\\" ) )
+ {
+ // exclude it
+ return false;
+ }
+ }
+
+ for ( int i = 0; i < ARRAYSIZE( s_AllowedExtensionsInZip ); ++i )
+ {
+ const char *pAllowedExtension = s_AllowedExtensionsInZip[i];
+ if ( !V_stristr( pSourceName, pAllowedExtension ) )
+ {
+ continue;
+ }
+
+ if ( !V_stricmp( pAllowedExtension, ".lst" ) )
+ {
+ // only want ???_exclude.lst files
+ if ( V_stristr( pSourceName, "_exclude.lst" ) )
+ {
+ return true;
+ }
+ return false;
+ }
+
+ if ( !V_stricmp( pAllowedExtension, ".txt" ) || !V_stricmp( pAllowedExtension, ".360.dat" ) )
+ {
+ if ( IsLocalizedFile( pSourceName ) && !IsLocalizedFileValid( pSourceName ) )
+ {
+ // exclude unsupported languages
+ return false;
+ }
+
+ if ( !V_stricmp( pAllowedExtension, ".txt" ) && V_stristr( pSourceName, "closecaption_" ) )
+ {
+ // exclude all the closecaption_<language>.txt files
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ // exclude it
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Returns true if map is in list, otherwise false
+//-----------------------------------------------------------------------------
+bool IsMapNameInList( const char *pMapName, CUtlVector< CUtlString > &mapList )
+{
+ char szBaseName[MAX_PATH];
+
+ V_FileBase( pMapName, szBaseName, sizeof( szBaseName ) );
+ V_strlower( szBaseName );
+
+ if ( mapList.Find( szBaseName ) != mapList.InvalidIndex() )
+ {
+ // found
+ return true;
+ }
+
+ // not found
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the list of valid BSPs
+//-----------------------------------------------------------------------------
+void BuildValidMapList( CUtlVector< CUtlString > &mapList )
+{
+ char szFilename[MAX_PATH];
+ V_ComposeFileName( g_szModPath, "maplist.txt", szFilename, sizeof( szFilename ) );
+
+ CUtlBuffer buffer;
+ if ( !ReadFileToBuffer( szFilename, buffer, true, true ) )
+ {
+ return;
+ }
+
+ characterset_t breakSet;
+ CharacterSetBuild( &breakSet, "" );
+
+ char szToken[MAX_PATH];
+ char szMapName[MAX_PATH];
+ for ( ;; )
+ {
+ int nTokenSize = buffer.ParseToken( &breakSet, szToken, sizeof( szToken ) );
+ if ( nTokenSize <= 0 )
+ {
+ break;
+ }
+
+ // reslists are pc built, filenames can be sloppy
+ V_FileBase( szToken, szMapName, sizeof( szMapName ) );
+ V_strlower( szMapName );
+
+ mapList.AddToTail( szMapName );
+ }
+}
+
+#define DO_UPDATE 0x01
+#define DO_ZIP 0x02
+
+//-----------------------------------------------------------------------------
+// Purpose: drive the file creation
+//-----------------------------------------------------------------------------
+void GenerateTargetFiles( CUtlVector<fileList_t> &fileList )
+{
+ char sourcePath[MAX_PATH];
+ char sourceFile[MAX_PATH];
+ char targetFile[MAX_PATH];
+ struct _stat sourceStatBuf;
+ struct _stat targetStatBuf;
+
+ strcpy( sourcePath, g_szSourcePath );
+ V_StripFilename( sourcePath );
+ if ( !sourcePath[0] )
+ strcpy( sourcePath, "." );
+ V_AppendSlash( sourcePath, sizeof( sourcePath ) );
+
+ // default is to update and zip
+ CUtlVector< int > updateList;
+ updateList.AddMultipleToTail( fileList.Count() );
+ for ( int i=0; i<fileList.Count(); i++ )
+ {
+ updateList[i] = DO_UPDATE|DO_ZIP;
+ }
+ int numMatches = 0;
+
+ // build update list
+ for ( int i=0; i<fileList.Count(); i++ )
+ {
+ if ( fileList[i].fileName.IsEmpty() )
+ {
+ // ignore entries that have been culled
+ updateList[i] = 0;
+ continue;
+ }
+
+ const char *ptr = fileList[i].fileName.String();
+ if ( !strnicmp( ptr, ".\\", 2 ) )
+ ptr += 2;
+ else if ( !strnicmp( ptr, sourcePath, strlen( sourcePath ) ) )
+ ptr += strlen( sourcePath );
+
+ strcpy( sourceFile, sourcePath );
+ strcat( sourceFile, ptr );
+ strcpy( targetFile, g_targetPath );
+ strcat( targetFile, ptr );
+
+ fileType_e fileType = ResolveFileType( sourceFile, targetFile, sizeof( targetFile ) );
+
+ // check the target name for inclusion due to extension modifications
+ if ( !IncludeInZip( targetFile ) )
+ {
+ // exclude from zip
+ updateList[i] &= ~DO_ZIP;
+ }
+
+ if ( fileType == FILETYPE_UNKNOWN )
+ {
+ // No conversion function, can't do anything
+ updateList[i] &= ~DO_UPDATE;
+ continue;
+ }
+ else
+ {
+ // known filetype, which has a conversion
+ // the wildcard match may catch existing converted files, which need to be rejected
+ // cull exisiting conversions from the work list
+ // the non-converted filename is part of the same wildcard match, gets caught and resolved
+ // the non-converted filename will then go through the proper conversion path
+ if ( V_stristr( sourceFile, ".360." ) )
+ {
+ // cull completely
+ updateList[i] = 0;
+ continue;
+ }
+ }
+
+ if ( fileType == FILETYPE_BSP || fileType == FILETYPE_AIN || fileType == FILETYPE_RESLST )
+ {
+ if ( g_ValidMapList.Count() && !IsMapNameInList( sourceFile, g_ValidMapList ) )
+ {
+ // cull completely
+ updateList[i] = 0;
+ continue;
+ }
+ }
+
+ int retVal = _stat( sourceFile, &sourceStatBuf );
+ if ( retVal != 0 )
+ {
+ // couldn't get source, skip update or zip
+ updateList[i] = 0;
+ continue;
+ }
+
+ retVal = _stat( targetFile, &targetStatBuf );
+ if ( retVal != 0 )
+ {
+ // target doesn't exit, update is required
+ continue;
+ }
+
+ // track valid candidates
+ numMatches++;
+
+ if ( fileType == FILETYPE_MDL || fileType == FILETYPE_ANI || fileType == FILETYPE_VTX || fileType == FILETYPE_VVD || fileType == FILETYPE_PHY )
+ {
+ // models are converted in a pre-pass
+ // let the update logic run and catch the conversions for zipping
+ continue;
+ }
+
+ if ( !g_bForce )
+ {
+ if ( difftime( sourceStatBuf.st_mtime, targetStatBuf.st_mtime ) <= 0 )
+ {
+ // target is same or older, no update
+ updateList[i] &= ~DO_UPDATE;
+ }
+ }
+ }
+
+ // cleanse and determine totals, makes succeeding logic simpler
+ int numWorkItems = 0;
+ int numFilesToUpdate = 0;
+ int numFilesToZip = 0;
+ for ( int i=0; i<fileList.Count(); i++ )
+ {
+ if ( updateList[i] & DO_UPDATE )
+ {
+ numFilesToUpdate++;
+ }
+ if ( g_bMakeZip && ( updateList[i] & DO_ZIP ) )
+ {
+ numFilesToZip++;
+ }
+ else
+ {
+ updateList[i] &= ~DO_ZIP;
+ }
+ if ( updateList[i] )
+ {
+ numWorkItems++;
+ }
+ }
+
+ Msg( "\n" );
+ Msg( "Matched %d/%d files.\n", numMatches, fileList.Count() );
+ Msg( "Creating or Updating %d files.\n", numFilesToUpdate );
+ if ( g_bMakeZip )
+ {
+ Msg( "Zipping %d files.\n", numFilesToZip );
+ }
+
+ InitConversionModules();
+
+ if ( numFilesToZip && !g_bTest )
+ {
+ Msg( "Creating Zip: %s\n", g_zipPath );
+ if ( !g_MasterXZip.Begin( g_zipPath, XBOX_DVD_SECTORSIZE ) )
+ {
+ Msg( "ERROR: Failed to open \"%s\" for writing.\n", g_zipPath );
+ return;
+ }
+ else
+ {
+ SetupCriticalPreloadScript( g_szModPath );
+ }
+ }
+
+ // iterate work list
+ int progress = 0;
+ for ( int i=0; i<fileList.Count(); i++ )
+ {
+ if ( !updateList[i] )
+ {
+ // no update or zip needed, skip
+ continue;
+ }
+
+ const char *ptr = fileList[i].fileName.String();
+ if ( !strnicmp( ptr, ".\\", 2 ) )
+ ptr += 2;
+ else if ( !strnicmp( ptr, sourcePath, strlen( sourcePath ) ) )
+ ptr += strlen( sourcePath );
+
+ strcpy( sourceFile, sourcePath );
+ strcat( sourceFile, ptr );
+ strcpy( targetFile, g_targetPath );
+ strcat( targetFile, ptr );
+
+ fileType_e fileType = ResolveFileType( sourceFile, targetFile, sizeof( targetFile ) );
+
+ if ( !g_bQuiet )
+ {
+ Msg( "%d/%d:%s -> %s\n", progress+1, numWorkItems, sourceFile, targetFile );
+ }
+
+ bool bSuccess = true;
+ if ( updateList[i] & DO_UPDATE )
+ {
+ // generate target file (and optionally zip output)
+ bSuccess = CreateTargetFile( sourceFile, targetFile, fileType, (updateList[i] & DO_ZIP) != 0 );
+ if ( !bSuccess )
+ {
+ // add to error list
+ int error = g_errorList.AddToTail();
+ g_errorList[error].result = false;
+ g_errorList[error].fileName.Set( sourceFile );
+ }
+ }
+ else if ( updateList[i] & DO_ZIP )
+ {
+ // existing target file is zipped
+ CUtlBuffer targetBuffer;
+ bSuccess = scriptlib->ReadFileToBuffer( targetFile, targetBuffer );
+ if ( bSuccess )
+ {
+ if ( !g_bTest )
+ {
+ bSuccess = g_MasterXZip.AddBuffer( targetFile, targetBuffer, true );
+ }
+ }
+ if ( !bSuccess )
+ {
+ // add to error list
+ int error = g_errorList.AddToTail();
+ g_errorList[error].result = false;
+ g_errorList[error].fileName.Set( targetFile );
+ }
+ }
+
+ progress++;
+ }
+
+ DoPostProcessingFunctions( !g_bTest );
+
+ ShutdownConversionModules();
+
+ if ( numFilesToZip && !g_bTest )
+ {
+ g_MasterXZip.End();
+ }
+
+ // iterate error list
+ Msg( "\n" );
+ for ( int i = 0; i < g_errorList.Count(); i++ )
+ {
+ Msg( "ERROR: could not process %s\n", g_errorList[i].fileName.String() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Spew Usage
+//-----------------------------------------------------------------------------
+void Usage()
+{
+ Msg( "usage: MakeGameData [filemask] [options]\n" );
+ Msg( "options:\n" );
+ Msg( "[-v] Version\n" );
+ Msg( "[-q] Quiet (critical spew only)\n" );
+ Msg( "[-h] [-help] [-?] Help\n" );
+ Msg( "[-t targetPath] Alternate output path, will generate output at target\n" );
+ Msg( "[-r] [-recurse] Recurse into source directory\n" );
+ Msg( "[-f] [-force] Force update, otherwise checks timestamps\n" );
+ Msg( "[-test] Skip writing to disk\n" );
+ Msg( "[-z <zipname>] Generate zip file AND create or update stale conversions\n" );
+ Msg( "[-zo <zipname>] Generate zip file ONLY (existing stale conversions get updated, no new conversions are written)\n" );
+ Msg( "[-preloadinfo] Spew contents of preload section in zip\n" );
+ Msg( "[-xmaquality <quality>] XMA Encoding quality override, [0-100]\n" );
+ Msg( "[-scenes] Make 360 scene image cache.\n" );
+ Msg( "[-pcscenes] Make PC scene image cache.\n" );
+ Msg( "[-usemaplist] For BSP related conversions, restricts to maplist.txt.\n" );
+
+ exit( 1 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: default output func
+//-----------------------------------------------------------------------------
+SpewRetval_t OutputFunc( SpewType_t spewType, char const *pMsg )
+{
+ printf( pMsg );
+
+ if ( spewType == SPEW_ERROR )
+ {
+ return SPEW_ABORT;
+ }
+ return ( spewType == SPEW_ASSERT ) ? SPEW_DEBUGGER : SPEW_CONTINUE;
+}
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+bool MakeGameDataApp::Create()
+{
+ SpewOutputFunc( OutputFunc );
+
+ AppSystemInfo_t appSystems[] =
+ {
+ { "mdllib.dll", MDLLIB_INTERFACE_VERSION },
+ { "", "" } // Required to terminate the list
+ };
+
+ AddSystem( g_pDataModel, VDATAMODEL_INTERFACE_VERSION );
+ AddSystem( g_pDmSerializers, DMSERIALIZERS_INTERFACE_VERSION );
+
+ // Load vphysics.dll
+ if ( !Sys_LoadInterface( "vphysics.dll", VPHYSICS_COLLISION_INTERFACE_VERSION, &g_pPhysicsModule, (void**)&g_pPhysicsCollision ) )
+ {
+ Msg( "Failed to load vphysics interface\n" );
+ return false;
+ }
+
+ bool bOk = AddSystems( appSystems );
+ if ( !bOk )
+ return false;
+
+ return true;
+}
+
+bool MakeGameDataApp::PreInit()
+{
+ CreateInterfaceFn factory = GetFactory();
+
+ ConnectTier1Libraries( &factory, 1 );
+ ConnectTier2Libraries( &factory, 1 );
+
+ if ( !g_pFullFileSystem || !g_pDataModel || !g_pPhysicsCollision || !mdllib )
+ {
+ Warning( "MakeGameData is missing a required interface!\n" );
+ return false;
+ }
+
+ return true;
+}
+
+void MakeGameDataApp::PostShutdown()
+{
+ if ( g_pPhysicsModule )
+ {
+ Sys_UnloadModule( g_pPhysicsModule );
+ g_pPhysicsModule = NULL;
+ g_pPhysicsCollision = NULL;
+ }
+
+ DisconnectTier2Libraries();
+ DisconnectTier1Libraries();
+}
+
+//-----------------------------------------------------------------------------
+// main
+//
+//-----------------------------------------------------------------------------
+int MakeGameDataApp::Main()
+{
+ int argnum;
+
+ // set the valve library printer
+ SpewOutputFunc( OutputFunc );
+
+ Msg( "\nMAKEGAMEDATA - Valve Xbox 360 Game Data Compiler (Build: %s %s)\n", __DATE__, __TIME__ );
+ Msg( "(C) Copyright 1996-2006, Valve Corporation, All rights reserved.\n\n" );
+
+ if ( CommandLine()->FindParm( "-v" ) || CommandLine()->FindParm( "-version" ) )
+ {
+ // spew just the version, used by batches for logging
+ return 0;
+ }
+
+#ifndef MAKESCENESIMAGE
+ if ( CommandLine()->ParmCount() < 2 || CommandLine()->FindParm( "?" ) || CommandLine()->FindParm( "-h" ) || CommandLine()->FindParm( "-help" ) )
+ {
+ Usage();
+ }
+#endif // MAKESCENESIMAGE
+
+ bool bHasFileMask = false;
+
+ const char *pFirstArg = CommandLine()->GetParm( 1 );
+ if ( pFirstArg[0] == '-' )
+ {
+ // first arg is an option, assume *.*
+ strcpy( g_szSourcePath, "*.*" );
+ }
+ else
+ {
+ strcpy( g_szSourcePath, pFirstArg );
+ bHasFileMask = true;
+ }
+
+ bool bRecurse = false;
+#ifndef MAKESCENESIMAGE
+ bRecurse = CommandLine()->FindParm( "-recurse" ) || CommandLine()->FindParm( "-r" );
+ g_bForce = CommandLine()->FindParm( "-force" ) || CommandLine()->FindParm( "-f" );
+ g_bTest = CommandLine()->FindParm( "-test" ) != 0;
+ g_bQuiet = CommandLine()->FindParm( "-quiet" ) || CommandLine()->FindParm( "-q" );
+ g_bMakeScenes = CommandLine()->FindParm( "-scenes" ) != 0;
+ g_bMakeScenesPC = CommandLine()->FindParm( "-pcscenes" ) != 0;
+#else
+ g_bMakeScenesPC = true;
+#endif // MAKESCENESIMAGE
+
+#ifndef MAKESCENESIMAGE
+ g_bUseMapList = CommandLine()->FindParm( "-usemaplist" ) != 0;
+#endif // MAKESCENESIMAGE
+
+ // Set up zip file options
+ g_WriteModeForConversions = WRITE_TO_DISK_ALWAYS;
+ argnum = CommandLine()->FindParm( "-z" );
+ if ( argnum )
+ {
+ strcpy( g_szSourcePath, "*.*" );
+ g_bMakeZip = true;
+ g_bMakeScenes = true;
+ bRecurse = true;
+ }
+ else
+ {
+ argnum = CommandLine()->FindParm( "-zo" );
+ if ( argnum )
+ {
+ strcpy( g_szSourcePath, "*.*" );
+ g_bMakeZip = true;
+ g_bMakeScenes = true;
+ g_WriteModeForConversions = WRITE_TO_DISK_UPDATE;
+ bRecurse = true;
+ }
+ }
+ if ( g_bMakeZip )
+ {
+ strcat( g_zipPath, CommandLine()->GetParm( argnum + 1 ) );
+ }
+
+ // default target path is source
+ strcpy( g_targetPath, g_szSourcePath );
+ V_StripFilename( g_targetPath );
+ if ( !g_targetPath[0] )
+ {
+ strcpy( g_targetPath, "." );
+ }
+
+ // override via command line
+ argnum = CommandLine()->FindParm( "-t" );
+ if ( argnum )
+ {
+ V_strcpy_safe( g_targetPath, CommandLine()->GetParm( argnum + 1 ) );
+ }
+ V_AppendSlash( g_targetPath, sizeof( g_targetPath ) );
+
+ if ( CommandLine()->FindParm( "-preloadinfo" ) )
+ {
+ g_MasterXZip.SpewPreloadInfo( g_szSourcePath );
+ return 0;
+ }
+
+#ifndef MAKESCENESIMAGE
+ GetGamePath();
+#endif // MAKESCENESIMAGE
+
+ g_bModPathIsValid = GetModPath();
+ if ( !SetupFileSystem() )
+ {
+ Msg( "ERROR: Failed to setup file system.\n" );
+ exit( 1 );
+ }
+
+ // data model initialization
+ g_pDataModel->SetUndoEnabled( false );
+ g_pDataModel->OnlyCreateUntypedElements( true );
+ g_pDataModel->SetDefaultElementFactory( NULL );
+
+ g_bIsPlatformZip = g_bMakeZip && ( V_stristr( g_szModPath, "\\platform" ) != NULL );
+
+ // cleanup any zombie temp files left from a possible prior abort or error
+ scriptlib->DeleteTemporaryFiles( "mgd_*.tmp" );
+
+ if ( g_bMakeZip || g_bUseMapList )
+ {
+ // zips use the map list to narrow the bsp conversion to actual shipping maps
+ BuildValidMapList( g_ValidMapList );
+ }
+
+ CUtlVector<fileList_t> fileList;
+ if ( bHasFileMask || g_bMakeZip )
+ {
+ scriptlib->FindFiles( g_szSourcePath, bRecurse, fileList );
+ }
+
+ // model conversions require seperate pre-processing to achieve grouping
+ if ( !PreprocessModelFiles( fileList ) )
+ {
+ return 0;
+ }
+
+ GenerateTargetFiles( fileList );
+
+ return 0;
+}
diff --git a/utils/xbox/MakeGameData/MakeGameData.h b/utils/xbox/MakeGameData/MakeGameData.h
new file mode 100644
index 0000000..c2f2004
--- /dev/null
+++ b/utils/xbox/MakeGameData/MakeGameData.h
@@ -0,0 +1,165 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=====================================================================================//
+
+#pragma once
+
+#include <windows.h>
+#include <mmreg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <direct.h>
+#include <io.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/utime.h>
+#include "tier0/dbg.h"
+#include "tier0/icommandline.h"
+#include "appframework/appframework.h"
+#include "characterset.h"
+#include "tier1/strtools.h"
+#include "tier1/UtlVector.h"
+#include "tier1/UtlBuffer.h"
+#include "tier1/UtlString.h"
+#include "tier1/UtlRBTree.h"
+#include "tier1/UtlDict.h"
+#include "tier1/UtlSortVector.h"
+#include "tier1/UtlStringMap.h"
+#include "tier1/UtlSymbol.h"
+#include "tier1/KeyValues.h"
+#include "tier1/lzss.h"
+#include "lzma/lzma.h"
+#include "datamap.h"
+#include "XZipTool.h"
+#include "../../common/scriplib.h"
+#include "../../common/cmdlib.h"
+#include "tier2/tier2.h"
+#include "dmserializers/idmserializers.h"
+#include "datamodel/dmattribute.h"
+#include "datamodel/dmelement.h"
+#include "studiobyteswap.h"
+#include "studio.h"
+#include "vphysics_interface.h"
+#include "materialsystem/IMaterial.h"
+#include "materialsystem/hardwareverts.h"
+#include "optimize.h"
+#include "soundchars.h"
+#include "mdllib/mdllib.h"
+
+enum fileType_e
+{
+ FILETYPE_UNKNOWN = 0,
+ FILETYPE_WAV,
+ FILETYPE_VTF,
+ FILETYPE_MDL,
+ FILETYPE_ANI,
+ FILETYPE_VTX,
+ FILETYPE_VVD,
+ FILETYPE_PHY,
+ FILETYPE_BSP,
+ FILETYPE_AIN,
+ FILETYPE_CCDAT,
+ FILETYPE_MP3,
+ FILETYPE_RESLST,
+ FILETYPE_PCF,
+ FILETYPE_SCENEIMAGE
+};
+
+typedef struct
+{
+ int result;
+ CUtlString fileName;
+} errorList_t;
+
+//-----------------------------------------------------------------------------
+// MakeGameData.cpp
+//-----------------------------------------------------------------------------
+
+
+bool IsLocalizedFile( const char *pFileName );
+bool IsLocalizedFileValid( const char *pFileName, const char *pLanguageSuffix = NULL );
+bool CompressCallback( CUtlBuffer &inputBuffer, CUtlBuffer &outputBuffer );
+bool ReadFileToBuffer( const char *pSourceName, CUtlBuffer &buffer, bool bText, bool bNoOpenFailureWarning );
+bool WriteBufferToFile( const char *pTargetName, CUtlBuffer &buffer, bool bWriteToZip, DiskWriteMode_t writeMode );
+fileType_e ResolveFileType( const char *pSourceName, char *pTargetName, int targetNameSize );
+
+extern DiskWriteMode_t g_WriteModeForConversions;
+extern CXZipTool g_MasterXZip;
+extern char g_szGamePath[];
+extern char g_szModPath[];
+extern bool g_bModPathIsValid;
+extern const char *g_GameNames[];
+extern bool g_bForce;
+extern bool g_bQuiet;
+extern bool g_bMakeZip;
+extern bool g_bIsPlatformZip;
+extern IPhysicsCollision *g_pPhysicsCollision;
+extern char g_szSourcePath[];
+extern CUtlVector< errorList_t > g_errorList;
+
+//-----------------------------------------------------------------------------
+// MakeTextures.cpp
+//-----------------------------------------------------------------------------
+bool CreateTargetFile_VTF( const char *pSourceName, const char *pTargetName, bool bWriteToZip );
+bool GetPreloadData_VTF( const char *pFilename, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut );
+
+//-----------------------------------------------------------------------------
+// MakeScenes.cpp
+//-----------------------------------------------------------------------------
+bool CreateSceneImageFile( char const *pchModPath, bool bWriteToZip, bool bLittleEndian, bool bQuiet, DiskWriteMode_t eWriteModeForConversions );
+
+//-----------------------------------------------------------------------------
+// MakeSounds.cpp
+//-----------------------------------------------------------------------------
+bool CreateTargetFile_WAV( const char *pSourceName, const char *pTargetName, bool bWriteToZip );
+bool CreateTargetFile_MP3( const char *pSourceName, const char *pTargetName, bool bWriteToZip );
+bool GetPreloadData_WAV( const char *pFilename, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut );
+
+//-----------------------------------------------------------------------------
+// MakeMaps.cpp
+//-----------------------------------------------------------------------------
+bool CreateTargetFile_BSP( const char *pSourceName, const char *pTargetName, bool bWriteToZip );
+bool CreateTargetFile_AIN( const char *pSourceName, const char *pTargetName, bool bWriteToZip );
+bool GetDependants_BSP( const char *pBspName, CUtlVector< CUtlString > *pList );
+
+//-----------------------------------------------------------------------------
+// MakeResources.cpp
+//-----------------------------------------------------------------------------
+bool CreateTargetFile_CCDAT( const char *pSourceName, const char *pTargetName, bool bWriteToZip );
+bool CreateTargetFile_RESLST( const char *pSourceName, const char *pTargetName, bool bWriteToZip );
+
+//-----------------------------------------------------------------------------
+// MakeModels.cpp
+//-----------------------------------------------------------------------------
+bool InitStudioByteSwap( void );
+void ShutdownStudioByteSwap( void );
+bool CreateTargetFile_Model( const char *pSourceName, const char *pTargetName, bool bWriteToZip );
+bool GetDependants_MDL( const char *pModelName, CUtlVector< CUtlString > *pList );
+bool GetPreloadData_VHV( const char *pFilename, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut );
+bool GetPreloadData_VTX( const char *pFilename, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut );
+bool GetPreloadData_VVD( const char *pFilename, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut );
+bool PreprocessModelFiles( CUtlVector<fileList_t> &fileList );
+
+//-----------------------------------------------------------------------------
+// MakeShaders.cpp
+//-----------------------------------------------------------------------------
+bool GetPreloadData_VCS( const char *pFilename, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut );
+
+//-----------------------------------------------------------------------------
+// MakeMisc.cpp
+//-----------------------------------------------------------------------------
+bool ProcessDXSupportConfig( bool bWriteToZip );
+
+//-----------------------------------------------------------------------------
+// MakeResources.cpp
+//-----------------------------------------------------------------------------
+bool CreateTargetFile_PCF( const char *pSourceName, const char *pTargetName, bool bWriteToZip );
+
+//-----------------------------------------------------------------------------
+// MakeZip.cpp
+//-----------------------------------------------------------------------------
+void SetupCriticalPreloadScript( const char *pModPath );
diff --git a/utils/xbox/MakeGameData/MakeGameData.vpc b/utils/xbox/MakeGameData/MakeGameData.vpc
new file mode 100644
index 0000000..315333c
--- /dev/null
+++ b/utils/xbox/MakeGameData/MakeGameData.vpc
@@ -0,0 +1,111 @@
+//-----------------------------------------------------------------------------
+// MAKEGAMEDATA.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_con_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $AdditionalIncludeDirectories "$BASE;$SRCDIR\x360xdk\include\win32\vs2005;$SRCDIR\game\shared"
+ $PreprocessorDefinitions "$BASE;NO_X360_XDK" [$VS2015]
+ }
+
+ $Linker
+ {
+ $AdditionalDependencies "$BASE xgraphics.lib d3d9.lib legacy_stdio_definitions.lib"
+ $AdditionalDependencies "$BASE xmaencoder.lib" [!$VS2015]
+ $AdditionalLibraryDirectories "$BASE;$SRCDIR\x360xdk\lib\win32\vs2005"
+ }
+}
+
+$Project "MakeGameData"
+{
+ $Folder "Source Files"
+ {
+ $File "MakeGameData.cpp"
+ $File "MakeMaps.cpp"
+ $File "MakeMisc.cpp"
+ $File "MakeModels.cpp"
+ $File "MakeParticles.cpp"
+ $File "MakeResources.cpp"
+ $File "MakeScenes.cpp"
+ $File "MakeShaders.cpp"
+ $File "MakeSounds.cpp"
+ $File "MakeTextures.cpp"
+ $File "MakeZip.cpp"
+
+ $Folder "Audio"
+ {
+ $File "imaadpcm.cpp"
+ $File "sound_io.cpp"
+ $File "resample.cpp"
+ }
+
+ $Folder "Public Modules"
+ {
+ $File "$SRCDIR\common\compiledcaptionswap.cpp"
+ $file "$SRCDIR\common\studiobyteswap.cpp"
+ $File "$SRCDIR\utils\common\scriplib.cpp"
+ $File "$SRCDIR\public\zip_utils.cpp"
+ $File "$SRCDIR\public\sentence.cpp"
+ $File "$SRCDIR\utils\common\cmdlib.cpp"
+ $File "$SRCDIR\public\filesystem_helpers.cpp"
+ $File "$SRCDIR\public\filesystem_init.cpp"
+ $File "$SRCDIR\utils\common\filesystem_tools.cpp"
+ $File "$SRCDIR\public\interpolatortypes.cpp"
+ }
+ }
+
+ $Folder "Header Files"
+ {
+ $File "MakeGameData.h"
+ $File "XZipTool.h"
+ $File "imaadpcm.h"
+ $File "resample.h"
+ $File "$SRCDIR\public\captioncompiler.h"
+ $File "$SRCDIR\common\studiobyteswap.h"
+ $File "$SRCDIR\utils\common\scriplib.h"
+ $File "$SRCDIR\public\filesystem.h"
+ $File "$SRCDIR\public\ibsppack.h"
+ $File "$SRCDIR\public\sentence.h"
+ $File "$SRCDIR\public\studio.h"
+ $File "$SRCDIR\public\tier1\utlbuffer.h"
+ $File "$SRCDIR\public\tier1\strtools.h"
+ $File "$SRCDIR\public\vphysics_interface.h"
+ $File "$SRCDIR\public\vstdlib\vstdlib.h"
+ $File "$SRCDIR\public\xwvfile.h"
+ $File "$SRCDIR\public\zip_utils.h"
+ $File "$SRCDIR\game\shared\choreoscene.h"
+ $File "$SRCDIR\game\shared\choreoactor.h"
+ $File "$SRCDIR\public\filesystem_helpers.h"
+ $File "$SRCDIR\public\filesystem_init.h"
+ $File "$SRCDIR\utils\common\filesystem_tools.h"
+ $File "$SRCDIR\public\interpolatortypes.h"
+ $File "$SRCDIR\utils\common\cmdlib.h"
+ $File "$SRCDIR\game\shared\choreochannel.h"
+ $File "$SRCDIR\game\shared\choreoevent.h"
+ $File "$SRCDIR\public\tier1\checksum_crc.h"
+ $File "$SRCDIR\public\tier2\tier2.h"
+ $File "$SRCDIR\common\lzma\lzma.h"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib appframework
+ $Lib mathlib
+ $Lib vtf
+ $Lib tier2
+ $Lib choreoobjects
+ $Lib bitmap
+ $Lib datamodel
+ $Lib dmserializers
+ $Lib $LIBCOMMON\lzma
+ }
+}
diff --git a/utils/xbox/MakeGameData/MakeMaps.cpp b/utils/xbox/MakeGameData/MakeMaps.cpp
new file mode 100644
index 0000000..618e5f6
--- /dev/null
+++ b/utils/xbox/MakeGameData/MakeMaps.cpp
@@ -0,0 +1,383 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: .360 Creation for all studiomdl generated files (mdl, vvd, vtx, ani, phy)
+//
+//=====================================================================================//
+
+#include "MakeGameData.h"
+#include "filesystem.h"
+#include "../../common/bsplib.h"
+#include "ibsppack.h"
+#include "vtf/vtf.h"
+#include "../../game/server/ai_hull.h"
+#include "zip_utils.h"
+
+#define AINET_VERSION_NUMBER 37
+#define MAX_NODES 1500
+
+bool ReadBSPHeader( const char *pFilename, dheader_t *pHeader )
+{
+ V_memset( pHeader, 0, sizeof( dheader_t ) );
+
+ int handle = _open( pFilename, _O_RDONLY|_O_BINARY );
+ if ( handle == -1 )
+ {
+ return false;
+ }
+
+ _read( handle, pHeader, sizeof( dheader_t ) );
+ close( handle );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Run possible lod culling fixup
+//-----------------------------------------------------------------------------
+bool ConvertVHV( const char *pVhvFilename, const char *pModelName, CUtlBuffer &sourceBuffer, CUtlBuffer &targetBuffer )
+{
+ // find strip info from model
+ char vsiFilename[MAX_PATH];
+ V_strncpy( vsiFilename, pModelName, sizeof( vsiFilename ) );
+ V_SetExtension( vsiFilename, ".vsi", sizeof( vsiFilename ) );
+
+ CUtlBuffer vsiBuffer;
+ if ( !g_pFullFileSystem->ReadFile( vsiFilename, NULL, vsiBuffer ) )
+ {
+ // cannot convert bsp's without model converions
+ Msg( "Error! Missing expected model conversion file '%s'. Cannot perform VHV fixup.\n", vsiFilename );
+ return false;
+ }
+
+ IMdlStripInfo *pMdlStripInfo = NULL;
+ if ( !mdllib->CreateNewStripInfo( &pMdlStripInfo ) )
+ {
+ Msg( "Error! Failed to allocate strip info object\n" );
+ return false;
+ }
+
+ if ( !pMdlStripInfo->UnSerialize( vsiBuffer ) )
+ {
+ Msg( "Error! Failed to unserialize strip info object '%s'\n", vsiFilename );
+ pMdlStripInfo->DeleteThis();
+ return false;
+ }
+
+ long originalChecksum = 0;
+ long newChecksum = 0;
+ if ( !pMdlStripInfo->GetCheckSum( &originalChecksum, &newChecksum ) )
+ {
+ Msg( "Error! Failed to get checksums from '%s'\n", vsiFilename );
+ pMdlStripInfo->DeleteThis();
+ return false;
+ }
+
+ HardwareVerts::FileHeader_t *pVHVhdr = (HardwareVerts::FileHeader_t*)sourceBuffer.Base();
+ if ( pVHVhdr->m_nChecksum != originalChecksum )
+ {
+ // vhv file should have matching original checksums
+ Msg( "Error! Mismatched checksums from '%s' and '%s'\n", vsiFilename, pVhvFilename );
+ pMdlStripInfo->DeleteThis();
+ return false;
+ }
+
+ targetBuffer.EnsureCapacity( sourceBuffer.TellMaxPut() );
+ targetBuffer.Put( sourceBuffer.Base(), sourceBuffer.TellMaxPut() );
+ if ( !pMdlStripInfo->StripHardwareVertsBuffer( targetBuffer ) )
+ {
+ pMdlStripInfo->DeleteThis();
+ return false;
+ }
+
+ // success
+ pMdlStripInfo->DeleteThis();
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate .360 bsp
+//-----------------------------------------------------------------------------
+bool CreateTargetFile_BSP( const char *pSourceName, const char *pTargetName, bool bWriteToZip )
+{
+ CUtlBuffer targetBuffer;
+ CUtlBuffer zipBuffer;
+ CUtlBuffer fileBuffer;
+ CUtlBuffer tempBuffer;
+ char tempZipName[MAX_PATH];
+ char tempSwapName[MAX_PATH];
+
+ tempZipName[0] = '\0';
+ tempSwapName[0] = '\0';
+ void *pPakData = NULL;
+ int pakSize = 0;
+ CXZipTool *pNewXZip = NULL;
+
+ if ( !g_bModPathIsValid )
+ {
+ Msg( "Indeterminate mod path, Cannot perform BSP conversion\n" );
+ return false;
+ }
+
+ // Load bsppack.dll
+ void *pBSPPack;
+ CSysModule *pBSPModule;
+ if ( !Sys_LoadInterface( "bsppack.dll", IBSPPACK_VERSION_STRING, &pBSPModule, &pBSPPack ) )
+ {
+ Msg( "Failed to load bsppack interface\n" );
+ return false;
+ }
+
+ scriptlib->MakeTemporaryFilename( g_szModPath, tempSwapName, sizeof( tempSwapName ) );
+
+ // Swaps the bsp directly to disk
+ bool bOK = ((IBSPPack*)pBSPPack)->SwapBSPFile( g_pFullFileSystem, pSourceName, tempSwapName, false, ConvertVTFTo360Format, ConvertVHV, CompressCallback );
+ if ( !bOK )
+ {
+ goto cleanUp;
+ }
+
+ // get the pak file from the swapped bsp
+ bOK = ((IBSPPack*)pBSPPack)->GetPakFileLump( g_pFullFileSystem, tempSwapName, &pPakData, &pakSize );
+ if ( !bOK )
+ {
+ goto cleanUp;
+ }
+
+ // build an xzip version of the pak file
+ if ( pPakData && pakSize )
+ {
+ // mount current pak file
+ IZip *pOldZip = IZip::CreateZip( false, true );
+ pOldZip->ParseFromBuffer( pPakData, pakSize );
+
+ // start a new xzip version
+ scriptlib->MakeTemporaryFilename( g_szModPath, tempZipName, sizeof( tempZipName ) );
+
+ pNewXZip = new CXZipTool;
+ pNewXZip->Begin( tempZipName, XBOX_DVD_SECTORSIZE );
+
+ // iterate each file in existing zip, add to new zip
+ int zipIndex = -1;
+ for ( ;; )
+ {
+ char filename[MAX_PATH];
+ filename[0] = '\0';
+ int fileSize = 0;
+ zipIndex = pOldZip->GetNextFilename( zipIndex, filename, sizeof( filename ), fileSize );
+ if ( zipIndex == -1 )
+ {
+ break;
+ }
+
+ fileBuffer.Purge();
+ bOK = pOldZip->ReadFileFromZip( filename, false, fileBuffer );
+ if ( !bOK )
+ {
+ goto cleanUp;
+ }
+
+ bOK = pNewXZip->AddBuffer( filename, fileBuffer, true );
+ if ( !bOK )
+ {
+ goto cleanUp;
+ }
+ }
+
+ IZip::ReleaseZip( pOldZip );
+ pNewXZip->End();
+
+ // read the new zip into memory
+ bOK = scriptlib->ReadFileToBuffer( tempZipName, zipBuffer );
+ if ( !bOK )
+ {
+ goto cleanUp;
+ }
+
+ // replace old pak lump with new zip
+ bOK = ((IBSPPack*)pBSPPack)->SetPakFileLump( g_pFullFileSystem, tempSwapName, tempSwapName, zipBuffer.Base(), zipBuffer.TellMaxPut() );
+ if ( !bOK )
+ {
+ goto cleanUp;
+ }
+ }
+
+ bOK = scriptlib->ReadFileToBuffer( tempSwapName, targetBuffer );
+ if ( !bOK )
+ {
+ goto cleanUp;
+ }
+
+ // never zip, always write local file
+ bOK = WriteBufferToFile( pTargetName, targetBuffer, false, WRITE_TO_DISK_ALWAYS );
+
+cleanUp:
+ if ( tempZipName[0] )
+ {
+ _unlink( tempZipName );
+ }
+ if ( tempSwapName[0] )
+ {
+ _unlink( tempSwapName );
+ }
+
+ Sys_UnloadModule( pBSPModule );
+
+ if ( pPakData )
+ {
+ free( pPakData );
+ }
+
+ delete pNewXZip;
+
+ return bOK;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate .360 node graphs
+//-----------------------------------------------------------------------------
+bool CreateTargetFile_AIN( const char *pSourceName, const char *pTargetName, bool bWriteToZip )
+{
+ CUtlBuffer sourceBuf;
+ if ( !scriptlib->ReadFileToBuffer( pSourceName, sourceBuf ) )
+ {
+ return false;
+ }
+
+ // the pc ain is tied to the pc bsp and should have been generated after the bsp
+ char szBspName[MAX_PATH];
+ char szBspPath[MAX_PATH];
+ V_FileBase( pSourceName, szBspName, sizeof( szBspName ) );
+ V_ExtractFilePath( pSourceName, szBspPath, sizeof( szBspPath ) );
+ V_AppendSlash( szBspPath, sizeof( szBspPath ) );
+ V_strncat( szBspPath, "..\\", sizeof( szBspPath ) );
+ V_strncat( szBspPath, szBspName, sizeof( szBspPath ) );
+ V_strncat( szBspPath, ".bsp", sizeof( szBspPath ) );
+ if ( scriptlib->CompareFileTime( pSourceName, szBspPath ) < 0 )
+ {
+ // ain has a smaller filetime, thus older than bsp
+ Msg( "%s: Need to regenerate PC nodegraph (stale)\n", pSourceName );
+ if ( !g_bForce )
+ {
+ return false;
+ }
+ }
+
+ // Check the version
+ if ( sourceBuf.GetChar() == 'V' && sourceBuf.GetChar() == 'e' && sourceBuf.GetChar() == 'r' )
+ {
+ Msg( "%s: Need to regenerate PC nodegraph (bad format)\n", pSourceName );
+ return false;
+ }
+
+ // reset
+ sourceBuf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+
+ // Version number
+ int version = sourceBuf.GetInt();
+ if ( version != AINET_VERSION_NUMBER )
+ {
+ Msg( "%s: Need to regenerate PC nodegraph (got version '%d', expected '%d')\n", pSourceName, version, AINET_VERSION_NUMBER );
+ return false;
+ }
+
+ // check the map revision
+ int mapVersion = sourceBuf.GetInt();
+ dheader_t bspHeader;
+ if ( ReadBSPHeader( szBspPath, &bspHeader ) )
+ {
+ if ( mapVersion != bspHeader.mapRevision )
+ {
+ Msg( "%s: Need to regenerate PC nodegraph (ai revision '%d' does not match bsp revision '%d')\n", pSourceName, mapVersion, bspHeader.mapRevision );
+ return false;
+ }
+ }
+ else
+ {
+ Msg( "%s: Could not find expected bsp '%s'\n", pSourceName, szBspPath );
+ }
+
+ // Nodes
+ int nodeCt = sourceBuf.GetInt();
+ if ( nodeCt > MAX_NODES || nodeCt < 0 )
+ {
+ Msg( "%s: Need to regenerate PC nodegraph (corrupt)\n", pSourceName );
+ return false;
+ }
+
+ CUtlBuffer targetBuf;
+ targetBuf.ActivateByteSwapping( true );
+
+ CByteswap swap;
+ swap.ActivateByteSwapping( true );
+
+ targetBuf.PutInt( version );
+ targetBuf.PutInt( mapVersion );
+ targetBuf.PutInt( nodeCt );
+
+ int numFloats = NUM_HULLS + 4;
+ for ( int node = 0; node < nodeCt; ++node )
+ {
+ targetBuf.EnsureCapacity( targetBuf.TellPut() + numFloats * sizeof( float ) );
+ swap.SwapBufferToTargetEndian<float>( (float*)targetBuf.PeekPut(), (float*)sourceBuf.PeekGet(), numFloats );
+ sourceBuf.SeekGet( CUtlBuffer::SEEK_CURRENT, numFloats * sizeof( float ) );
+ targetBuf.SeekPut( CUtlBuffer::SEEK_CURRENT, numFloats * sizeof( float ) );
+
+ targetBuf.PutChar( sourceBuf.GetChar() );
+
+ // Align the remaining data
+ targetBuf.SeekPut( CUtlBuffer::SEEK_CURRENT, 3 );
+
+ targetBuf.PutUnsignedShort( sourceBuf.GetUnsignedShort() );
+ targetBuf.PutShort( sourceBuf.GetShort() );
+ }
+
+ // Node links
+ int totalNumLinks = sourceBuf.GetInt();
+ targetBuf.PutInt( totalNumLinks );
+
+ for ( int link = 0; link < totalNumLinks; ++link )
+ {
+ targetBuf.PutShort( sourceBuf.GetShort() );
+ targetBuf.PutShort( sourceBuf.GetShort() );
+ targetBuf.Put( sourceBuf.PeekGet(), NUM_HULLS );
+ sourceBuf.SeekGet( CUtlBuffer::SEEK_CURRENT, NUM_HULLS );
+ }
+
+ // WC lookup table
+ targetBuf.EnsureCapacity( targetBuf.TellPut() + nodeCt * sizeof( int ) );
+ swap.SwapBufferToTargetEndian<int>( (int*)targetBuf.PeekPut(), (int*)sourceBuf.PeekGet(), nodeCt );
+ targetBuf.SeekPut( CUtlBuffer::SEEK_CURRENT, nodeCt * sizeof( int ) );
+
+ // Write the file out
+ return WriteBufferToFile( pTargetName, targetBuf, bWriteToZip, WRITE_TO_DISK_ALWAYS );
+}
+
+bool GetDependants_BSP( const char *pBspName, CUtlVector< CUtlString > *pList )
+{
+ if ( !g_bModPathIsValid )
+ {
+ Msg( "Indeterminate mod path, Cannot perform BSP conversion\n" );
+ return false;
+ }
+
+ // Load bsppack.dll
+ void *pBSPPack;
+ CSysModule *pBSPModule;
+ if ( !Sys_LoadInterface( "bsppack.dll", IBSPPACK_VERSION_STRING, &pBSPModule, &pBSPPack ) )
+ {
+ Msg( "Failed to load bsppack interface\n" );
+ return false;
+ }
+
+ // 360 builds a more complete reslist that includes bsp internal files
+ // build full path to bsp file
+ char szBspFilename[MAX_PATH];
+ V_ComposeFileName( g_szGamePath, pBspName, szBspFilename, sizeof( szBspFilename ) );
+
+ bool bOK = ((IBSPPack*)pBSPPack)->GetBSPDependants( g_pFullFileSystem, szBspFilename, pList );
+
+ Sys_UnloadModule( pBSPModule );
+
+ return bOK;
+}
diff --git a/utils/xbox/MakeGameData/MakeMisc.cpp b/utils/xbox/MakeGameData/MakeMisc.cpp
new file mode 100644
index 0000000..d5e88de
--- /dev/null
+++ b/utils/xbox/MakeGameData/MakeMisc.cpp
@@ -0,0 +1,86 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Conversion for general wierd files.
+//
+//=====================================================================================//
+
+#include "MakeGameData.h"
+
+//-----------------------------------------------------------------------------
+// The DX Support file is a very fat expensive KV file, causes a run-time startup slowdown.
+// Becauase it normally lives in the game\bin directory, it can't be in the zip or preloaded.
+// Thus, it gets reprocessed into just the trivial 360 portion and placed into the platform.zip
+// Yes, it's evil.
+//-----------------------------------------------------------------------------
+bool ProcessDXSupportConfig( bool bWriteToZip )
+{
+ if ( !g_bIsPlatformZip )
+ {
+ // only relevant when building platform zip, otherwise no-op
+ return false;
+ }
+
+ const char *pConfigName = "dxsupport.cfg";
+ char szTempPath[MAX_PATH];
+ char szSourcePath[MAX_PATH];
+ V_ComposeFileName( g_szModPath, "../bin", szTempPath, sizeof( szTempPath ) );
+ V_ComposeFileName( szTempPath, pConfigName, szSourcePath, sizeof( szSourcePath ) );
+
+ CUtlBuffer sourceBuf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+ if ( !g_pFullFileSystem->ReadFile( szSourcePath, NULL, sourceBuf ) )
+ {
+ Msg( "Error! Couldn't open file '%s'!\n", pConfigName );
+ return false;
+ }
+
+ KeyValues *pKV = new KeyValues( "" );
+ if ( !pKV->LoadFromBuffer( "dxsupport.cfg", sourceBuf ) )
+ {
+ Msg( "Error! Couldn't parse config file '%s'!\n", pConfigName );
+ pKV->deleteThis();
+ return false;
+ }
+
+ // only care about the xbox specific dxlevel 98 block
+ KeyValues *pXboxSubKey = NULL;
+ for ( KeyValues *pSubKey = pKV->GetFirstSubKey(); pSubKey != NULL && pXboxSubKey == NULL; pSubKey = pSubKey->GetNextKey() )
+ {
+ // descend each sub block
+ for ( KeyValues *pKey = pSubKey->GetFirstSubKey(); pKey != NULL && pXboxSubKey == NULL; pKey = pKey->GetNextKey() )
+ {
+ if ( !V_stricmp( pKey->GetName(), "name" ) && pKey->GetInt( (const char *)NULL ) == 98 )
+ {
+ pXboxSubKey = pSubKey;
+ }
+ }
+ }
+
+ if ( !pXboxSubKey )
+ {
+ Msg( "Error! Couldn't find expected dxlevel 98 in config file '%s'!\n", pConfigName );
+ pKV->deleteThis();
+ return false;
+ }
+
+ CUtlBuffer kvBuffer( 0, 0, CUtlBuffer::TEXT_BUFFER );
+ kvBuffer.Printf( "\"dxsupport\"\n" );
+ kvBuffer.Printf( "{\n" );
+ kvBuffer.Printf( "\t\"0\"\n" );
+ kvBuffer.Printf( "\t{\n" );
+ for ( KeyValues *pKey = pXboxSubKey->GetFirstSubKey(); pKey != NULL; pKey = pKey->GetNextKey() )
+ {
+ kvBuffer.Printf( "\t\t\"%s\" \"%s\"\n", pKey->GetName(), pKey->GetString( (const char *)NULL ) );
+ }
+ kvBuffer.Printf( "\t}\n" );
+ kvBuffer.Printf( "}\n" );
+
+ CUtlBuffer targetBuf( 0, 0, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::CONTAINS_CRLF );
+ kvBuffer.ConvertCRLF( targetBuf );
+
+ // only appears in zip file
+ bool bSuccess = WriteBufferToFile( pConfigName, targetBuf, bWriteToZip, WRITE_TO_DISK_NEVER );
+
+ pKV->deleteThis();
+
+ return bSuccess;
+} \ No newline at end of file
diff --git a/utils/xbox/MakeGameData/MakeModels.cpp b/utils/xbox/MakeGameData/MakeModels.cpp
new file mode 100644
index 0000000..21a14dc
--- /dev/null
+++ b/utils/xbox/MakeGameData/MakeModels.cpp
@@ -0,0 +1,614 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: .360 Creation for all studiomdl generated files (mdl, vvd, vtx, ani, phy)
+//
+//=====================================================================================//
+
+#include "MakeGameData.h"
+#include "studiobyteswap.h"
+#include "studio.h"
+#include "vphysics_interface.h"
+#include "materialsystem/IMaterial.h"
+#include "materialsystem/hardwareverts.h"
+#include "optimize.h"
+
+//-----------------------------------------------------------------------------
+// Models are already converted in a pre-pass.
+//-----------------------------------------------------------------------------
+bool CreateTargetFile_Model( const char *pSourceName, const char *pTargetName, bool bWriteToZip )
+{
+ // model component should be present
+ CUtlBuffer targetBuffer;
+ if ( !scriptlib->ReadFileToBuffer( pTargetName, targetBuffer ) )
+ {
+ return false;
+ }
+
+ // no conversion to write, but possibly zipped
+ bool bSuccess = WriteBufferToFile( pTargetName, targetBuffer, bWriteToZip, WRITE_TO_DISK_NEVER );
+ return bSuccess;
+}
+
+//-----------------------------------------------------------------------------
+// Load necessary dlls
+//-----------------------------------------------------------------------------
+bool InitStudioByteSwap( void )
+{
+ StudioByteSwap::SetVerbose( false );
+ StudioByteSwap::ActivateByteSwapping( true );
+ StudioByteSwap::SetCollisionInterface( g_pPhysicsCollision );
+
+ return true;
+}
+
+//----------------------------------------------------------------------
+// Get list of files that a model requires.
+//----------------------------------------------------------------------
+bool GetDependants_MDL( const char *pModelName, CUtlVector< CUtlString > *pList )
+{
+ if ( !g_bModPathIsValid )
+ {
+ Msg( "Indeterminate mod path, Cannot perform BSP conversion\n" );
+ return false;
+ }
+
+ CUtlBuffer sourceBuf;
+ if ( !g_pFullFileSystem->ReadFile( pModelName, "GAME", sourceBuf ) )
+ {
+ Msg( "Error! Couldn't open file '%s'!\n", pModelName );
+ return false;
+ }
+
+ studiohdr_t *pStudioHdr = (studiohdr_t *)sourceBuf.Base();
+ Studio_ConvertStudioHdrToNewVersion( pStudioHdr );
+ if ( pStudioHdr->version != STUDIO_VERSION )
+ {
+ Msg( "Error! Bad Model '%s', Expecting Version (%d), got (%d)\n", pModelName, STUDIO_VERSION, pStudioHdr->version );
+ return false;
+ }
+
+ char szOutName[MAX_PATH];
+ if ( pStudioHdr->flags & STUDIOHDR_FLAGS_OBSOLETE )
+ {
+ V_strncpy( szOutName, "materials/sprites/obsolete.vmt", sizeof( szOutName ) );
+ V_FixSlashes( szOutName );
+ pList->AddToTail( szOutName );
+ }
+ else if ( pStudioHdr->textureindex != 0 )
+ {
+ // iterate each texture
+ int i;
+ int j;
+ for ( i = 0; i < pStudioHdr->numtextures; i++ )
+ {
+ // iterate through all directories until a valid material is found
+ bool bFound = false;
+ for ( j = 0; j < pStudioHdr->numcdtextures; j++ )
+ {
+ char szPath[MAX_PATH];
+ V_ComposeFileName( "materials", pStudioHdr->pCdtexture( j ), szPath, sizeof( szPath ) );
+
+ // should have been fixed in studiomdl
+ // some mdls are ending up with double slashes, borking loads
+ int len = strlen( szPath );
+ if ( len > 2 && szPath[len-2] == '\\' && szPath[len-1] == '\\' )
+ {
+ szPath[len-1] = '\0';
+ }
+
+ V_ComposeFileName( szPath, pStudioHdr->pTexture( i )->pszName(), szOutName, sizeof( szOutName ) );
+ V_SetExtension( szOutName, ".vmt", sizeof( szOutName ) );
+
+ if ( g_pFullFileSystem->FileExists( szOutName, "GAME" ) )
+ {
+ bFound = true;
+ break;
+ }
+ }
+
+ if ( bFound )
+ {
+ pList->AddToTail( szOutName );
+ }
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Get the preload data for a vhv file
+//-----------------------------------------------------------------------------
+bool GetPreloadData_VHV( const char *pFilename, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut )
+{
+ HardwareVerts::FileHeader_t *pHeader = (HardwareVerts::FileHeader_t *)fileBufferIn.Base();
+
+ unsigned int version = BigLong( pHeader->m_nVersion );
+
+ // ensure caller's buffer is clean
+ // caller determines preload size, via TellMaxPut()
+ preloadBufferOut.Purge();
+
+ if ( version != VHV_VERSION )
+ {
+ // bad version
+ Msg( "Can't preload: '%s', expecting version %d got version %d\n", pFilename, VHV_VERSION, version );
+ return false;
+ }
+
+ unsigned int nPreloadSize = sizeof( HardwareVerts::FileHeader_t );
+
+ preloadBufferOut.Put( fileBufferIn.Base(), nPreloadSize );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Get the preload data for a vtx file
+//-----------------------------------------------------------------------------
+bool GetPreloadData_VTX( const char *pFilename, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut )
+{
+ OptimizedModel::FileHeader_t *pHeader = (OptimizedModel::FileHeader_t *)fileBufferIn.Base();
+
+ unsigned int version = BigLong( pHeader->version );
+
+ // ensure caller's buffer is clean
+ // caller determines preload size, via TellMaxPut()
+ preloadBufferOut.Purge();
+
+ if ( version != OPTIMIZED_MODEL_FILE_VERSION )
+ {
+ // bad version
+ Msg( "Can't preload: '%s', expecting version %d got version %d\n", pFilename, OPTIMIZED_MODEL_FILE_VERSION, version );
+ return false;
+ }
+
+ unsigned int nPreloadSize = sizeof( OptimizedModel::FileHeader_t );
+
+ preloadBufferOut.Put( fileBufferIn.Base(), nPreloadSize );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Get the preload data for a vvd file
+//-----------------------------------------------------------------------------
+bool GetPreloadData_VVD( const char *pFilename, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut )
+{
+ vertexFileHeader_t *pHeader = (vertexFileHeader_t *)fileBufferIn.Base();
+
+ unsigned int id = BigLong( pHeader->id );
+ unsigned int version = BigLong( pHeader->version );
+
+ // ensure caller's buffer is clean
+ // caller determines preload size, via TellMaxPut()
+ preloadBufferOut.Purge();
+
+ if ( id != MODEL_VERTEX_FILE_ID )
+ {
+ // bad version
+ Msg( "Can't preload: '%s', expecting id %d got id %d\n", pFilename, MODEL_VERTEX_FILE_ID, id );
+ return false;
+ }
+
+ if ( version != MODEL_VERTEX_FILE_VERSION )
+ {
+ // bad version
+ Msg( "Can't preload: '%s', expecting version %d got version %d\n", pFilename, MODEL_VERTEX_FILE_VERSION, version );
+ return false;
+ }
+
+ unsigned int nPreloadSize = sizeof( vertexFileHeader_t );
+
+ preloadBufferOut.Put( fileBufferIn.Base(), nPreloadSize );
+
+ return true;
+}
+
+bool CompressFunc( const void *pInput, int inputSize, void **pOutput, int *pOutputSize )
+{
+ *pOutput = NULL;
+ *pOutputSize = 0;
+
+ if ( !inputSize )
+ {
+ // nothing to do
+ return false;
+ }
+
+ unsigned int compressedSize;
+ unsigned char *pCompressedOutput = LZMA_OpportunisticCompress( (unsigned char *)pInput, inputSize, &compressedSize );
+ if ( pCompressedOutput )
+ {
+ *pOutput = pCompressedOutput;
+ *pOutputSize = compressedSize;
+ return true;
+ }
+
+ return false;
+
+}
+
+//-----------------------------------------------------------------------------
+// Rebuilds all of a MDL's components.
+//-----------------------------------------------------------------------------
+static bool GenerateModelFiles( const char *pMdlFilename )
+{
+ CUtlBuffer tempBuffer;
+ int fileSize;
+ int paddedSize;
+ int swappedSize;
+
+ // .mdl
+ CUtlBuffer mdlBuffer;
+ if ( !scriptlib->ReadFileToBuffer( pMdlFilename, mdlBuffer ) )
+ {
+ return false;
+ }
+ if ( !Studio_ConvertStudioHdrToNewVersion( (studiohdr_t *)mdlBuffer.Base() ))
+ {
+ Msg("%s needs to be recompiled\n", pMdlFilename );
+ }
+
+ // .vtx
+ char szVtxFilename[MAX_PATH];
+ V_StripExtension( pMdlFilename, szVtxFilename, sizeof( szVtxFilename ) );
+ V_strncat( szVtxFilename, ".dx90.vtx", sizeof( szVtxFilename ) );
+ CUtlBuffer vtxBuffer;
+ bool bHasVtx = ReadFileToBuffer( szVtxFilename, vtxBuffer, false, true );
+
+ // .vvd
+ char szVvdFilename[MAX_PATH];
+ V_StripExtension( pMdlFilename, szVvdFilename, sizeof( szVvdFilename ) );
+ V_strncat( szVvdFilename, ".vvd", sizeof( szVvdFilename ) );
+ CUtlBuffer vvdBuffer;
+ bool bHasVvd = ReadFileToBuffer( szVvdFilename, vvdBuffer, false, true );
+
+ if ( bHasVtx != bHasVvd )
+ {
+ // paired resources, either mandates the other
+ return false;
+ }
+
+ // a .mdl file that has .vtx/.vvd gets re-processed to cull lod data
+ if ( bHasVtx && bHasVvd )
+ {
+ // cull lod if needed
+ IMdlStripInfo *pStripInfo = NULL;
+ bool bResult = mdllib->StripModelBuffers( mdlBuffer, vvdBuffer, vtxBuffer, &pStripInfo );
+ if ( !bResult )
+ {
+ return false;
+ }
+ if ( pStripInfo )
+ {
+ // .vsi
+ CUtlBuffer vsiBuffer;
+ pStripInfo->Serialize( vsiBuffer );
+ pStripInfo->DeleteThis();
+
+ // save strip info for later processing
+ char szVsiFilename[MAX_PATH];
+ V_StripExtension( pMdlFilename, szVsiFilename, sizeof( szVsiFilename ) );
+ V_strncat( szVsiFilename, ".vsi", sizeof( szVsiFilename ) );
+ WriteBufferToFile( szVsiFilename, vsiBuffer, false, WRITE_TO_DISK_ALWAYS );
+ }
+ }
+
+ // .ani processing may further update .mdl buffer
+ char szAniFilename[MAX_PATH];
+ V_StripExtension( pMdlFilename, szAniFilename, sizeof( szAniFilename ) );
+ V_strncat( szAniFilename, ".ani", sizeof( szAniFilename ) );
+ CUtlBuffer aniBuffer;
+ bool bHasAni = ReadFileToBuffer( szAniFilename, aniBuffer, false, true );
+ if ( bHasAni )
+ {
+ // Some vestigal .ani files exist in the tree, only process valid .ani
+ if ( ((studiohdr_t*)mdlBuffer.Base())->numanimblocks != 0 )
+ {
+ // .ani processing modifies .mdl buffer
+ fileSize = aniBuffer.TellPut();
+ paddedSize = fileSize + BYTESWAP_ALIGNMENT_PADDING;
+ aniBuffer.EnsureCapacity( paddedSize );
+ tempBuffer.EnsureCapacity( paddedSize );
+ V_StripExtension( pMdlFilename, szAniFilename, sizeof( szAniFilename ) );
+ V_strncat( szAniFilename, ".360.ani", sizeof( szAniFilename ) );
+ swappedSize = StudioByteSwap::ByteswapStudioFile( szAniFilename, tempBuffer.Base(), aniBuffer.PeekGet(), fileSize, (studiohdr_t*)mdlBuffer.Base(), CompressFunc );
+ if ( swappedSize > 0 )
+ {
+ // .ani buffer is replaced with swapped data
+ aniBuffer.Purge();
+ aniBuffer.Put( tempBuffer.Base(), swappedSize );
+ WriteBufferToFile( szAniFilename, aniBuffer, false, WRITE_TO_DISK_ALWAYS );
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ // .phy
+ char szPhyFilename[MAX_PATH];
+ V_StripExtension( pMdlFilename, szPhyFilename, sizeof( szPhyFilename ) );
+ V_strncat( szPhyFilename, ".phy", sizeof( szPhyFilename ) );
+ CUtlBuffer phyBuffer;
+ bool bHasPhy = ReadFileToBuffer( szPhyFilename, phyBuffer, false, true );
+ if ( bHasPhy )
+ {
+ fileSize = phyBuffer.TellPut();
+ paddedSize = fileSize + BYTESWAP_ALIGNMENT_PADDING;
+ phyBuffer.EnsureCapacity( paddedSize );
+ tempBuffer.EnsureCapacity( paddedSize );
+ V_StripExtension( pMdlFilename, szPhyFilename, sizeof( szPhyFilename ) );
+ V_strncat( szPhyFilename, ".360.phy", sizeof( szPhyFilename ) );
+ swappedSize = StudioByteSwap::ByteswapStudioFile( szPhyFilename, tempBuffer.Base(), phyBuffer.PeekGet(), fileSize, (studiohdr_t*)mdlBuffer.Base(), CompressFunc );
+ if ( swappedSize > 0 )
+ {
+ // .phy buffer is replaced with swapped data
+ phyBuffer.Purge();
+ phyBuffer.Put( tempBuffer.Base(), swappedSize );
+ WriteBufferToFile( szPhyFilename, phyBuffer, false, WRITE_TO_DISK_ALWAYS );
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ if ( bHasVtx )
+ {
+ fileSize = vtxBuffer.TellPut();
+ paddedSize = fileSize + BYTESWAP_ALIGNMENT_PADDING;
+ vtxBuffer.EnsureCapacity( paddedSize );
+ tempBuffer.EnsureCapacity( paddedSize );
+ V_StripExtension( pMdlFilename, szVtxFilename, sizeof( szVtxFilename ) );
+ V_strncat( szVtxFilename, ".dx90.360.vtx", sizeof( szVtxFilename ) );
+ swappedSize = StudioByteSwap::ByteswapStudioFile( szVtxFilename, tempBuffer.Base(), vtxBuffer.PeekGet(), fileSize, (studiohdr_t*)mdlBuffer.Base(), CompressFunc );
+ if ( swappedSize > 0 )
+ {
+ // .vtx buffer is replaced with swapped data
+ vtxBuffer.Purge();
+ vtxBuffer.Put( tempBuffer.Base(), swappedSize );
+ WriteBufferToFile( szVtxFilename, vtxBuffer, false, WRITE_TO_DISK_ALWAYS );
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ if ( bHasVvd )
+ {
+ fileSize = vvdBuffer.TellPut();
+ paddedSize = fileSize + BYTESWAP_ALIGNMENT_PADDING;
+ vvdBuffer.EnsureCapacity( paddedSize );
+ tempBuffer.EnsureCapacity( paddedSize );
+ V_StripExtension( pMdlFilename, szVvdFilename, sizeof( szVvdFilename ) );
+ V_strncat( szVvdFilename, ".360.vvd", sizeof( szVvdFilename ) );
+ swappedSize = StudioByteSwap::ByteswapStudioFile( szVvdFilename, tempBuffer.Base(), vvdBuffer.PeekGet(), fileSize, (studiohdr_t*)mdlBuffer.Base(), CompressFunc );
+ if ( swappedSize > 0 )
+ {
+ // .vvd buffer is replaced with swapped data
+ vvdBuffer.Purge();
+ vvdBuffer.Put( tempBuffer.Base(), swappedSize );
+ WriteBufferToFile( szVvdFilename, vvdBuffer, false, WRITE_TO_DISK_ALWAYS );
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // swap and write final .mdl
+ fileSize = mdlBuffer.TellPut();
+ paddedSize = fileSize + BYTESWAP_ALIGNMENT_PADDING;
+ mdlBuffer.EnsureCapacity( paddedSize );
+ tempBuffer.EnsureCapacity( paddedSize );
+ char szMdlFilename[MAX_PATH];
+ V_StripExtension( pMdlFilename, szMdlFilename, sizeof( szMdlFilename ) );
+ V_strncat( szMdlFilename, ".360.mdl", sizeof( szMdlFilename ) );
+ swappedSize = StudioByteSwap::ByteswapStudioFile( szMdlFilename, tempBuffer.Base(), mdlBuffer.PeekGet(), fileSize, NULL, CompressFunc );
+ if ( swappedSize > 0 )
+ {
+ // .mdl buffer is replaced with swapped data
+ mdlBuffer.Purge();
+ mdlBuffer.Put( tempBuffer.Base(), swappedSize );
+ WriteBufferToFile( szMdlFilename, mdlBuffer, false, WRITE_TO_DISK_ALWAYS );
+ }
+ else
+ {
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Returns true if specified model path has a dirty sub-component, and requires
+// update.
+//-----------------------------------------------------------------------------
+static bool ModelNeedsUpdate( const char *pMdlSourcePath )
+{
+ struct ModelExtensions_t
+ {
+ const char *pSourceExtension;
+ const char *pTargetExtension;
+ bool bSourceMustExist; // if source exists, so must target
+ };
+ ModelExtensions_t pExtensions[] =
+ {
+ { ".mdl", ".360.mdl", true },
+ { ".dx90.vtx", ".dx90.360.vtx", false },
+ { ".vvd", ".360.vvd", false },
+ { ".phy", ".360.phy", false },
+ { ".ani", ".360.ani", false },
+ // vtx/vvd generate a vsi, vsi must be fresher to be valid
+ { ".dx90.vtx", ".vsi", false },
+ { ".vvd", ".vsi", false },
+ };
+
+ if ( g_bForce )
+ {
+ return true;
+ }
+
+ for ( int i = 0; i < ARRAYSIZE( pExtensions ); i++ )
+ {
+ char szSourcePath[MAX_PATH];
+ struct _stat sourceStatBuf;
+ V_strncpy( szSourcePath, pMdlSourcePath, sizeof( szSourcePath ) );
+ V_SetExtension( szSourcePath, pExtensions[i].pSourceExtension, sizeof( szSourcePath ) );
+ int retVal = _stat( szSourcePath, &sourceStatBuf );
+ if ( retVal != 0 )
+ {
+ // couldn't get source
+ if ( pExtensions[i].bSourceMustExist )
+ {
+ return true;
+ }
+ else
+ {
+ // source is optional
+ continue;
+ }
+ }
+
+ char szTargetPath[MAX_PATH];
+ struct _stat targetStatBuf;
+ V_strncpy( szTargetPath, pMdlSourcePath, sizeof( szTargetPath ) );
+ V_SetExtension( szTargetPath, pExtensions[i].pTargetExtension, sizeof( szTargetPath ) );
+ if ( _stat( szTargetPath, &targetStatBuf ) != 0 )
+ {
+ // target doesn't exist
+ return true;
+ }
+
+ if ( difftime( sourceStatBuf.st_mtime, targetStatBuf.st_mtime ) > 0 )
+ {
+ // source is older (thus newer), update required
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool ModelNamesLessFunc( CUtlString const &pLHS, CUtlString const &pRHS )
+{
+ return CaselessStringLessThan( pLHS.Get(), pRHS.Get() );
+}
+
+//-----------------------------------------------------------------------------
+// Models require specialized group handling to generate intermediate lod culled
+// versions that are then used as the the source for target conversion.
+//-----------------------------------------------------------------------------
+bool PreprocessModelFiles( CUtlVector<fileList_t> &fileList )
+{
+ if ( !InitStudioByteSwap() )
+ {
+ return false;
+ }
+
+ CUtlVector< CUtlString > updateList;
+ CUtlRBTree< CUtlString, int > visitedModels( 0, 0, ModelNamesLessFunc );
+
+ char szSourcePath[MAX_PATH];
+ strcpy( szSourcePath, g_szSourcePath );
+ V_StripFilename( szSourcePath );
+ if ( !szSourcePath[0] )
+ strcpy( szSourcePath, "." );
+ V_AppendSlash( szSourcePath, sizeof( szSourcePath ) );
+
+ char szModelName[MAX_PATH];
+ for ( int i=0; i<fileList.Count(); i++ )
+ {
+ V_strncpy( szModelName, fileList[i].fileName.String(), sizeof( szModelName ) );
+
+ if ( V_stristr( szModelName, ".360." ) )
+ {
+ // must ignore any target files
+ continue;
+ }
+
+ // want only model related files
+ char *pExtension = V_stristr( szModelName, ".mdl" );
+ if ( !pExtension )
+ {
+ pExtension = V_stristr( szModelName, ".dx90.vtx" );
+ if ( !pExtension )
+ {
+ pExtension = V_stristr( szModelName, ".vvd" );
+ if ( !pExtension )
+ {
+ pExtension = V_stristr( szModelName, ".ani" );
+ if ( !pExtension )
+ {
+ pExtension = V_stristr( szModelName, ".phy" );
+ if ( !pExtension )
+ {
+ pExtension = V_stristr( szModelName, ".vsi" );
+ if ( !pExtension )
+ {
+ continue;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ *pExtension = '\0';
+ V_strncat( szModelName, ".mdl", sizeof( szModelName ) );
+
+ if ( visitedModels.Find( szModelName ) != visitedModels.InvalidIndex() )
+ {
+ // already processed
+ continue;
+ }
+ visitedModels.Insert( szModelName );
+
+ // resolve to full source path
+ const char *ptr = szModelName;
+ if ( !strnicmp( ptr, ".\\", 2 ) )
+ ptr += 2;
+ else if ( !strnicmp( ptr, szSourcePath, strlen( szSourcePath ) ) )
+ ptr += strlen( szSourcePath );
+ char szCleanName[MAX_PATH];
+ strcpy( szCleanName, szSourcePath );
+ strcat( szCleanName, ptr );
+ char szFullSourcePath[MAX_PATH];
+ _fullpath( szFullSourcePath, szCleanName, sizeof( szFullSourcePath ) );
+
+ // any one dirty component generates the set of all expected files
+ if ( ModelNeedsUpdate( szFullSourcePath ) )
+ {
+ int index = updateList.AddToTail();
+ updateList[index].Set( szFullSourcePath );
+ }
+ }
+
+ Msg( "\n" );
+ Msg( "Model Pre Pass: Updating %d Models.\n", updateList.Count() );
+ for ( int i = 0; i < updateList.Count(); i++ )
+ {
+ if ( !GenerateModelFiles( updateList[i].String() ) )
+ {
+ int error = g_errorList.AddToTail();
+ g_errorList[error].result = false;
+ g_errorList[error].fileName.Set( updateList[i].String() );
+ }
+ }
+
+ // iterate error list
+ if ( g_errorList.Count() )
+ {
+ Msg( "\n" );
+ for ( int i = 0; i < g_errorList.Count(); i++ )
+ {
+ Msg( "ERROR: could not pre-process model: %s\n", g_errorList[i].fileName.String() );
+ }
+ }
+
+ return true;
+}
diff --git a/utils/xbox/MakeGameData/MakeParticles.cpp b/utils/xbox/MakeGameData/MakeParticles.cpp
new file mode 100644
index 0000000..7e6a5f1
--- /dev/null
+++ b/utils/xbox/MakeGameData/MakeParticles.cpp
@@ -0,0 +1,37 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: .360 file creation of PCF files
+//
+//=====================================================================================//
+
+#include "MakeGameData.h"
+
+bool CreateTargetFile_PCF( const char *pSourceName, const char *pTargetName, bool bWriteToZip )
+{
+ DmxHeader_t header;
+ CDmElement *pRoot;
+ if ( g_pDataModel->RestoreFromFile( pSourceName, NULL, NULL, &pRoot, CR_DELETE_NEW, &header ) == DMFILEID_INVALID )
+ {
+ Msg( "CreateTargetFile_PCF: Error reading file \"%s\"!\n", pSourceName );
+ return false;
+ }
+
+ const char *pOutFormat = header.formatName;
+ if ( !g_pDataModel->FindFormatUpdater( pOutFormat ) )
+ {
+ pOutFormat = "dmx";
+ }
+
+ CUtlBuffer binaryBuffer;
+ if ( !g_pDataModel->Serialize( binaryBuffer, "binary", pOutFormat, pRoot->GetHandle() ) )
+ {
+ Msg( "CreateTargetFile_PCF: Error writing buffer\n" );
+ return false;
+ }
+
+ g_pDataModel->RemoveFileId( pRoot->GetFileId() );
+
+ WriteBufferToFile( pTargetName, binaryBuffer, bWriteToZip, g_WriteModeForConversions );
+
+ return true;
+}
diff --git a/utils/xbox/MakeGameData/MakeResources.cpp b/utils/xbox/MakeGameData/MakeResources.cpp
new file mode 100644
index 0000000..3a411c3
--- /dev/null
+++ b/utils/xbox/MakeGameData/MakeResources.cpp
@@ -0,0 +1,343 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: .360 file creation of miscellaneous resource and data files
+//
+//=====================================================================================//
+
+#include "MakeGameData.h"
+#include "captioncompiler.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate .360 compiled caption files
+//-----------------------------------------------------------------------------
+bool CreateTargetFile_CCDAT( const char *pSourceName, const char *pTargetName, bool bWriteToZip )
+{
+ CUtlBuffer targetBuffer;
+ bool bOk = false;
+
+ if ( !scriptlib->ReadFileToBuffer( pSourceName, targetBuffer ) )
+ {
+ return false;
+ }
+
+ if ( SwapClosecaptionFile( targetBuffer.Base() ) )
+ {
+ bOk = WriteBufferToFile( pTargetName, targetBuffer, bWriteToZip, g_WriteModeForConversions );
+ }
+
+ return bOk;
+}
+
+static bool ReslistLessFunc( CUtlString const &pLHS, CUtlString const &pRHS )
+{
+ return CaselessStringLessThan( pLHS.Get(), pRHS.Get() );
+}
+
+//-----------------------------------------------------------------------------
+// Find or Add, prevents duplicates. Returns TRUE if found.
+//-----------------------------------------------------------------------------
+bool FindOrAddFileToResourceList( const char *pFilename, CUtlRBTree< CUtlString, int > *pTree, bool bAdd = true )
+{
+ char szOutName[MAX_PATH];
+ char *pOutName;
+ V_strncpy( szOutName, pFilename, sizeof( szOutName ) );
+ V_FixSlashes( szOutName );
+ V_RemoveDotSlashes( szOutName );
+ V_strlower( szOutName );
+ pOutName = szOutName;
+
+ // strip any prefixed game name
+ for ( int i = 0; g_GameNames[i] != NULL; i++ )
+ {
+ size_t len = strlen( g_GameNames[i] );
+ if ( !V_strnicmp( pOutName, g_GameNames[i], len ) && pOutName[len] == '\\' )
+ {
+ // skip past game name and slash
+ pOutName += len+1;
+ break;
+ }
+ }
+
+ if ( pTree->Find( pOutName ) != pTree->InvalidIndex() )
+ {
+ // found
+ return true;
+ }
+
+ if ( bAdd )
+ {
+ pTree->Insert( pOutName );
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Remove entry from dictionary, Returns TRUE if removed.
+//-----------------------------------------------------------------------------
+bool RemoveFileFromResourceList( const char *pFilename, CUtlRBTree< CUtlString, int > *pTree )
+{
+ char szOutName[MAX_PATH];
+ char *pOutName;
+ V_strncpy( szOutName, pFilename, sizeof( szOutName ) );
+ V_FixSlashes( szOutName );
+ V_RemoveDotSlashes( szOutName );
+ V_strlower( szOutName );
+ pOutName = szOutName;
+
+ // strip any prefixed game name
+ for ( int i = 0; g_GameNames[i] != NULL; i++ )
+ {
+ size_t len = strlen( g_GameNames[i] );
+ if ( !V_strnicmp( pOutName, g_GameNames[i], len ) && pOutName[len] == '\\' )
+ {
+ // skip past game name and slash
+ pOutName += len+1;
+ break;
+ }
+ }
+
+ if ( pTree->Find( pOutName ) != pTree->InvalidIndex() )
+ {
+ pTree->Remove( pOutName );
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Generate a tree containing files from a reslist. Returns TRUE if successful.
+//-----------------------------------------------------------------------------
+bool LoadReslist( const char *pReslistName, CUtlRBTree< CUtlString, int > *pTree )
+{
+ CUtlBuffer buffer;
+ if ( !scriptlib->ReadFileToBuffer( pReslistName, buffer, true ) )
+ {
+ return false;
+ }
+
+ char szBasename[MAX_PATH];
+ V_FileBase( pReslistName, szBasename, sizeof( szBasename ) );
+
+ characterset_t breakSet;
+ CharacterSetBuild( &breakSet, "" );
+
+ // parse reslist
+ char szToken[MAX_PATH];
+ char szBspName[MAX_PATH];
+ szBspName[0] = '\0';
+ for ( ;; )
+ {
+ int nTokenSize = buffer.ParseToken( &breakSet, szToken, sizeof( szToken ) );
+ if ( nTokenSize <= 0 )
+ {
+ break;
+ }
+
+ // reslists are pc built, filenames can be sloppy
+ V_strlower( szToken );
+ V_FixSlashes( szToken );
+ V_RemoveDotSlashes( szToken );
+
+ // can safely cull filetypes that are ignored by queued loader at runtime
+ bool bKeep = false;
+ const char *pExt = V_GetFileExtension( szToken );
+ if ( !pExt )
+ {
+ // unknown
+ continue;
+ }
+ else if ( !V_stricmp( pExt, "vmt" ) ||
+ !V_stricmp( pExt, "vhv" ) ||
+ !V_stricmp( pExt, "mdl" ) ||
+ !V_stricmp( pExt, "raw" ) ||
+ !V_stricmp( pExt, "wav" ) )
+ {
+ bKeep = true;
+ }
+ else if ( !V_stricmp( pExt, "mp3" ) )
+ {
+ // change to .wav
+ V_SetExtension( szToken, ".wav", sizeof( szToken ) );
+ bKeep = true;
+ }
+ else if ( !V_stricmp( pExt, "bsp" ) )
+ {
+ // reslists erroneously have multiple bsps
+ if ( !V_stristr( szToken, szBasename ) )
+ {
+ // wrong one, cull it
+ continue;
+ }
+ else
+ {
+ // right one, save it
+ strcpy( szBspName, szToken );
+ bKeep = true;
+ }
+ }
+
+ if ( bKeep )
+ {
+ FindOrAddFileToResourceList( szToken, pTree );
+ }
+ }
+
+ if ( !szBspName[0] )
+ {
+ // reslist is not bsp derived, nothing more to do
+ return true;
+ }
+
+ CUtlVector< CUtlString > bspList;
+ bool bOK = GetDependants_BSP( szBspName, &bspList );
+ if ( !bOK )
+ {
+ return false;
+ }
+
+ // add all the bsp dependants to the resource list
+ for ( int i=0; i<bspList.Count(); i++ )
+ {
+ FindOrAddFileToResourceList( bspList[i].String(), pTree );
+ }
+
+ // iterate all the models in the resource list, get all their dependents
+ CUtlVector< CUtlString > modelList;
+ for ( int i = pTree->FirstInorder(); i != pTree->InvalidIndex(); i = pTree->NextInorder( i ) )
+ {
+ const char *pExt = V_GetFileExtension( pTree->Element( i ).String() );
+ if ( !pExt || V_stricmp( pExt, "mdl" ) )
+ {
+ continue;
+ }
+
+ if ( !GetDependants_MDL( pTree->Element( i ).String(), &modelList ) )
+ {
+ return false;
+ }
+ }
+
+ // add all the model dependents to the resource list
+ for ( int i=0; i<modelList.Count(); i++ )
+ {
+ FindOrAddFileToResourceList( modelList[i].String(), pTree );
+ }
+
+ // check for optional commentary, include wav dependencies
+ char szCommentaryName[MAX_PATH];
+ V_ComposeFileName( g_szGamePath, szBspName, szCommentaryName, sizeof( szCommentaryName ) );
+ V_StripExtension( szCommentaryName, szCommentaryName, sizeof( szCommentaryName ) );
+ V_strncat( szCommentaryName, "_commentary.txt", sizeof( szCommentaryName ) );
+ CUtlBuffer commentaryBuffer;
+ if ( ReadFileToBuffer( szCommentaryName, commentaryBuffer, true, true ) )
+ {
+ // any single token may be quite large to due to text
+ char szCommentaryToken[8192];
+ for ( ;; )
+ {
+ int nTokenSize = commentaryBuffer.ParseToken( &breakSet, szCommentaryToken, sizeof( szCommentaryToken ) );
+ if ( nTokenSize < 0 )
+ {
+ break;
+ }
+ if ( nTokenSize > 0 && !V_stricmp( szCommentaryToken, "commentaryfile" ) )
+ {
+ // get the commentary file
+ nTokenSize = commentaryBuffer.ParseToken( &breakSet, szCommentaryToken, sizeof( szCommentaryToken ) );
+ if ( nTokenSize > 0 )
+ {
+ // skip past sound chars
+ char *pName = szCommentaryToken;
+ while ( *pName && IsSoundChar( *pName ) )
+ {
+ pName++;
+ }
+ char szWavFile[MAX_PATH];
+ V_snprintf( szWavFile, sizeof( szWavFile ), "sound/%s", pName );
+ FindOrAddFileToResourceList( szWavFile, pTree );
+ }
+ }
+ }
+ }
+
+ // check for optional blacklist
+ char szBlacklist[MAX_PATH];
+ V_ComposeFileName( g_szGamePath, "reslistfixes_xbox.xsc", szBlacklist, sizeof( szBlacklist ) );
+ CUtlBuffer blacklistBuffer;
+ if ( ReadFileToBuffer( szBlacklist, blacklistBuffer, true, true ) )
+ {
+ for ( ;; )
+ {
+ int nTokenSize = blacklistBuffer.ParseToken( &breakSet, szToken, sizeof( szToken ) );
+ if ( nTokenSize <= 0 )
+ {
+ break;
+ }
+
+ bool bAdd;
+ if ( !V_stricmp( szToken, "-" ) )
+ {
+ bAdd = false;
+ }
+ else if ( !V_stricmp( szToken, "+" ) )
+ {
+ bAdd = true;
+ }
+ else
+ {
+ // bad syntax, skip line
+ Msg( "Bad Syntax, expecting '+' or '-' as first token in reslist fixup file '%s'.\n", szBlacklist );
+ continue;
+ }
+
+ // get entry
+ nTokenSize = blacklistBuffer.ParseToken( &breakSet, szToken, sizeof( szToken ) );
+ if ( nTokenSize <= 0 )
+ {
+ break;
+ }
+
+ if ( bAdd )
+ {
+ FindOrAddFileToResourceList( szToken, pTree );
+ }
+ else
+ {
+ RemoveFileFromResourceList( szToken, pTree );
+ }
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate .360 compiled reslist files
+// Reslist files are processed for the unique consumption of Queued Loading.
+//-----------------------------------------------------------------------------
+bool CreateTargetFile_RESLST( const char *pSourceName, const char *pTargetName, bool bWriteToZip )
+{
+ bool bOK = false;
+
+ // parse reslist
+ CUtlRBTree< CUtlString, int > rbTree( 0, 0, ReslistLessFunc );
+ if ( !LoadReslist( pSourceName, &rbTree ) )
+ {
+ return false;
+ }
+
+ CUtlBuffer targetBuffer( 0, 0, CUtlBuffer::TEXT_BUFFER );
+ for ( int iIndex = rbTree.FirstInorder(); iIndex != rbTree.InvalidIndex(); iIndex = rbTree.NextInorder( iIndex ) )
+ {
+ targetBuffer.PutChar( '\"' );
+ targetBuffer.PutString( rbTree[iIndex].String() );
+ targetBuffer.PutChar( '\"' );
+ targetBuffer.PutString( "\n" );
+ }
+
+ bOK = WriteBufferToFile( pTargetName, targetBuffer, bWriteToZip, g_WriteModeForConversions );
+
+ return bOK;
+} \ No newline at end of file
diff --git a/utils/xbox/MakeGameData/MakeScenes.cpp b/utils/xbox/MakeGameData/MakeScenes.cpp
new file mode 100644
index 0000000..4077978
--- /dev/null
+++ b/utils/xbox/MakeGameData/MakeScenes.cpp
@@ -0,0 +1,40 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: .360 file creation of Choreo VCD files
+//
+//=====================================================================================//
+
+#include "MakeGameData.h"
+#include "sceneimage.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+class CDefaultStatus : public ISceneCompileStatus
+{
+public:
+ virtual void UpdateStatus( char const *pchSceneName, bool bQuiet, int nIndex, int nCount )
+ {
+ if ( !bQuiet )
+ {
+ Msg( "Scenes: Compiling: %s\n", pchSceneName );
+ }
+ }
+};
+
+bool CreateSceneImageFile( char const *pchModPath, bool bWriteToZip, bool bLittleEndian, bool bQuiet, DiskWriteMode_t eWriteModeForConversions )
+{
+ CUtlBuffer targetBuffer;
+
+ const char *pFilename = bLittleEndian ? "scenes/scenes.image" : "scenes/scenes.360.image";
+
+ CDefaultStatus statusHelper;
+
+ bool bSuccess = g_pSceneImage->CreateSceneImageFile( targetBuffer, pchModPath, bLittleEndian, bQuiet, &statusHelper );
+ if ( bSuccess )
+ {
+ bSuccess = WriteBufferToFile( pFilename, targetBuffer, bWriteToZip, eWriteModeForConversions );
+ }
+
+ return bSuccess;
+} \ No newline at end of file
diff --git a/utils/xbox/MakeGameData/MakeScenesImage.vpc b/utils/xbox/MakeGameData/MakeScenesImage.vpc
new file mode 100644
index 0000000..0361be6
--- /dev/null
+++ b/utils/xbox/MakeGameData/MakeScenesImage.vpc
@@ -0,0 +1,133 @@
+//-----------------------------------------------------------------------------
+// MAKESCENESIMAGE.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_con_base.vpc"
+
+$Macro GAMENAME "MakeScenesImage"
+
+$Configuration "Debug"
+{
+ $General
+ {
+ $OutputDirectory ".\Debug_$GAMENAME"
+ $IntermediateDirectory ".\Debug_$GAMENAME"
+ }
+}
+
+$Configuration "Release"
+{
+ $General
+ {
+ $OutputDirectory ".\Release_$GAMENAME"
+ $IntermediateDirectory ".\Release_$GAMENAME"
+ }
+}
+
+$Configuration
+{
+ $Compiler
+ {
+ $PreprocessorDefinitions "$BASE;MAKESCENESIMAGE"
+ $PreprocessorDefinitions "$BASE;NO_X360_XDK" [$VS2015]
+ $AdditionalIncludeDirectories "$BASE;$SRCDIR\x360xdk\include\win32\vs2005;$SRCDIR\game\shared"
+ }
+
+ $Linker
+ {
+ $AdditionalDependencies "$BASE xgraphics.lib d3d9.lib legacy_stdio_definitions.lib"
+ $AdditionalDependencies "$BASE xmaencoder.lib" [!$VS2015]
+ $AdditionalLibraryDirectories "$BASE;$SRCDIR\x360xdk\lib\win32\vs2005"
+ }
+}
+
+$Project "MakeScenesImage"
+{
+ $Folder "Source Files"
+ {
+ -$File "$SRCDIR\public\tier0\memoverride.cpp"
+ $File "MakeGameData.cpp"
+ $File "MakeMaps.cpp"
+ $File "MakeMisc.cpp"
+ $File "MakeModels.cpp"
+ $File "MakeParticles.cpp"
+ $File "MakeResources.cpp"
+ $File "MakeScenes.cpp"
+ $File "MakeShaders.cpp"
+ $File "MakeSounds.cpp"
+ $File "MakeTextures.cpp"
+ $File "MakeZip.cpp"
+
+ $Folder "Audio"
+ {
+ $File "imaadpcm.cpp"
+ $File "sound_io.cpp"
+ $File "resample.cpp"
+ }
+
+ $Folder "Public Modules"
+ {
+ $File "$SRCDIR\common\compiledcaptionswap.cpp"
+ $file "$SRCDIR\common\studiobyteswap.cpp"
+ $File "$SRCDIR\utils\common\scriplib.cpp"
+ $File "$SRCDIR\public\zip_utils.cpp"
+ $File "$SRCDIR\public\sentence.cpp"
+ $File "$SRCDIR\utils\common\cmdlib.cpp"
+ $File "$SRCDIR\public\filesystem_helpers.cpp"
+ $File "$SRCDIR\public\filesystem_init.cpp"
+ $File "$SRCDIR\utils\common\filesystem_tools.cpp"
+ $File "$SRCDIR\public\interpolatortypes.cpp"
+ }
+ }
+
+ $Folder "Header Files"
+ {
+ $File "MakeGameData.h"
+ $File "XZipTool.h"
+ $File "imaadpcm.h"
+ $File "resample.h"
+ $File "$SRCDIR\public\captioncompiler.h"
+ $File "$SRCDIR\common\studiobyteswap.h"
+ $File "$SRCDIR\utils\common\scriplib.h"
+ $File "$SRCDIR\public\filesystem.h"
+ $File "$SRCDIR\public\ibsppack.h"
+ $File "$SRCDIR\public\sentence.h"
+ $File "$SRCDIR\public\studio.h"
+ $File "$SRCDIR\public\tier1\utlbuffer.h"
+ $File "$SRCDIR\public\tier1\strtools.h"
+ $File "$SRCDIR\public\vphysics_interface.h"
+ $File "$SRCDIR\public\vstdlib\vstdlib.h"
+ $File "$SRCDIR\public\xwvfile.h"
+ $File "$SRCDIR\public\zip_utils.h"
+ $File "$SRCDIR\game\shared\choreoscene.h"
+ $File "$SRCDIR\game\shared\choreoactor.h"
+ $File "$SRCDIR\public\filesystem_helpers.h"
+ $File "$SRCDIR\public\filesystem_init.h"
+ $File "$SRCDIR\utils\common\filesystem_tools.h"
+ $File "$SRCDIR\public\interpolatortypes.h"
+ $File "$SRCDIR\utils\common\cmdlib.h"
+ $File "$SRCDIR\game\shared\choreochannel.h"
+ $File "$SRCDIR\game\shared\choreoevent.h"
+ $File "$SRCDIR\public\tier1\checksum_crc.h"
+ $File "$SRCDIR\public\tier2\tier2.h"
+ $File "$SRCDIR\common\lzma\lzma.h"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib appframework
+ $Lib mathlib
+ $Lib vtf
+ $Lib tier2
+ $Lib choreoobjects
+ $Lib bitmap
+ $Lib datamodel
+ $Lib dmserializers
+ $Lib $LIBCOMMON\lzma
+ }
+}
diff --git a/utils/xbox/MakeGameData/MakeShaders.cpp b/utils/xbox/MakeGameData/MakeShaders.cpp
new file mode 100644
index 0000000..25f51b4
--- /dev/null
+++ b/utils/xbox/MakeGameData/MakeShaders.cpp
@@ -0,0 +1,52 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: .360 file creation of shaders
+//
+//=====================================================================================//
+
+#include "MakeGameData.h"
+#include "materialsystem/shader_vcs_version.h"
+
+#define SHADER_FILE_THRESHOLD 32*1024
+
+//-----------------------------------------------------------------------------
+// Get the preload data for a vcs file
+//-----------------------------------------------------------------------------
+bool GetPreloadData_VCS( const char *pFilename, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut )
+{
+ ShaderHeader_t *pHeader = (ShaderHeader_t *)fileBufferIn.Base();
+
+ unsigned int version = BigLong( pHeader->m_nVersion );
+
+ // ensure caller's buffer is clean
+ // caller determines preload size, via TellMaxPut()
+ preloadBufferOut.Purge();
+
+ unsigned int nPreloadSize;
+ if ( fileBufferIn.TellMaxPut() <= SHADER_FILE_THRESHOLD )
+ {
+ // include the whole file
+ nPreloadSize = fileBufferIn.TellMaxPut();
+ }
+ else
+ {
+ if ( version < SHADER_VCS_VERSION_NUMBER )
+ {
+ // not supporting old versions
+ return false;
+ }
+
+ if ( version != SHADER_VCS_VERSION_NUMBER )
+ {
+ // bad version
+ Msg( "Can't preload: '%s', expecting version %d got version %d\n", pFilename, SHADER_VCS_VERSION_NUMBER, version );
+ return false;
+ }
+
+ nPreloadSize = sizeof( ShaderHeader_t ) + BigLong( pHeader->m_nNumStaticCombos ) * sizeof( StaticComboRecord_t );
+ }
+
+ preloadBufferOut.Put( fileBufferIn.Base(), nPreloadSize );
+
+ return true;
+} \ No newline at end of file
diff --git a/utils/xbox/MakeGameData/MakeSounds.cpp b/utils/xbox/MakeGameData/MakeSounds.cpp
new file mode 100644
index 0000000..f0913ce
--- /dev/null
+++ b/utils/xbox/MakeGameData/MakeSounds.cpp
@@ -0,0 +1,968 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: .360.WAV Creation
+//
+//=====================================================================================//
+
+#include "MakeGameData.h"
+#ifndef NO_X360_XDK
+#include <XMAEncoder.h>
+#endif
+#include "datamap.h"
+#include "sentence.h"
+#include "tier2/riff.h"
+#include "resample.h"
+#include "xwvfile.h"
+
+// all files are built for streaming compliance
+// allows for fastest runtime loading path
+// actual streaming or static state is determined by engine
+#define XBOX_DVD_SECTORSIZE 2048
+#define XMA_BLOCK_SIZE 2048 // must be aligned to 1024
+#define MAX_CHUNKS 256
+
+// [0,100]
+#define XMA_HIGH_QUALITY 90
+#define XMA_DEFAULT_QUALITY 75
+#define XMA_MEDIUM_QUALITY 50
+#define XMA_LOW_QUALITY 25
+
+typedef struct
+{
+ unsigned int id;
+ int size;
+ byte *pData;
+} chunk_t;
+
+struct conversion_t
+{
+ const char *pSubDir;
+ int quality;
+ bool bForceTo22K;
+};
+
+// default conversion rules
+conversion_t g_defaultConversionRules[] =
+{
+ // subdir quality 22Khz
+ { "", XMA_DEFAULT_QUALITY, false }, // default settings
+ { "weapons", XMA_DEFAULT_QUALITY, false },
+ { "music", XMA_DEFAULT_QUALITY, false },
+ { "vo", XMA_MEDIUM_QUALITY, false },
+ { "npc", XMA_MEDIUM_QUALITY, false },
+ { "ambient", XMA_DEFAULT_QUALITY, false },
+ { "commentary", XMA_LOW_QUALITY, true },
+ { NULL },
+};
+
+// portal conversion rules
+conversion_t g_portalConversionRules[] =
+{
+ // subdir quality 22Khz
+ { "", XMA_DEFAULT_QUALITY, false }, // default settings
+ { "commentary", XMA_LOW_QUALITY, true },
+ { NULL },
+};
+
+chunk_t g_chunks[MAX_CHUNKS];
+int g_numChunks;
+
+extern IFileReadBinary *g_pSndIO;
+
+//-----------------------------------------------------------------------------
+// Purpose: chunk printer
+//-----------------------------------------------------------------------------
+void PrintChunk( unsigned int chunkName, int size )
+{
+ char c[4];
+
+ for ( int i=0; i<4; i++ )
+ {
+ c[i] = ( chunkName >> i*8 ) & 0xFF;
+ if ( !c[i] )
+ c[i] = ' ';
+ }
+
+ Msg( "%c%c%c%c: %d bytes\n", c[0], c[1], c[2], c[3], size );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: which chunks are supported, false to ignore
+//-----------------------------------------------------------------------------
+bool IsValidChunk( unsigned int chunkName )
+{
+ switch ( chunkName )
+ {
+ case WAVE_DATA:
+ case WAVE_CUE:
+ case WAVE_SAMPLER:
+ case WAVE_VALVEDATA:
+ case WAVE_FMT:
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: align buffer
+//-----------------------------------------------------------------------------
+int AlignToBoundary( CUtlBuffer &buf, int alignment )
+{
+ int curPosition;
+ int newPosition;
+ byte padByte = 0;
+
+ buf.SeekPut( CUtlBuffer::SEEK_TAIL, 0 );
+ curPosition = buf.TellPut();
+
+ if ( alignment <= 1 )
+ return curPosition;
+
+ // advance to aligned position
+ newPosition = AlignValue( curPosition, alignment );
+ buf.EnsureCapacity( newPosition );
+
+ // write empty
+ for ( int i=0; i<newPosition-curPosition; i++ )
+ {
+ buf.Put( &padByte, 1 );
+ }
+
+ return newPosition;
+}
+
+//--------------------------------------------------------------------------------------
+// SampleToXMABlockOffset
+//
+// Description: converts from a sample index to a block index + the number of samples
+// to offset from the beginning of the block.
+//
+// Parameters:
+// dwSampleIndex: sample index to convert
+// pdwSeekTable: pointer to the file's XMA2 seek table
+// nEntries: number of DWORD entries in the seek table
+// out_pBlockIndex: index of block where the desired sample lives
+// out_pOffset: number of samples in the block before the desired sample
+//--------------------------------------------------------------------------------------
+bool SampleToXMABlockOffset( DWORD dwSampleIndex, const DWORD *pdwSeekTable, DWORD nEntries, DWORD *out_pBlockIndex, DWORD *out_pOffset )
+{
+ // Run through the seek table to find the block closest to the desired sample.
+ // Each seek table entry is the index (counting from the beginning of the file)
+ // of the first sample in the corresponding block, but there's no entry for the
+ // first block (since the index would always be zero).
+ bool bFound = false;
+ for ( DWORD i = 0; !bFound && i < nEntries; ++i )
+ {
+ if ( dwSampleIndex < BigLong( pdwSeekTable[i] ) )
+ {
+ *out_pBlockIndex = i;
+ bFound = true;
+ }
+ }
+
+ // Calculate the sample offset by figuring out what the sample index of the first sample
+ // in the block is, then subtracting that from dwSampleIndex.
+ if ( bFound )
+ {
+ DWORD dwStartOfBlock = (*out_pBlockIndex == 0) ? 0 : BigLong( pdwSeekTable[*out_pBlockIndex - 1] );
+ *out_pOffset = dwSampleIndex - dwStartOfBlock;
+ }
+
+ return bFound;
+}
+
+//-----------------------------------------------------------------------------
+// Compile and compress vdat
+//-----------------------------------------------------------------------------
+bool CompressVDAT( chunk_t *pChunk )
+{
+ CSentence *pSentence;
+
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ buf.EnsureCapacity( pChunk->size );
+ memcpy( buf.Base(), pChunk->pData, pChunk->size );
+ buf.SeekPut( CUtlBuffer::SEEK_HEAD, pChunk->size );
+
+ pSentence = new CSentence();
+
+ // Make binary version of VDAT
+ // Throws all phonemes into one word, discards sentence memory, etc.
+ pSentence->InitFromDataChunk( buf.Base(), buf.TellPut() );
+ pSentence->MakeRuntimeOnly();
+ CUtlBuffer binaryBuffer( 0, 0, 0 );
+ binaryBuffer.SetBigEndian( true );
+ pSentence->CacheSaveToBuffer( binaryBuffer, CACHED_SENTENCE_VERSION_ALIGNED );
+ delete pSentence;
+
+ unsigned int compressedSize = 0;
+ unsigned char *pCompressedOutput = LZMA_OpportunisticCompress( (unsigned char *)binaryBuffer.Base(),
+ binaryBuffer.TellPut(), &compressedSize );
+ if ( pCompressedOutput )
+ {
+ if ( !g_bQuiet )
+ {
+ Msg( "CompressVDAT: Compressed %d to %d\n", binaryBuffer.TellPut(), compressedSize );
+ }
+
+ free( pChunk->pData );
+ pChunk->size = compressedSize;
+ pChunk->pData = pCompressedOutput;
+ }
+ else
+ {
+ // save binary VDAT as-is
+ free( pChunk->pData );
+ pChunk->size = binaryBuffer.TellPut();
+ pChunk->pData = (byte *)malloc( pChunk->size );
+ memcpy( pChunk->pData, binaryBuffer.Base(), pChunk->size );
+ }
+
+ // success
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: read chunks into provided array
+//-----------------------------------------------------------------------------
+bool ReadChunks( const char *pFileName, int &numChunks, chunk_t chunks[MAX_CHUNKS] )
+{
+ numChunks = 0;
+
+ InFileRIFF riff( pFileName, *g_pSndIO );
+ if ( riff.RIFFName() != RIFF_WAVE )
+ {
+ return false;
+ }
+
+ IterateRIFF walk( riff, riff.RIFFSize() );
+
+ while ( walk.ChunkAvailable() )
+ {
+ chunks[numChunks].id = walk.ChunkName();
+ chunks[numChunks].size = walk.ChunkSize();
+
+ int size = chunks[numChunks].size;
+ if ( walk.ChunkName() == WAVE_FMT && size < sizeof( WAVEFORMATEXTENSIBLE ) )
+ {
+ // format chunks are variable and cast to different structures
+ // ensure the data footprint is at least the structure we want to manipulate
+ size = sizeof( WAVEFORMATEXTENSIBLE );
+ }
+
+ chunks[numChunks].pData = (byte *)malloc( size );
+ memset( chunks[numChunks].pData, 0, size );
+
+ walk.ChunkRead( chunks[numChunks].pData );
+
+ numChunks++;
+ if ( numChunks >= MAX_CHUNKS )
+ return false;
+
+ walk.ChunkNext();
+ }
+
+ // success
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: promote pcm 8 bit to 16 bit pcm
+//-----------------------------------------------------------------------------
+void ConvertPCMDataChunk8To16( chunk_t *pFormatChunk, chunk_t *pDataChunk )
+{
+ WAVEFORMATEX *pFormat = (WAVEFORMATEX*)pFormatChunk->pData;
+
+ int sampleSize = ( pFormat->nChannels * pFormat->wBitsPerSample ) >> 3;
+ int sampleCount = pDataChunk->size / sampleSize;
+ int outputSize = sizeof( short ) * ( sampleCount * pFormat->nChannels );
+ short *pOut = (short *)malloc( outputSize );
+
+ // in-place convert data from 8-bits to 16-bits
+ Convert8To16( pDataChunk->pData, pOut, sampleCount, pFormat->nChannels );
+
+ free( pDataChunk->pData );
+ pDataChunk->pData = (byte *)pOut;
+ pDataChunk->size = outputSize;
+
+ pFormat->wFormatTag = WAVE_FORMAT_PCM;
+ pFormat->nBlockAlign = 2 * pFormat->nChannels;
+ pFormat->wBitsPerSample = 16;
+ pFormat->nAvgBytesPerSec = 2 * pFormat->nSamplesPerSec * pFormat->nChannels;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: convert adpcm to 16 bit pcm
+//-----------------------------------------------------------------------------
+void ConvertADPCMDataChunkTo16( chunk_t *pFormatChunk, chunk_t *pDataChunk )
+{
+ WAVEFORMATEX *pFormat = (WAVEFORMATEX*)pFormatChunk->pData;
+
+ int sampleCount = ADPCMSampleCount( (byte *)pFormat, pDataChunk->pData, pDataChunk->size );
+ int outputSize = sizeof( short ) * sampleCount * pFormat->nChannels;
+ short *pOut = (short *)malloc( outputSize );
+
+ // convert to PCM 16bit format
+ DecompressADPCMSamples( (byte*)pFormat, (byte*)pDataChunk->pData, pDataChunk->size, pOut );
+
+ free( pDataChunk->pData );
+ pDataChunk->pData = (byte *)pOut;
+ pDataChunk->size = outputSize;
+
+ pFormat->wFormatTag = WAVE_FORMAT_PCM;
+ pFormat->nBlockAlign = 2 * pFormat->nChannels;
+ pFormat->wBitsPerSample = 16;
+ pFormat->nAvgBytesPerSec = 2 * pFormat->nSamplesPerSec * pFormat->nChannels;
+
+ pFormatChunk->size = 16;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Decimate to 22K
+//-----------------------------------------------------------------------------
+void ConvertPCMDataChunk16To22K( chunk_t *pFormatChunk, chunk_t *pDataChunk )
+{
+ WAVEFORMATEX *pFormat = (WAVEFORMATEX*)pFormatChunk->pData;
+
+ if ( pFormat->nSamplesPerSec != 44100 || pFormat->wBitsPerSample != 16 || pFormat->wFormatTag != WAVE_FORMAT_PCM )
+ {
+ // not in expected format
+ return;
+ }
+
+ int sampleSize = ( pFormat->nChannels * pFormat->wBitsPerSample ) >> 3;
+ int sampleCount = pDataChunk->size / sampleSize;
+ short *pOut = (short *)malloc( sizeof( short ) * ( sampleCount * pFormat->nChannels ) );
+
+ DecimateSampleRateBy2_16( (short *)pDataChunk->pData, pOut, sampleCount, pFormat->nChannels );
+
+ free( pDataChunk->pData );
+ pDataChunk->pData = (byte *)pOut;
+ pDataChunk->size = sizeof( short ) * ( sampleCount/2 * pFormat->nChannels );
+
+ pFormat->nSamplesPerSec = 22050;
+ pFormat->nBlockAlign = 2 * pFormat->nChannels;
+ pFormat->nAvgBytesPerSec = 2 * pFormat->nSamplesPerSec * pFormat->nChannels;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: determine loop start
+//-----------------------------------------------------------------------------
+int FindLoopStart( int samplerChunk, int cueChunk )
+{
+ int loopStartFromCue = -1;
+ int loopStartFromSampler = -1;
+
+ if ( cueChunk != -1 )
+ {
+ struct cuechunk_t
+ {
+ unsigned int dwName;
+ unsigned int dwPosition;
+ unsigned int fccChunk;
+ unsigned int dwChunkStart;
+ unsigned int dwBlockStart;
+ unsigned int dwSampleOffset;
+ };
+ struct cueRIFF_t
+ {
+ int cueCount;
+ cuechunk_t cues[1];
+ };
+
+ cueRIFF_t *pCue = (cueRIFF_t *)g_chunks[cueChunk].pData;
+ if ( pCue->cueCount > 0 )
+ {
+ loopStartFromCue = pCue->cues[0].dwSampleOffset;
+ }
+ }
+
+ if ( samplerChunk != -1 )
+ {
+ struct SampleLoop
+ {
+ unsigned int dwIdentifier;
+ unsigned int dwType;
+ unsigned int dwStart;
+ unsigned int dwEnd;
+ unsigned int dwFraction;
+ unsigned int dwPlayCount;
+ };
+
+ struct samplerchunk_t
+ {
+ unsigned int dwManufacturer;
+ unsigned int dwProduct;
+ unsigned int dwSamplePeriod;
+ unsigned int dwMIDIUnityNote;
+ unsigned int dwMIDIPitchFraction;
+ unsigned int dwSMPTEFormat;
+ unsigned int dwSMPTEOffset;
+ unsigned int cSampleLoops;
+ unsigned int cbSamplerData;
+ struct SampleLoop Loops[1];
+ };
+
+ // assume that the loop end is the sample end
+ // assume that only the first loop is relevant
+ samplerchunk_t *pSampler = (samplerchunk_t *)g_chunks[samplerChunk].pData;
+ if ( pSampler->cSampleLoops > 0 )
+ {
+ // only support normal forward loops
+ if ( pSampler->Loops[0].dwType == 0 )
+ {
+ loopStartFromSampler = pSampler->Loops[0].dwStart;
+ }
+ }
+ }
+
+ return ( max( loopStartFromCue, loopStartFromSampler ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns chunk, -1 if not found
+//-----------------------------------------------------------------------------
+int FindChunk( unsigned int id )
+{
+ int i;
+ for ( i=0; i<g_numChunks; i++ )
+ {
+ if ( g_chunks[i].id == id )
+ {
+ return i;
+ }
+ }
+
+ // not found
+ return - 1;
+}
+
+bool EncodeAsXMA( const char *pDebugName, CUtlBuffer &targetBuff, int quality, bool bIsVoiceOver )
+{
+#ifdef NO_X360_XDK
+ return false;
+#else
+ int formatChunk = FindChunk( WAVE_FMT );
+ int dataChunk = FindChunk( WAVE_DATA );
+ if ( formatChunk == -1 || dataChunk == -1 )
+ {
+ // huh? these should have been pre-validated
+ return false;
+ }
+
+ int vdatSize = 0;
+ int vdatChunk = FindChunk( WAVE_VALVEDATA );
+ if ( vdatChunk != -1 )
+ {
+ vdatSize = g_chunks[vdatChunk].size;
+ }
+
+ int loopStart = FindLoopStart( FindChunk( WAVE_SAMPLER ), FindChunk( WAVE_CUE ) );
+
+ // format structure must be expected 16 bit PCM, otherwise encoder crashes
+ WAVEFORMATEX *pFormat = (WAVEFORMATEX *)g_chunks[formatChunk].pData;
+ pFormat->nAvgBytesPerSec = pFormat->nSamplesPerSec * pFormat->nChannels * 2;
+ pFormat->nBlockAlign = 2 * pFormat->nChannels;
+ pFormat->cbSize = 0;
+
+ XMAENCODERSTREAM inputStream = { 0 };
+
+ WAVEFORMATEXTENSIBLE wfx;
+ Assert( g_chunks[formatChunk].size <= sizeof( WAVEFORMATEXTENSIBLE ) );
+ memcpy( &wfx, g_chunks[formatChunk].pData, g_chunks[formatChunk].size );
+ if ( g_chunks[formatChunk].size < sizeof( WAVEFORMATEXTENSIBLE ) )
+ {
+ memset( (unsigned char*)&wfx + g_chunks[formatChunk].size, 0, sizeof( WAVEFORMATEXTENSIBLE ) - g_chunks[formatChunk].size );
+ }
+
+ memcpy( &inputStream.Format, &wfx, sizeof( WAVEFORMATEX ) );
+ inputStream.pBuffer = g_chunks[dataChunk].pData;
+ inputStream.BufferSize = g_chunks[dataChunk].size;
+ if ( loopStart != -1 )
+ {
+ // can only support a single loop point until end of file
+ inputStream.LoopStart = loopStart;
+ inputStream.LoopLength = inputStream.BufferSize / ( pFormat->nChannels * sizeof( short ) ) - loopStart;
+ }
+
+ void *pXMAData = NULL;
+ DWORD XMADataSize = 0;
+ XMA2WAVEFORMAT *pXMA2Format = NULL;
+ DWORD XMA2FormatSize = 0;
+ DWORD *pXMASeekTable = NULL;
+ DWORD XMASeekTableSize = 0;
+ HRESULT hr = S_OK;
+
+ DWORD xmaFlags = XMAENCODER_NOFILTER;
+ if ( loopStart != -1 )
+ {
+ xmaFlags |= XMAENCODER_LOOP;
+ }
+
+ int numAttempts = 1;
+ while ( numAttempts < 10 )
+ {
+ hr = XMA2InMemoryEncoder( 1, &inputStream, quality, xmaFlags, XMA_BLOCK_SIZE/1024, &pXMAData, &XMADataSize, &pXMA2Format, &XMA2FormatSize, &pXMASeekTable, &XMASeekTableSize );
+ if ( !FAILED( hr ) )
+ break;
+
+ // make small jumps
+ quality += 5;
+ if ( quality > 100 )
+ quality = 100;
+ if ( !g_bQuiet )
+ {
+ Msg( "XMA Encoding Error on '%s', Attempting increasing quality to %d\n", pDebugName, quality );
+ }
+
+ numAttempts++;
+
+ pXMAData = NULL;
+ XMADataSize = 0;
+ pXMA2Format = NULL;
+ XMA2FormatSize = 0;
+ pXMASeekTable = NULL;
+ XMASeekTableSize = 0;
+ }
+
+ if ( FAILED( hr ) )
+ {
+ // unrecoverable
+ return false;
+ }
+ else if ( numAttempts > 1 )
+ {
+ if ( !g_bQuiet )
+ {
+ Msg( "XMA Encoding Success on '%s' at quality %d\n", pDebugName, quality );
+ }
+ }
+
+ DWORD loopBlock = 0;
+ DWORD numLeadingSamples = 0;
+ DWORD numTrailingSamples = 0;
+
+ if ( loopStart != -1 )
+ {
+ // calculate start block/offset
+ DWORD loopBlockStartIndex = 0;
+ DWORD loopBlockStartOffset = 0;
+
+ if ( !SampleToXMABlockOffset( BigLong( pXMA2Format->LoopBegin ), pXMASeekTable, XMASeekTableSize/sizeof( DWORD ), &loopBlockStartIndex, &loopBlockStartOffset ) )
+ {
+ // could not determine loop point, out of range of encoded samples
+ Msg( "XMA Loop Encoding Error on '%s', loop %d\n", pDebugName, loopStart );
+ return false;
+ }
+
+ loopBlock = loopBlockStartIndex;
+ numLeadingSamples = loopBlockStartOffset;
+
+ if ( BigLong( pXMA2Format->LoopEnd ) < BigLong( pXMA2Format->SamplesEncoded ) )
+ {
+ // calculate end block/offset
+ DWORD loopBlockEndIndex = 0;
+ DWORD loopBlockEndOffset = 0;
+
+ if ( !SampleToXMABlockOffset( BigLong( pXMA2Format->LoopEnd ), pXMASeekTable, XMASeekTableSize/sizeof( DWORD ), &loopBlockEndIndex, &loopBlockEndOffset ) )
+ {
+ // could not determine loop point, out of range of encoded samples
+ Msg( "XMA Loop Encoding Error on '%s', loop %d\n", pDebugName, loopStart );
+ return false;
+ }
+
+ if ( loopBlockEndIndex != BigLong( pXMA2Format->BlockCount ) - 1 )
+ {
+ // end block MUST be last block
+ Msg( "XMA Loop Encoding Error on '%s', block end is %d/%d\n", pDebugName, loopBlockEndOffset, BigLong( pXMA2Format->BlockCount ) );
+ return false;
+ }
+
+ numTrailingSamples = BigLong( pXMA2Format->SamplesEncoded ) - BigLong( pXMA2Format->LoopEnd );
+ }
+
+ // check for proper encoding range
+ if ( loopBlock > 32767 )
+ {
+ Msg( "XMA Loop Encoding Error on '%s', loop block exceeds 16 bits %d\n", pDebugName, loopBlock );
+ return false;
+ }
+ if ( numLeadingSamples > 32767 )
+ {
+ Msg( "XMA Loop Encoding Error on '%s', leading samples exceeds 16 bits %d\n", pDebugName, numLeadingSamples );
+ return false;
+ }
+ if ( numTrailingSamples > 32767 )
+ {
+ Msg( "XMA Loop Encoding Error on '%s', trailing samples exceeds 16 bits %d\n", pDebugName, numTrailingSamples );
+ return false;
+ }
+ }
+
+ xwvHeader_t header;
+ memset( &header, 0, sizeof( xwvHeader_t ) );
+
+ int seekTableSize = 0;
+ if ( vdatSize || bIsVoiceOver )
+ {
+ // save the optional seek table only for vdat or vo
+ // the seek table size is expected to be derived by this calculation
+ seekTableSize = ( XMADataSize / XMA_BYTES_PER_PACKET ) * sizeof( int );
+ if ( seekTableSize != XMASeekTableSize )
+ {
+ Msg( "XMA Error: Unexpected seek table calculation in '%s'!", pDebugName );
+ return false;
+ }
+ }
+
+ if ( loopStart != -1 && ( vdatSize || bIsVoiceOver ) )
+ {
+ Msg( "XMA Warning: Unexpected loop in vo data '%s'!", pDebugName );
+
+ // do not write the seek table for looping sounds
+ seekTableSize = 0;
+ }
+
+ header.id = BigLong( XWV_ID );
+ header.version = BigLong( XWV_VERSION );
+ header.headerSize = BigLong( sizeof( xwvHeader_t ) );
+ header.staticDataSize = BigLong( seekTableSize + vdatSize );
+ header.dataOffset = BigLong( AlignValue( sizeof( xwvHeader_t) + seekTableSize + vdatSize, XBOX_DVD_SECTORSIZE ) );
+ header.dataSize = BigLong( XMADataSize );
+
+ // track the XMA number of samples that will get decoded
+ // which is NOT the same as what the source actually encoded
+ header.numDecodedSamples = pXMA2Format->SamplesEncoded;
+
+ if ( loopStart != -1 )
+ {
+ // the loop start is in source space (now meaningless), need the loop in XMA decoding sample space
+ header.loopStart = pXMA2Format->LoopBegin;
+ }
+ else
+ {
+ header.loopStart = BigLong( -1 );
+ }
+ header.loopBlock = BigShort( (unsigned short)loopBlock );
+ header.numLeadingSamples = BigShort( (unsigned short)numLeadingSamples );
+ header.numTrailingSamples = BigShort( (unsigned short)numTrailingSamples );
+
+ header.vdatSize = BigShort( (short)vdatSize );
+ header.format = XWV_FORMAT_XMA;
+ header.bitsPerSample = 16;
+ header.SetSampleRate( pFormat->nSamplesPerSec );
+ header.SetChannels( pFormat->nChannels );
+ header.quality = quality;
+ header.bHasSeekTable = ( seekTableSize != 0 );
+
+ // output header
+ targetBuff.Put( &header, sizeof( xwvHeader_t ) );
+
+ // output optional seek table
+ if ( seekTableSize )
+ {
+ // seek table is already in big-endian format
+ targetBuff.Put( pXMASeekTable, seekTableSize );
+ }
+
+ // output vdat
+ if ( vdatSize )
+ {
+ targetBuff.Put( g_chunks[vdatChunk].pData, g_chunks[vdatChunk].size );
+ }
+
+ AlignToBoundary( targetBuff, XBOX_DVD_SECTORSIZE );
+
+ // write data
+ targetBuff.Put( pXMAData, XMADataSize );
+
+ // pad to EOF
+ AlignToBoundary( targetBuff, XBOX_DVD_SECTORSIZE );
+
+ free( pXMAData );
+ free( pXMA2Format );
+ free( pXMASeekTable );
+
+ // xma encoder leaves its temporary files, we'll delete
+ scriptlib->DeleteTemporaryFiles( "LoopStrm*" );
+ scriptlib->DeleteTemporaryFiles( "EncStrm*" );
+
+ return true;
+#endif
+}
+
+bool EncodeAsPCM( const char *pTargetName, CUtlBuffer &targetBuff )
+{
+ int formatChunk = FindChunk( WAVE_FMT );
+ int dataChunk = FindChunk( WAVE_DATA );
+ if ( formatChunk == -1 || dataChunk == -1 )
+ {
+ // huh? these should have been pre-validated
+ return false;
+ }
+
+ WAVEFORMATEX *pFormat = (WAVEFORMATEX *)g_chunks[formatChunk].pData;
+ if ( pFormat->wBitsPerSample != 16 )
+ {
+ // huh? the input is expeted to be 16 bit PCM
+ return false;
+ }
+
+ int vdatSize = 0;
+ int vdatChunk = FindChunk( WAVE_VALVEDATA );
+ if ( vdatChunk != -1 )
+ {
+ vdatSize = g_chunks[vdatChunk].size;
+ }
+
+ chunk_t *pDataChunk = &g_chunks[dataChunk];
+
+ xwvHeader_t header;
+ memset( &header, 0, sizeof( xwvHeader_t ) );
+
+ int sampleSize = pFormat->nChannels * sizeof( short );
+ int sampleCount = pDataChunk->size / sampleSize;
+
+ header.id = BigLong( XWV_ID );
+ header.version = BigLong( XWV_VERSION );
+ header.headerSize = BigLong( sizeof( xwvHeader_t ) );
+ header.staticDataSize = BigLong( vdatSize );
+ header.dataOffset = BigLong( AlignValue( sizeof( xwvHeader_t) + vdatSize, XBOX_DVD_SECTORSIZE ) );
+ header.dataSize = BigLong( pDataChunk->size );
+ header.numDecodedSamples = BigLong( sampleCount );
+ header.loopStart = BigLong( -1 );
+ header.loopBlock = 0;
+ header.numLeadingSamples = 0;
+ header.numTrailingSamples = 0;
+ header.vdatSize = BigShort( (short)vdatSize );
+ header.format = XWV_FORMAT_PCM;
+ header.bitsPerSample = 16;
+ header.SetSampleRate( pFormat->nSamplesPerSec );
+ header.SetChannels( pFormat->nChannels );
+ header.quality = 100;
+
+ // output header
+ targetBuff.Put( &header, sizeof( xwvHeader_t ) );
+
+ // output vdat
+ if ( vdatSize )
+ {
+ targetBuff.Put( g_chunks[vdatChunk].pData, g_chunks[vdatChunk].size );
+ }
+
+ AlignToBoundary( targetBuff, XBOX_DVD_SECTORSIZE );
+
+ for ( int i = 0; i < sampleCount * pFormat->nChannels; i++ )
+ {
+ ((short *)pDataChunk->pData)[i] = BigShort( ((short *)pDataChunk->pData)[i] );
+ }
+
+ // write data
+ targetBuff.Put( pDataChunk->pData, pDataChunk->size );
+
+ // pad to EOF
+ AlignToBoundary( targetBuff, XBOX_DVD_SECTORSIZE );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: read source, do work, and write to target
+//-----------------------------------------------------------------------------
+bool CreateTargetFile_WAV( const char *pSourceName, const char *pTargetName, bool bWriteToZip )
+{
+ g_numChunks = 0;
+
+ // resolve relative source to absolute path
+ char fullSourcePath[MAX_PATH];
+ if ( _fullpath( fullSourcePath, pSourceName, sizeof( fullSourcePath ) ) )
+ {
+ pSourceName = fullSourcePath;
+ }
+
+ if ( !ReadChunks( pSourceName, g_numChunks, g_chunks ) )
+ {
+ Msg( "No RIFF Chunks on '%s'\n", pSourceName );
+ return false;
+ }
+
+ int formatChunk = FindChunk( WAVE_FMT );
+ if ( formatChunk == -1 )
+ {
+ Msg( "RIFF Format Chunk not found on '%s'\n", pSourceName );
+ return false;
+ }
+
+ int dataChunk = FindChunk( WAVE_DATA );
+ if ( dataChunk == -1 )
+ {
+ Msg( "RIFF Data Chunk not found on '%s'\n", pSourceName );
+ return false;
+ }
+
+ // get the conversion rules
+ conversion_t *pConversion = g_defaultConversionRules;
+ if ( V_stristr( g_szModPath, "\\portal" ) )
+ {
+ pConversion = g_portalConversionRules;
+ }
+
+ // conversion rules are based on matching subdir
+ for ( int i=1; ;i++ )
+ {
+ char subString[MAX_PATH];
+ if ( !pConversion[i].pSubDir )
+ {
+ // end of list
+ break;
+ }
+
+ sprintf( subString, "\\%s\\", pConversion[i].pSubDir );
+ if ( V_stristr( pSourceName, subString ) )
+ {
+ // use matched conversion rules
+ pConversion = &pConversion[i];
+ break;
+ }
+ }
+
+ bool bForceTo22K = pConversion->bForceTo22K;
+ int quality = pConversion->quality;
+
+ // cannot trust the localization depots to have matched their sources
+ // cannot allow 44K
+ if ( IsLocalizedFile( pSourceName ) )
+ {
+ bForceTo22K = true;
+ }
+
+ // classify strict vo from /sound/vo only
+ bool bIsVoiceOver = V_stristr( pSourceName, "\\sound\\vo\\" ) != NULL;
+
+ // can override default settings
+ quality = CommandLine()->ParmValue( "-xmaquality", quality );
+ if ( quality < 0 )
+ quality = 0;
+ else if ( quality > 100 )
+ quality = 100;
+ if ( !g_bQuiet )
+ {
+ Msg( "Encoding quality: %d on '%s'\n", quality, pSourceName );
+ }
+
+ int vdatSize = 0;
+ int vdatChunk = FindChunk( WAVE_VALVEDATA );
+ if ( vdatChunk != -1 )
+ {
+ // compile to optimal block
+ if ( !CompressVDAT( &g_chunks[vdatChunk] ) )
+ {
+ Msg( "Compress VDAT Error on '%s'\n", pSourceName );
+ return false;
+ }
+ vdatSize = g_chunks[vdatChunk].size;
+ }
+
+ // for safety (not trusting their decoding) and simplicity convert all data to 16 bit PCM before encoding
+ WAVEFORMATEX *pFormat = (WAVEFORMATEX *)g_chunks[formatChunk].pData;
+ if ( ( pFormat->wFormatTag == WAVE_FORMAT_PCM ) )
+ {
+ if ( pFormat->wBitsPerSample == 8 )
+ {
+ ConvertPCMDataChunk8To16( &g_chunks[formatChunk], &g_chunks[dataChunk] );
+ }
+ }
+ else if ( pFormat->wFormatTag == WAVE_FORMAT_ADPCM )
+ {
+ ConvertADPCMDataChunkTo16( &g_chunks[formatChunk], &g_chunks[dataChunk] );
+ }
+ else
+ {
+ Msg( "Unknown RIFF Format on '%s'\n", pSourceName );
+ return false;
+ }
+
+ // optionally decimate to 22K
+ if ( pFormat->nSamplesPerSec == 44100 && bForceTo22K )
+ {
+ if ( !g_bQuiet )
+ {
+ Msg( "Converting to 22K '%s'\n", pSourceName );
+ }
+ ConvertPCMDataChunk16To22K( &g_chunks[formatChunk], &g_chunks[dataChunk] );
+ }
+
+ CUtlBuffer targetBuff;
+ bool bSuccess;
+
+ bSuccess = EncodeAsXMA( pSourceName, targetBuff, quality, bIsVoiceOver );
+ if ( bSuccess )
+ {
+ WriteBufferToFile( pTargetName, targetBuff, bWriteToZip, g_WriteModeForConversions );
+ }
+
+ // release data
+ for ( int i = 0; i < g_numChunks; i++ )
+ {
+ free( g_chunks[i].pData );
+ }
+
+ return bSuccess;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: MP3's are already pre-converted into .360.wav
+//-----------------------------------------------------------------------------
+bool CreateTargetFile_MP3( const char *pSourceName, const char *pTargetName, bool bWriteToZip )
+{
+ CUtlBuffer targetBuffer;
+
+ // ignore the .mp3 source, the .360.wav target should have been pre-converted, checked in, and exist
+ // use the expected target as the source
+ if ( !scriptlib->ReadFileToBuffer( pTargetName, targetBuffer ) )
+ {
+ // the .360.wav target does not exist
+ // try again using a .wav version and convert from that
+ char wavFilename[MAX_PATH];
+ V_StripExtension( pSourceName, wavFilename, sizeof( wavFilename ) );
+ V_SetExtension( wavFilename, ".wav", sizeof( wavFilename ) );
+ if ( scriptlib->DoesFileExist( wavFilename ) )
+ {
+ if ( CreateTargetFile_WAV( wavFilename, pTargetName, bWriteToZip ) )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // no conversion to write, but possibly zipped
+ bool bSuccess = WriteBufferToFile( pTargetName, targetBuffer, bWriteToZip, WRITE_TO_DISK_NEVER );
+ return bSuccess;
+}
+
+//-----------------------------------------------------------------------------
+// Get the preload data for a wav file
+//-----------------------------------------------------------------------------
+bool GetPreloadData_WAV( const char *pFilename, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut )
+{
+ xwvHeader_t *pHeader = ( xwvHeader_t * )fileBufferIn.Base();
+ if ( pHeader->id != ( unsigned int )BigLong( XWV_ID ) ||
+ pHeader->version != ( unsigned int )BigLong( XWV_VERSION ) ||
+ pHeader->headerSize != BigLong( sizeof( xwvHeader_t ) ) )
+ {
+ // bad version
+ Msg( "Can't preload: '%s', has bad version\n", pFilename );
+ return false;
+ }
+
+ // ensure caller's buffer is clean
+ // caller determines preload size, via TellMaxPut()
+ preloadBufferOut.Purge();
+ unsigned int preloadSize = BigLong( pHeader->headerSize ) + BigLong( pHeader->staticDataSize );
+ preloadBufferOut.Put( fileBufferIn.Base(), preloadSize );
+
+ return true;
+}
diff --git a/utils/xbox/MakeGameData/MakeTextures.cpp b/utils/xbox/MakeGameData/MakeTextures.cpp
new file mode 100644
index 0000000..f267367
--- /dev/null
+++ b/utils/xbox/MakeGameData/MakeTextures.cpp
@@ -0,0 +1,45 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: .360.VTF Creation
+//
+//=====================================================================================//
+
+#include "MakeGameData.h"
+#include "vtf/vtf.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Create the 360 VTF, use the library!
+//-----------------------------------------------------------------------------
+bool CreateTargetFile_VTF( const char *pSourceName, const char *pTargetName, bool bWriteToZip )
+{
+ CUtlBuffer sourceBuffer;
+ CUtlBuffer targetBuffer;
+
+ if ( !scriptlib->ReadFileToBuffer( pSourceName, sourceBuffer ) )
+ {
+ return false;
+ }
+
+ // using library conversion routine
+ bool bSuccess = ConvertVTFTo360Format( pSourceName, sourceBuffer, targetBuffer, CompressCallback );
+ if ( bSuccess )
+ {
+ bSuccess = WriteBufferToFile( pTargetName, targetBuffer, bWriteToZip, g_WriteModeForConversions );
+ }
+
+ return bSuccess;
+}
+
+//-----------------------------------------------------------------------------
+// Get the preload data for a vtf file
+//-----------------------------------------------------------------------------
+bool GetPreloadData_VTF( const char *pFilename, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut )
+{
+ if ( !GetVTFPreload360Data( pFilename, fileBufferIn, preloadBufferOut ) )
+ {
+ Msg( "Can't preload: '%s', has bad version\n", pFilename );
+ return false;
+ }
+
+ return true;
+} \ No newline at end of file
diff --git a/utils/xbox/MakeGameData/MakeZip.cpp b/utils/xbox/MakeGameData/MakeZip.cpp
new file mode 100644
index 0000000..69950b0
--- /dev/null
+++ b/utils/xbox/MakeGameData/MakeZip.cpp
@@ -0,0 +1,593 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "MakeGameData.h"
+
+static CUtlSymbolTable g_CriticalPreloadTable( 0, 32, true );
+
+//-----------------------------------------------------------------------------
+// Purpose: Compute preload data by file type. Calls into appropriate libraries
+// to get the preload info. Libraries would use filename to generate
+// the preload if there is a compilation step, otherwise the file buffer
+// is a buffer loaded filename.
+//-----------------------------------------------------------------------------
+static bool GetPreloadBuffer( const char *pFilename, CUtlBuffer &fileBuffer, CUtlBuffer &preloadBuffer )
+{
+ char fileExtension[MAX_PATH];
+ Q_ExtractFileExtension( pFilename, fileExtension, sizeof( fileExtension ) );
+
+ // adding an entire file IS ONLY for files that are expected to be read by the game as a single read
+ // NOT for files that have any seek pattern
+ bool bAddEntireFile = false;
+
+ // trivial small files, always add
+ if ( !Q_stricmp( fileExtension, "txt" ) ||
+ !Q_stricmp( fileExtension, "dat" ) ||
+ !Q_stricmp( fileExtension, "lst" ) ||
+ !Q_stricmp( fileExtension, "res" ) ||
+ !Q_stricmp( fileExtension, "vmt" ) ||
+ !Q_stricmp( fileExtension, "cfg" ) ||
+ !Q_stricmp( fileExtension, "bnf" ) ||
+ !Q_stricmp( fileExtension, "rc" ) ||
+ !Q_stricmp( fileExtension, "vbf" ) ||
+ !Q_stricmp( fileExtension, "vfe" ) ||
+ !Q_stricmp( fileExtension, "pcf" ) ||
+ !Q_stricmp( fileExtension, "inf" ) )
+ {
+ bAddEntireFile = true;
+ }
+
+ // critical resources get blindly added to the preload
+ if ( !bAddEntireFile && ( g_CriticalPreloadTable.Find( pFilename ) != UTL_INVAL_SYMBOL ) )
+ {
+ bAddEntireFile = true;
+ }
+
+ if ( bAddEntireFile && LZMA_IsCompressed( (unsigned char *)fileBuffer.Base() ) )
+ {
+ // sorry, not allowed to add entirely to preload if already compressed
+ // breaks the run-time filesystem due to inability to deliver file as-is
+ bAddEntireFile = false;
+ }
+
+ if ( bAddEntireFile )
+ {
+ if ( fileBuffer.TellMaxPut() >= 1*1024 )
+ {
+ // only compress preload files of reasonable size
+ unsigned int compressedSize = 0;
+ unsigned char *pCompressedOutput = LZMA_OpportunisticCompress( (unsigned char *)fileBuffer.Base(),
+ fileBuffer.TellMaxPut(), &compressedSize );
+ if ( pCompressedOutput )
+ {
+ // add as compressed
+ preloadBuffer.EnsureCapacity( compressedSize );
+ preloadBuffer.Put( pCompressedOutput, compressedSize );
+ free( pCompressedOutput );
+ return true;
+ }
+ }
+
+ // add entire file to preload section
+ preloadBuffer.EnsureCapacity( fileBuffer.TellMaxPut() );
+ preloadBuffer.Put( fileBuffer.Base(), fileBuffer.TellMaxPut() );
+ return true;
+ }
+
+ // Each library will fetch the optional preload data into caller's buffer
+ if ( !Q_stricmp( fileExtension, "wav" ) )
+ {
+ return GetPreloadData_WAV( pFilename, fileBuffer, preloadBuffer );
+ }
+ else if ( !Q_stricmp( fileExtension, "vtf" ) )
+ {
+ return GetPreloadData_VTF( pFilename, fileBuffer, preloadBuffer );
+ }
+ else if ( !Q_stricmp( fileExtension, "vcs" ) )
+ {
+ return GetPreloadData_VCS( pFilename, fileBuffer, preloadBuffer );
+ }
+ else if ( !Q_stricmp( fileExtension, "vhv" ) )
+ {
+ return GetPreloadData_VHV( pFilename, fileBuffer, preloadBuffer );
+ }
+ else if ( !Q_stricmp( fileExtension, "vtx" ) )
+ {
+ return GetPreloadData_VTX( pFilename, fileBuffer, preloadBuffer );
+ }
+ else if ( !Q_stricmp( fileExtension, "vvd" ) )
+ {
+ return GetPreloadData_VVD( pFilename, fileBuffer, preloadBuffer );
+ }
+
+ // others...
+ return false;
+}
+
+void SetupCriticalPreloadScript( const char *pModPath )
+{
+ characterset_t breakSet;
+ CharacterSetBuild( &breakSet, "" );
+
+ // purge any prior entries
+ g_CriticalPreloadTable.RemoveAll();
+
+ // populate table
+ char szCriticaList[MAX_PATH];
+ char szToken[MAX_PATH];
+ V_ComposeFileName( pModPath, "scripts\\preload_xbox.xsc", szCriticaList, sizeof( szCriticaList ) );
+ CUtlBuffer criticalListBuffer;
+ if ( ReadFileToBuffer( szCriticaList, criticalListBuffer, true, true ) )
+ {
+ for ( ;; )
+ {
+ int nTokenSize = criticalListBuffer.ParseToken( &breakSet, szToken, sizeof( szToken ) );
+ if ( nTokenSize <= 0 )
+ {
+ break;
+ }
+
+ V_strlower( szToken );
+ V_FixSlashes( szToken, CORRECT_PATH_SEPARATOR );
+ if ( UTL_INVAL_SYMBOL == g_CriticalPreloadTable.Find( szToken ) )
+ {
+ g_CriticalPreloadTable.AddString( szToken );
+ }
+ }
+ }
+}
+
+CXZipTool::CXZipTool()
+{
+ m_pZip = NULL;
+ m_hPreloadFile = INVALID_HANDLE_VALUE;
+ m_hOutputZipFile = INVALID_HANDLE_VALUE;
+ m_PreloadFilename[0] = '\0';
+}
+
+CXZipTool::~CXZipTool()
+{
+ Reset();
+}
+
+void CXZipTool::Reset()
+{
+ if ( m_hOutputZipFile != INVALID_HANDLE_VALUE )
+ {
+ CloseHandle( m_hOutputZipFile );
+ m_hOutputZipFile = INVALID_HANDLE_VALUE;
+ }
+
+ if ( m_hPreloadFile != INVALID_HANDLE_VALUE )
+ {
+ CloseHandle( m_hPreloadFile );
+ m_hPreloadFile = INVALID_HANDLE_VALUE;
+ }
+
+ if ( m_PreloadFilename[0] )
+ {
+ DeleteFile( m_PreloadFilename );
+ m_PreloadFilename[0] = '\0';
+ }
+
+ if ( m_pZip )
+ {
+ IZip::ReleaseZip( m_pZip );
+ m_pZip = NULL;
+ }
+
+ m_ZipPreloadDirectoryEntries.Purge();
+ m_ZipCRCList.Purge();
+ m_ZipPreloadRemapEntries.Purge();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a file buffer to the zip
+//-----------------------------------------------------------------------------
+bool CXZipTool::AddBuffer( const char *pFilename, CUtlBuffer &fileBuffer, bool bDoPreload )
+{
+ if ( !m_pZip )
+ {
+ return false;
+ }
+
+ // safely strip unecessary prefix, otherise pollutes CRC
+ if ( !strnicmp( pFilename, ".\\", 2 ) )
+ {
+ pFilename += 2;
+ }
+
+ // scan for CRC collision now, not at runtime
+ CRCEntry_t crcEntry;
+ crcEntry.fileNameCRC = HashStringCaselessConventional( pFilename );
+ crcEntry.filename = pFilename;
+ int idx = m_ZipCRCList.Find( crcEntry );
+ if ( -1 != idx )
+ {
+ if ( !V_stricmp( pFilename, m_ZipCRCList[idx].filename.String() ) )
+ {
+ // file has already been added, ignore as succesful
+ return true;
+ }
+
+ Msg( "ERROR: CRC Collision: '%s' with '%s'\n", pFilename, m_ZipCRCList[idx].filename.String() );
+ return false;
+ }
+ else
+ {
+ // add unique entry to lists
+ // must track filenames in a non-sort manner
+ m_ZipCRCList.Insert( crcEntry );
+ }
+
+ // default, no preload entry
+ unsigned short preloadDir = INVALID_PRELOAD_ENTRY;
+
+ if ( bDoPreload )
+ {
+ CUtlBuffer preloadBuffer;
+ bool bHasPreload = GetPreloadBuffer( pFilename, fileBuffer, preloadBuffer );
+ int preloadSize = preloadBuffer.TellMaxPut();
+ if ( bHasPreload && preloadSize > 0 )
+ {
+ if ( m_ZipPreloadDirectoryEntries.Count() >= 65534 )
+ {
+ Msg( "ERROR: Preload section FULL!, skipping %s\n", pFilename );
+ return FALSE;
+ }
+
+ // Initialize the entry header
+ ZIP_PreloadDirectoryEntry entry;
+ memset( &entry, 0, sizeof( entry ) );
+
+ entry.Length = preloadSize;
+ entry.DataOffset = SetFilePointer( m_hPreloadFile, 0, NULL, FILE_CURRENT );
+
+ // Add the directory entry to the preload table
+ preloadDir = m_ZipPreloadDirectoryEntries.AddToTail( entry );
+
+ // Append the preload data to the preload file
+ DWORD numBytesWritten;
+ BOOL bOK = WriteFile( m_hPreloadFile, preloadBuffer.Base(), preloadSize, &numBytesWritten, NULL );
+ if ( !bOK || preloadSize != numBytesWritten )
+ {
+ Msg( "ERROR: writing %d preload bytes of '%s'\n", preloadSize, pFilename );
+ return false;
+ }
+
+ if ( !g_bQuiet )
+ {
+ // Spew it
+ if ( LZMA_IsCompressed( (unsigned char *)preloadBuffer.Base() ) )
+ {
+ unsigned int actualSize = LZMA_GetActualSize( (unsigned char *)preloadBuffer.Base() );
+ Msg( "Preload: '%s': Compressed:%u Actual:%u\n", pFilename, preloadSize, actualSize );
+ }
+ else
+ {
+ Msg( "Preload: '%s': Length:%u\n", pFilename, preloadSize );
+ }
+ }
+ }
+ }
+
+ unsigned int fileSize = fileBuffer.TellMaxPut();
+ if ( fileSize > 0 )
+ {
+ // order in zip is sorted, not sequential
+ m_pZip->AddBufferToZip( pFilename, fileBuffer.Base(), fileSize, false );
+
+ // track the file in the zip directory to it's preload entry
+ // order in preload is sequential as buffers are added
+ preloadRemap_t remap;
+ remap.filename = pFilename;
+ remap.preloadDirIndex = preloadDir;
+ m_ZipPreloadRemapEntries.AddToTail( remap );
+
+ if ( !g_bQuiet )
+ {
+ Msg( "File: '%s': Length:%u\n", pFilename, fileSize );
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Load a file and add it to the zip
+//-----------------------------------------------------------------------------
+bool CXZipTool::AddFile( const char *pFilename, bool bDoPreload )
+{
+ if ( !m_pZip )
+ {
+ return false;
+ }
+
+ FILE* pFile = fopen( pFilename, "rb" );
+ if( !pFile )
+ {
+ Msg( "ERROR: failed to open file: '%s'\n", pFilename );
+ return false;
+ }
+
+ // Get the length of the file
+ fseek( pFile, 0, SEEK_END );
+ unsigned fileSize = ftell( pFile );
+ fseek( pFile, 0, SEEK_SET);
+
+ // read file to buffer
+ CUtlBuffer fileBuffer;
+ fileBuffer.EnsureCapacity( fileSize );
+ fread( fileBuffer.Base(), fileSize, 1, pFile );
+ fclose( pFile );
+ fileBuffer.SeekPut( CUtlBuffer::SEEK_HEAD, fileSize );
+
+ return AddBuffer( pFilename, fileBuffer, bDoPreload );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add the preload section and save out the zip file
+//-----------------------------------------------------------------------------
+bool CXZipTool::End()
+{
+ if ( !m_pZip )
+ {
+ return false;
+ }
+
+ // Add the preload section to the zip
+ if ( m_ZipPreloadDirectoryEntries.Count() )
+ {
+ CUtlBuffer sectionBuffer;
+ CByteswap byteSwap;
+
+ // pc tools write 360 native data
+ byteSwap.ActivateByteSwapping( IsPC() );
+
+ // determine the preload data footprint
+ unsigned int preloadDataSize = SetFilePointer( m_hPreloadFile, 0, NULL, FILE_CURRENT );
+
+ // finalize header
+ m_ZipPreloadHeader.DirectoryEntries = m_ZipPreloadRemapEntries.Count();
+ m_ZipPreloadHeader.PreloadDirectoryEntries = m_ZipPreloadDirectoryEntries.Count();
+
+ // determine the total section size ( treated as a single file inside zip )
+ unsigned int sectionSize = sizeof( ZIP_PreloadHeader ) +
+ m_ZipPreloadHeader.PreloadDirectoryEntries * sizeof( ZIP_PreloadDirectoryEntry ) +
+ m_ZipPreloadHeader.DirectoryEntries * sizeof( unsigned short ) +
+ preloadDataSize;
+ sectionSize = AlignValue( sectionSize, m_ZipPreloadHeader.Alignment );
+ sectionBuffer.EnsureCapacity( sectionSize );
+
+ // save data in order
+ // save the header
+ byteSwap.SwapFieldsToTargetEndian( &m_ZipPreloadHeader );
+ sectionBuffer.Put( &m_ZipPreloadHeader, sizeof( ZIP_PreloadHeader ) );
+
+ // fixup and save the preload directory
+ for ( int i=0; i<m_ZipPreloadDirectoryEntries.Count(); i++ )
+ {
+ ZIP_PreloadDirectoryEntry entry = m_ZipPreloadDirectoryEntries[i];
+ byteSwap.SwapFieldsToTargetEndian( &entry );
+ sectionBuffer.Put( &entry, sizeof( ZIP_PreloadDirectoryEntry ) );
+ }
+
+ // generate remap table
+ char fileName[MAX_PATH];
+ int fileSize;
+ int zipIndex = -1;
+ unsigned short *pRemapTable = (unsigned short *)malloc( m_ZipPreloadRemapEntries.Count() * sizeof( unsigned short ) );
+ for ( int i=0; i<m_ZipPreloadRemapEntries.Count(); i++ )
+ {
+ // zip files get iterated in the same order they are serialized to disk
+ fileName[0] = '\0';
+ fileSize = 0;
+ zipIndex = m_pZip->GetNextFilename( zipIndex, fileName, sizeof( fileName ), fileSize );
+
+ // find the file in the preload remap table
+ bool bFound = false;
+ int j;
+ for ( j=0; j<m_ZipPreloadRemapEntries.Count(); j++ )
+ {
+ if ( !Q_stricmp( fileName, m_ZipPreloadRemapEntries[j].filename.String() ) )
+ {
+ bFound = true;
+ break;
+ }
+ }
+ if ( !bFound )
+ {
+ // shouldn't happen, every file in the zip has a matching preload remap entry, that is valid or marked invalid
+ Msg( "ERROR: file '%s' was expected to have an entry in preload table\n", fileName );
+ }
+
+ // the remap table is used to go find a file's preload data (if available)
+ pRemapTable[i] = m_ZipPreloadRemapEntries[j].preloadDirIndex;
+ }
+ for ( int i=0; i<m_ZipPreloadRemapEntries.Count(); i++ )
+ {
+ unsigned short s = pRemapTable[i];
+ sectionBuffer.PutShort( BigShort( s ) );
+ }
+ free( pRemapTable );
+
+ // get and save preload data
+ void *pPreloadData = malloc( preloadDataSize );
+ SetFilePointer( m_hPreloadFile, 0, NULL, FILE_BEGIN );
+ DWORD numBytesRead;
+ BOOL bOK = ReadFile( m_hPreloadFile, pPreloadData, preloadDataSize, &numBytesRead, NULL );
+ if ( !bOK || numBytesRead != preloadDataSize )
+ {
+ Msg( "ERROR: failed to read %d bytes from temporary preload file\n", preloadDataSize );
+ }
+ CloseHandle( m_hPreloadFile );
+ m_hPreloadFile = INVALID_HANDLE_VALUE;
+
+ sectionBuffer.Put( pPreloadData, preloadDataSize );
+ free( pPreloadData );
+
+ // cannot have written more than was pre-calced, unless code was broken
+ Assert( (unsigned int)sectionBuffer.TellMaxPut() <= sectionSize );
+ while( (unsigned int)sectionBuffer.TellMaxPut() < sectionSize )
+ {
+ // pad to final alignment
+ sectionBuffer.PutChar( 0 );
+ }
+
+ m_pZip->AddBufferToZip( PRELOAD_SECTION_NAME, sectionBuffer.Base(), sectionBuffer.TellMaxPut(), false );
+ }
+ else
+ {
+ // Clear the preload section placeholder
+ m_pZip->RemoveFileFromZip( PRELOAD_SECTION_NAME );
+ }
+
+ m_pZip->SaveToDisk( m_hOutputZipFile );
+
+ Reset();
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create the zip file
+//-----------------------------------------------------------------------------
+bool CXZipTool::Begin( const char *pZipFileName, unsigned int alignment )
+{
+ // get the volume of the target zip
+ char drivePath[MAX_PATH];
+ _splitpath( pZipFileName, drivePath, NULL, NULL, NULL );
+
+ m_pZip = IZip::CreateZip( drivePath, true );
+
+ if ( alignment )
+ {
+ // making an aligned zip that uses an optimized (but incompatible) format
+ m_pZip->ForceAlignment( true, false, alignment );
+ }
+
+ // Open the output file
+ m_hOutputZipFile = CreateFile( pZipFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
+ if ( m_hOutputZipFile == INVALID_HANDLE_VALUE )
+ {
+ Msg( "ERROR: failed to create zip file '%s'\n", pZipFileName );
+ return false;
+ }
+
+ // Create a temporary file for storing the preloaded data
+ scriptlib->MakeTemporaryFilename( g_szModPath, m_PreloadFilename, sizeof( m_PreloadFilename ) );
+ m_hPreloadFile = CreateFile( m_PreloadFilename, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
+ if ( m_hPreloadFile == INVALID_HANDLE_VALUE )
+ {
+ Msg( "ERROR: failed to create temporary file '%s' for preload data\n", m_PreloadFilename );
+ CloseHandle( m_hOutputZipFile );
+ m_hOutputZipFile = INVALID_HANDLE_VALUE;
+ return false;
+ }
+ memset( &m_ZipPreloadHeader, 0, sizeof( ZIP_PreloadHeader ) );
+
+ m_ZipPreloadHeader.Version = PRELOAD_HDR_VERSION;
+ m_ZipPreloadHeader.Alignment = alignment;
+
+ // Add a placeholder for the preload section
+ m_pZip->AddBufferToZip( PRELOAD_SECTION_NAME, NULL, 0, false );
+ preloadRemap_t remap;
+ remap.filename = PRELOAD_SECTION_NAME;
+ remap.preloadDirIndex = INVALID_PRELOAD_ENTRY;
+ m_ZipPreloadRemapEntries.AddToTail( remap );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Dump the preload contents
+//-----------------------------------------------------------------------------
+void CXZipTool::SpewPreloadInfo( const char *pZipName )
+{
+ IZip *pZip = IZip::CreateZip( NULL, true );
+
+ HANDLE hZipFile = pZip->ParseFromDisk( pZipName );
+ if ( !hZipFile )
+ {
+ Msg( "Bad or missing zip file, failed to open '%s'\n", pZipName );
+ return;
+ }
+
+ CUtlBuffer preloadBuffer;
+ if ( !pZip->ReadFileFromZip( hZipFile, PRELOAD_SECTION_NAME, false, preloadBuffer ) )
+ {
+ Msg( "No preload info for '%s'\n", pZipName );
+ return;
+ }
+
+ preloadBuffer.ActivateByteSwapping( IsPC() );
+
+ ZIP_PreloadHeader header;
+ preloadBuffer.GetObjects( &header );
+
+ // get the dir table
+ ZIP_PreloadDirectoryEntry *pDir = (ZIP_PreloadDirectoryEntry *)malloc( header.PreloadDirectoryEntries * sizeof( ZIP_PreloadDirectoryEntry ) );
+ preloadBuffer.GetObjects( pDir, header.PreloadDirectoryEntries );
+
+ // get the remap table
+ unsigned short *pRemap = (unsigned short *)malloc( header.DirectoryEntries * sizeof( unsigned short ) );
+ for ( unsigned int i = 0; i < header.DirectoryEntries; i++ )
+ {
+ pRemap[i] = preloadBuffer.GetShort();
+ }
+
+ int zipIndex = -1;
+ int fileSize;
+ char fileName[MAX_PATH];
+
+ // iterate preload entries sequentially
+ CUtlDict< unsigned int, int > sizes( true );
+ for ( unsigned int i = 0; i < header.DirectoryEntries; i++ )
+ {
+ fileName[0] = '\0';
+ fileSize = 0;
+ zipIndex = pZip->GetNextFilename( zipIndex, fileName, sizeof( fileName ), fileSize );
+
+ unsigned short zipPreloadDirIndex = pRemap[i];
+ if ( zipPreloadDirIndex == INVALID_PRELOAD_ENTRY )
+ {
+ continue;
+ }
+
+ Msg( "Offset: 0x%8.8x Length: %5d %s (%d)\n", pDir[zipPreloadDirIndex].DataOffset, pDir[zipPreloadDirIndex].Length, fileName, fileSize );
+
+ // total preload sizes by extension
+ const char *pExt = V_GetFileExtension( fileName );
+ if ( !pExt )
+ {
+ pExt = "???";
+ }
+ int iIndex = sizes.Find( pExt );
+ if ( iIndex == sizes.InvalidIndex() )
+ {
+ iIndex = sizes.Insert( pExt );
+ sizes[iIndex] = 0;
+ }
+ sizes[iIndex] += pDir[zipPreloadDirIndex].Length;
+ }
+
+ Msg( "\n" );
+ Msg( "Preload Size: %.2f MB\n", (float)preloadBuffer.TellMaxPut()/(1024.0f * 1024.0f) );
+ Msg( "Zip Entries: %d\n", header.DirectoryEntries );
+ Msg( "Preload Entries: %d\n", header.PreloadDirectoryEntries );
+
+ // dump each extension's total size, necessary for debugging who is the largest contributor
+ for ( int i = 0; i < sizes.Count(); i++ )
+ {
+ Msg( "Extension: '%3s' %d bytes (%.2f%s)\n", sizes.GetElementName( i ), sizes[i], (float)sizes[i]/(float)preloadBuffer.TellMaxPut() * 100.0f, "%%" );
+ }
+ Msg( "\n" );
+
+ free( pRemap );
+ free( pDir );
+
+ IZip::ReleaseZip( pZip );
+}
diff --git a/utils/xbox/MakeGameData/XZipTool.h b/utils/xbox/MakeGameData/XZipTool.h
new file mode 100644
index 0000000..41713cf
--- /dev/null
+++ b/utils/xbox/MakeGameData/XZipTool.h
@@ -0,0 +1,61 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=====================================================================================//
+
+#pragma once
+
+#include <stdio.h>
+#include "utlbuffer.h"
+#include "zip_uncompressed.h"
+#include "generichash.h"
+#include "zip_utils.h"
+#include "byteswap.h"
+#include "tier1/UtlVector.h"
+#include "UtlSortVector.h"
+
+struct CRCEntry_t
+{
+ unsigned int fileNameCRC;
+ CUtlString filename;
+};
+
+struct preloadRemap_t
+{
+ CUtlString filename;
+ unsigned short preloadDirIndex;
+};
+
+class CZipCRCLessFunc
+{
+public:
+ bool Less( CRCEntry_t const& src1, CRCEntry_t const& src2, void *pCtx )
+ {
+ return ( src1.fileNameCRC < src2.fileNameCRC );
+ }
+};
+
+class CXZipTool
+{
+public:
+ CXZipTool();
+ ~CXZipTool();
+
+ void Reset();
+ bool Begin( const char* pFileName, unsigned int alignment = 0 );
+ bool End();
+ bool AddBuffer( const char* pFileName, CUtlBuffer &buffer, bool bPreload = true );
+ bool AddFile( const char* pFileName, bool bPreload = true );
+ void SpewPreloadInfo( const char *pZipName );
+
+private:
+ IZip *m_pZip;
+ char m_PreloadFilename[MAX_PATH];
+ HANDLE m_hPreloadFile;
+ HANDLE m_hOutputZipFile;
+ ZIP_PreloadHeader m_ZipPreloadHeader;
+ CUtlVector< ZIP_PreloadDirectoryEntry > m_ZipPreloadDirectoryEntries;
+ CUtlSortVector< CRCEntry_t, CZipCRCLessFunc > m_ZipCRCList;
+ CUtlVector< preloadRemap_t > m_ZipPreloadRemapEntries;
+}; \ No newline at end of file
diff --git a/utils/xbox/MakeGameData/imaadpcm.cpp b/utils/xbox/MakeGameData/imaadpcm.cpp
new file mode 100644
index 0000000..6648669
--- /dev/null
+++ b/utils/xbox/MakeGameData/imaadpcm.cpp
@@ -0,0 +1,1531 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+/***************************************************************************
+ *
+ * Copyright (C) 2001 Microsoft Corporation. All Rights Reserved.
+ *
+ * File: imaadpcm.cpp
+ * Content: IMA ADPCM CODEC.
+ * History:
+ * Date By Reason
+ * ==== == ======
+ * 04/29/01 dereks Created.
+ * 06/12/01 jharding Adapted for command-line encode
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <wtypes.h>
+#include <assert.h>
+#include "imaadpcm.h"
+
+// 1/2 the range of the searchable step indices
+// for a particular block when optimizing on a
+// per-block basis. Widening or narrowing this
+// range may produce better/worse encodings.
+// Experimentation may be necessary. Higher values
+// cause each block to be encoded better, but may
+// produce popping in particularly fast attacks across
+// blocks, while smaller values limit the number
+// of encodings you consider
+#define STEPINDEXSEARCHRANGE (24)
+
+/****************************************************************************
+ *
+ * CImaAdpcmCodec
+ *
+ * Description:
+ * Object constructor.
+ *
+ * Arguments:
+ * (void)
+ *
+ * Returns:
+ * (void)
+ *
+ ****************************************************************************/
+
+//
+// This array is used by NextStepIndex to determine the next step index to use.
+// The step index is an index to the m_asStep[] array, below.
+//
+
+const short CImaAdpcmCodec::m_asNextStep[16] =
+{
+ -1, -1, -1, -1, 2, 4, 6, 8,
+ -1, -1, -1, -1, 2, 4, 6, 8
+};
+
+//
+// This array contains the array of step sizes used to encode the ADPCM
+// samples. The step index in each ADPCM block is an index to this array.
+//
+
+const short CImaAdpcmCodec::m_asStep[89] =
+{
+ 7, 8, 9, 10, 11, 12, 13,
+ 14, 16, 17, 19, 21, 23, 25,
+ 28, 31, 34, 37, 41, 45, 50,
+ 55, 60, 66, 73, 80, 88, 97,
+ 107, 118, 130, 143, 157, 173, 190,
+ 209, 230, 253, 279, 307, 337, 371,
+ 408, 449, 494, 544, 598, 658, 724,
+ 796, 876, 963, 1060, 1166, 1282, 1411,
+ 1552, 1707, 1878, 2066, 2272, 2499, 2749,
+ 3024, 3327, 3660, 4026, 4428, 4871, 5358,
+ 5894, 6484, 7132, 7845, 8630, 9493, 10442,
+ 11487, 12635, 13899, 15289, 16818, 18500, 20350,
+ 22385, 24623, 27086, 29794, 32767
+};
+
+CImaAdpcmCodec::CImaAdpcmCodec
+(
+ void
+)
+{
+}
+
+
+/****************************************************************************
+ *
+ * ~CImaAdpcmCodec
+ *
+ * Description:
+ * Object destructor.
+ *
+ * Arguments:
+ * (void)
+ *
+ * Returns:
+ * (void)
+ *
+ ****************************************************************************/
+
+CImaAdpcmCodec::~CImaAdpcmCodec
+(
+ void
+)
+{
+}
+
+
+/****************************************************************************
+ *
+ * Initialize
+ *
+ * Description:
+ * Initializes the object.
+ *
+ * Arguments:
+ * LPCIMAADPCMWAVEFORMAT [in]: encoded data format.
+ * BOOL [in]: TRUE to initialize the object as an encoder.
+ *
+ * Returns:
+ * BOOL: TRUE on success.
+ *
+ ****************************************************************************/
+
+BOOL
+CImaAdpcmCodec::Initialize
+(
+ LPCIMAADPCMWAVEFORMAT pwfxEncode,
+ CODEC_MODE cmCodecMode
+)
+{
+ static const LPFNIMAADPCMCONVERT apfnConvert[2][2] =
+ {
+ {
+ DecodeM16,
+ DecodeS16
+ },
+ {
+ EncodeM16,
+ EncodeS16
+ }
+ };
+
+ if(!IsValidImaAdpcmFormat(pwfxEncode))
+ {
+ return FALSE;
+ }
+
+ //
+ // Save the format data
+ //
+
+ m_wfxEncode = *pwfxEncode;
+ m_cmCodecMode = cmCodecMode;
+
+ //
+ // Set up the conversion function
+ //
+
+ m_pfnConvert = apfnConvert[!(m_cmCodecMode == CODEC_MODE_DECODE)][m_wfxEncode.wfx.nChannels - 1];
+
+ //
+ // Initialize the stepping indices
+ //
+
+ m_nStepIndexL = m_nStepIndexR = 0;
+
+ return TRUE;
+}
+
+
+/****************************************************************************
+ *
+ * Convert
+ *
+ * Description:
+ * Converts data from the source to destination format.
+ *
+ * Arguments:
+ * LPCVOID [in]: source buffer.
+ * LPVOID [out]: destination buffer.
+ * UINT [in]: block count.
+ *
+ * Returns:
+ * BOOL: TRUE on success.
+ *
+ ****************************************************************************/
+
+BOOL
+CImaAdpcmCodec::Convert
+(
+ LPCVOID pvSrc,
+ LPVOID pvDst,
+ UINT cBlocks
+)
+{
+ // Array of decoders
+ static const LPFNIMAADPCMCONVERT apfnDecoders[2] =
+ {
+ DecodeM16,
+ DecodeS16
+ };
+
+ // Both destination and source block sizes
+ DWORD dwSrcBlockSize = m_wfxEncode.wfx.nChannels * m_wfxEncode.wSamplesPerBlock * sizeof(short);
+ DWORD dwDstBlockSize = m_wfxEncode.wfx.nBlockAlign;
+
+ // Zero out the output
+ ZeroMemory( pvDst, cBlocks * dwDstBlockSize );
+
+ switch( m_cmCodecMode )
+ {
+ case CODEC_MODE_DECODE:
+ // If we are decoding, just do it
+ return m_pfnConvert( (LPBYTE)pvSrc, (LPBYTE)pvDst, cBlocks, m_wfxEncode.wfx.nBlockAlign, m_wfxEncode.wSamplesPerBlock, &m_nStepIndexL, &m_nStepIndexR );
+
+ case CODEC_MODE_ENCODE_NORMAL:
+ // Normal encode
+ // We have some output right now, so this becomes a separate case.
+ // Otherwise, it would be the same as CODEC_MODE_DECODE
+ {
+ printf("Using normal encoding...\n");
+
+ // Allocate temporary buffers
+ LPBYTE pvDecoded = new BYTE[cBlocks * dwSrcBlockSize];
+ if( !pvDecoded )
+ return FALSE;
+
+ // Find the decoder
+ LPFNIMAADPCMCONVERT pfnOppConvert;
+ pfnOppConvert = apfnDecoders[m_wfxEncode.wfx.nChannels - 1];
+
+ // Encode the stream
+ if( !m_pfnConvert( (LPBYTE)pvSrc, (LPBYTE)pvDst, cBlocks, m_wfxEncode.wfx.nBlockAlign, m_wfxEncode.wSamplesPerBlock, &m_nStepIndexL, &m_nStepIndexR ) )
+ {
+ delete[] pvDecoded;
+ return FALSE;
+ }
+
+ // Decode it back
+ if( !pfnOppConvert( (LPBYTE)pvDst, pvDecoded, cBlocks, m_wfxEncode.wfx.nBlockAlign, XBOX_ADPCM_SAMPLES_PER_BLOCK, &m_nStepIndexL, &m_nStepIndexR ) )
+ {
+ delete[] pvDecoded;
+ return FALSE;
+ }
+
+ // Report the normal difference
+ printf( "Difference between original and decoded streams: 0x%I64x\n",
+ CalcDifference( (LPBYTE)pvSrc, pvDecoded, cBlocks, cBlocks, dwSrcBlockSize ) );
+
+ delete[] pvDecoded;
+ }
+ break;
+
+ case CODEC_MODE_ENCODE_OPTIMIZE_WHOLE_FILE:
+ // Optimize whole file encode
+ // Encode the file with each possible starting step index
+ // and pick the best one
+ {
+ printf("Using whole file encoding...\n");
+
+ // Allocate temporary buffers
+ LPBYTE pvTempDst = new BYTE[cBlocks * dwDstBlockSize];
+ if(!pvTempDst)
+ return FALSE;
+
+ LPBYTE pvDecoded = new BYTE[cBlocks * dwSrcBlockSize];
+ if(!pvDecoded)
+ {
+ delete[] pvTempDst;
+ return FALSE;
+ }
+
+ // Find the decoder
+ LPFNIMAADPCMCONVERT pfnOppConvert;
+ pfnOppConvert = apfnDecoders[m_wfxEncode.wfx.nChannels - 1];
+
+ // Keep track of the best encoding, as well as the chosen step index
+ ULONGLONG ullLeastDiff = (ULONGLONG)-1;
+ UINT uChosen = (UINT)-1;
+
+ // Encode the entire stream with each step index and choose the best one
+ for( UINT i = 0; i < ARRAYSIZE(m_asStep); ++i )
+ {
+ ZeroMemory( pvTempDst, cBlocks * dwDstBlockSize );
+ ZeroMemory( pvDecoded, cBlocks * dwSrcBlockSize );
+
+ // Encode
+ m_nStepIndexL = m_nStepIndexR = i;
+ if( !m_pfnConvert( (LPBYTE)pvSrc, pvTempDst, cBlocks, m_wfxEncode.wfx.nBlockAlign, m_wfxEncode.wSamplesPerBlock, &m_nStepIndexL, &m_nStepIndexR ) )
+ continue;
+
+ // Decode
+ if( !pfnOppConvert( pvTempDst, pvDecoded, cBlocks, m_wfxEncode.wfx.nBlockAlign, XBOX_ADPCM_SAMPLES_PER_BLOCK, &m_nStepIndexL, &m_nStepIndexR ) )
+ continue;
+
+ // Diff
+ ULONGLONG ullDiff = CalcDifference( (LPBYTE)pvSrc, pvDecoded, cBlocks, cBlocks, dwSrcBlockSize );
+ if( ullDiff < ullLeastDiff )
+ {
+ ullLeastDiff = ullDiff;
+ uChosen = i;
+ CopyMemory( (LPBYTE)pvDst, pvTempDst, cBlocks * dwDstBlockSize );
+ }
+ }
+
+ // Report the optimized difference
+ printf( "Difference between original and decoded streams: 0x%I64x\n", ullLeastDiff );
+ printf( "Step index chosen: %d\n", uChosen );
+
+ delete[] pvTempDst;
+ delete[] pvDecoded;
+ }
+ break;
+
+ case CODEC_MODE_ENCODE_OPTIMIZE_EACH_BLOCK:
+ // Optimize per block encode
+ // Encode each block within the file with each
+ // possible starting step index and pick the
+ // best one for each block
+ {
+ printf("Using per-block encoding\n\n");
+
+ // Allocate temporary buffers
+ LPBYTE pvTempDst = new BYTE[dwDstBlockSize];
+ if( !pvTempDst )
+ return FALSE;
+
+ LPBYTE pvDecoded = new BYTE[dwSrcBlockSize];
+ if( !pvDecoded )
+ {
+ delete[] pvTempDst;
+ return FALSE;
+ }
+
+ // Find the decoder
+ LPFNIMAADPCMCONVERT pfnOppConvert;
+ pfnOppConvert = apfnDecoders[m_wfxEncode.wfx.nChannels - 1];
+
+ // We keep track of the best step index of the previous block
+ // This enables us to search a small range of values close to
+ // this value (the size of 2*STEPINDEXSEARCHRANGE)
+ // To begin, the previous block's best step index was -1.
+ INT iPreviousBestStepIndex = -1;
+
+ for( UINT c = 0; c < cBlocks; ++c )
+ {
+ ULONGLONG ullLeastDiff = (ULONGLONG)-1;
+ INT iThisBestStepIndex = -1;
+ INT iStartIndex, iStopIndex;
+
+ // Setup the start/stop indices properly
+ if( iPreviousBestStepIndex == -1 )
+ {
+ // If the previous best step index is -1,
+ // then we haven't yet encoded a block. Search
+ // through the entire range of step indices,
+ // rather than just in a limited range
+ iStartIndex = 0;
+ iStopIndex = ARRAYSIZE( m_asStep );
+ }
+ else
+ {
+ // Keep the range of indices to search limited
+ // to around the previously chosen step index
+ iStartIndex = iPreviousBestStepIndex - STEPINDEXSEARCHRANGE;
+ iStopIndex = iPreviousBestStepIndex + STEPINDEXSEARCHRANGE + 1;
+ }
+
+ // Try each step index in the searchable range and choose the best one
+ // for this block
+ for( INT i = iStartIndex; i < iStopIndex; ++i )
+ {
+ // Don't consider anything out of range
+ if( i < 0 || i >= ARRAYSIZE( m_asStep ) )
+ continue;
+
+ // Zero out the temporary buffers
+ ZeroMemory( pvTempDst, dwDstBlockSize );
+ ZeroMemory( pvDecoded, dwSrcBlockSize );
+
+ // Encode
+ m_nStepIndexL = m_nStepIndexR = i;
+ if( !m_pfnConvert( (LPBYTE)pvSrc + c*dwSrcBlockSize, pvTempDst, 1, m_wfxEncode.wfx.nBlockAlign, m_wfxEncode.wSamplesPerBlock, &m_nStepIndexL, &m_nStepIndexR ) )
+ continue;
+
+ // Decode
+ if( !pfnOppConvert( pvTempDst, pvDecoded, 1, m_wfxEncode.wfx.nBlockAlign, XBOX_ADPCM_SAMPLES_PER_BLOCK, &m_nStepIndexL, &m_nStepIndexR ) )
+ continue;
+
+ // Diff
+ ULONGLONG ullDiff = CalcDifference( (LPBYTE)pvSrc + c*dwSrcBlockSize, pvDecoded, 1, cBlocks, dwSrcBlockSize );
+ if( ullDiff < ullLeastDiff )
+ {
+ ullLeastDiff = ullDiff;
+ iThisBestStepIndex = i;
+ CopyMemory( (LPBYTE)pvDst + c*dwDstBlockSize, (LPBYTE)pvTempDst, dwDstBlockSize );
+ }
+ }
+
+ // Save the best step index for this block
+ iPreviousBestStepIndex = iThisBestStepIndex;
+ }
+
+ delete[] pvTempDst;
+ delete[] pvDecoded;
+
+ // Report on the optimized difference
+ pvDecoded = new BYTE[cBlocks * dwSrcBlockSize];
+ pfnOppConvert( (LPBYTE)pvDst, pvDecoded, cBlocks, m_wfxEncode.wfx.nBlockAlign, XBOX_ADPCM_SAMPLES_PER_BLOCK, &m_nStepIndexL, &m_nStepIndexR );
+ printf( "Difference between original and decoded streams: 0x%I64x\n", CalcDifference( (LPBYTE)pvSrc, pvDecoded, cBlocks, cBlocks, dwSrcBlockSize ) );
+
+ delete[] pvDecoded;
+ }
+ break;
+ }
+
+ return TRUE;
+}
+
+
+/****************************************************************************
+ *
+ * Reset
+ *
+ * Description:
+ * Resets the conversion operation.
+ *
+ * Arguments:
+ * (void)
+ *
+ * Returns:
+ * (void)
+ *
+ ****************************************************************************/
+
+void
+CImaAdpcmCodec::Reset
+(
+ void
+)
+{
+ //
+ // Reset the stepping indices
+ //
+
+ m_nStepIndexL = m_nStepIndexR = 0;
+}
+
+
+/****************************************************************************
+ *
+ * GetEncodeAlignment
+ *
+ * Description:
+ * Gets the alignment of an encoded buffer.
+ *
+ * Arguments:
+ * (void)
+ *
+ * Returns:
+ * WORD: alignment, in bytes.
+ *
+ ****************************************************************************/
+
+WORD
+CImaAdpcmCodec::GetEncodeAlignment
+(
+ void
+)
+{
+ return m_wfxEncode.wfx.nBlockAlign;
+}
+
+
+/****************************************************************************
+ *
+ * GetDecodeAlignment
+ *
+ * Description:
+ * Gets the alignment of a decoded buffer.
+ *
+ * Arguments:
+ * (void)
+ *
+ * Returns:
+ * DWORD: alignment, in bytes.
+ *
+ ****************************************************************************/
+
+WORD
+CImaAdpcmCodec::GetDecodeAlignment
+(
+ void
+)
+{
+ return m_wfxEncode.wSamplesPerBlock * m_wfxEncode.wfx.nChannels * IMAADPCM_PCM_BITS_PER_SAMPLE / 8;
+}
+
+
+/****************************************************************************
+ *
+ * CalculateEncodeAlignment
+ *
+ * Description:
+ * Calculates an encoded data block alignment based on a PCM sample
+ * count and an alignment multiplier.
+ *
+ * Arguments:
+ * WORD [in]: channel count.
+ * WORD [in]: PCM samples per block.
+ *
+ * Returns:
+ * WORD: alignment, in bytes.
+ *
+ ****************************************************************************/
+
+WORD
+CImaAdpcmCodec::CalculateEncodeAlignment
+(
+ WORD nChannels,
+ WORD nSamplesPerBlock
+)
+{
+ const WORD nEncodedSampleBits = nChannels * IMAADPCM_BITS_PER_SAMPLE;
+ const WORD nHeaderBytes = nChannels * IMAADPCM_HEADER_LENGTH;
+ INT nBlockAlign;
+
+ //
+ // Calculate the raw block alignment that nSamplesPerBlock dictates. This
+ // value may include a partial encoded sample, so be sure to round up.
+ //
+ // Start with the samples-per-block, minus 1. The first sample is actually
+ // stored in the header.
+ //
+
+ nBlockAlign = nSamplesPerBlock - 1;
+
+ //
+ // Convert to encoded sample size
+ //
+
+ nBlockAlign *= nEncodedSampleBits;
+ nBlockAlign += 7;
+ nBlockAlign /= 8;
+
+ //
+ // The stereo encoder requires that there be at least two DWORDs to process
+ //
+
+ nBlockAlign += 7;
+ nBlockAlign /= 8;
+ nBlockAlign *= 8;
+
+ //
+ // Add the header
+ //
+
+ nBlockAlign += nHeaderBytes;
+
+ // We used an INT temporarily, but the final result should fit into a WORD
+ assert( nBlockAlign < 0xFFFF );
+ return (WORD)nBlockAlign;
+}
+
+
+/****************************************************************************
+ *
+ * CreatePcmFormat
+ *
+ * Description:
+ * Creates a PCM format descriptor.
+ *
+ * Arguments:
+ * WORD [in]: channel count.
+ * DWORD [in]: sampling rate.
+ * LPWAVEFORMATEX [out]: format descriptor.
+ *
+ * Returns:
+ * (void)
+ *
+ ****************************************************************************/
+
+void
+CImaAdpcmCodec::CreatePcmFormat
+(
+ WORD nChannels,
+ DWORD nSamplesPerSec,
+ LPWAVEFORMATEX pwfx
+)
+{
+ pwfx->wFormatTag = WAVE_FORMAT_PCM;
+ pwfx->nChannels = nChannels;
+ pwfx->nSamplesPerSec = nSamplesPerSec;
+ pwfx->nBlockAlign = nChannels * IMAADPCM_PCM_BITS_PER_SAMPLE / 8;
+ pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
+ pwfx->wBitsPerSample = IMAADPCM_PCM_BITS_PER_SAMPLE;
+}
+
+
+/****************************************************************************
+ *
+ * CreateImaAdpcmFormat
+ *
+ * Description:
+ * Creates an IMA ADPCM format descriptor.
+ *
+ * Arguments:
+ * WORD [in]: channel count.
+ * DWORD [in]: sampling rate.
+ * LPIMAADPCMWAVEFORMAT [out]: format descriptor.
+ *
+ * Returns:
+ * (void)
+ *
+ ****************************************************************************/
+
+void
+CImaAdpcmCodec::CreateImaAdpcmFormat
+(
+ WORD nChannels,
+ DWORD nSamplesPerSec,
+ WORD nSamplesPerBlock,
+ LPIMAADPCMWAVEFORMAT pwfx
+)
+{
+ pwfx->wfx.wFormatTag = WAVE_FORMAT_XBOX_ADPCM;
+ pwfx->wfx.nChannels = nChannels;
+ pwfx->wfx.nSamplesPerSec = nSamplesPerSec;
+ pwfx->wfx.nBlockAlign = CalculateEncodeAlignment(nChannels, nSamplesPerBlock);
+ pwfx->wfx.nAvgBytesPerSec = nSamplesPerSec * pwfx->wfx.nBlockAlign / nSamplesPerBlock;
+ pwfx->wfx.wBitsPerSample = IMAADPCM_BITS_PER_SAMPLE;
+ pwfx->wfx.cbSize = sizeof(*pwfx) - sizeof(pwfx->wfx);
+ pwfx->wSamplesPerBlock = nSamplesPerBlock;
+}
+
+
+/****************************************************************************
+ *
+ * IsValidPcmFormat
+ *
+ * Description:
+ * Validates a format structure.
+ *
+ * Arguments:
+ * LPCWAVEFORMATEX [in]: format.
+ *
+ * Returns:
+ * BOOL: TRUE on success.
+ *
+ ****************************************************************************/
+
+BOOL
+CImaAdpcmCodec::IsValidPcmFormat
+(
+ LPCWAVEFORMATEX pwfx
+)
+{
+ if(WAVE_FORMAT_PCM != pwfx->wFormatTag)
+ {
+ return FALSE;
+ }
+
+ if((pwfx->nChannels < 1) || (pwfx->nChannels > IMAADPCM_MAX_CHANNELS))
+ {
+ return FALSE;
+ }
+
+ if(IMAADPCM_PCM_BITS_PER_SAMPLE != pwfx->wBitsPerSample)
+ {
+ return FALSE;
+ }
+
+ if(pwfx->nChannels * pwfx->wBitsPerSample / 8 != pwfx->nBlockAlign)
+ {
+ return FALSE;
+ }
+
+ if(pwfx->nBlockAlign * pwfx->nSamplesPerSec != pwfx->nAvgBytesPerSec)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/****************************************************************************
+ *
+ * IsValidXboxAdpcmFormat
+ *
+ * Description:
+ * Validates a format structure.
+ *
+ * Arguments:
+ * LPCIMAADPCMWAVEFORMAT [in]: format.
+ *
+ * Returns:
+ * BOOL: TRUE on success.
+ *
+ ****************************************************************************/
+
+BOOL
+CImaAdpcmCodec::IsValidImaAdpcmFormat
+(
+ LPCIMAADPCMWAVEFORMAT pwfx
+)
+{
+ if(WAVE_FORMAT_XBOX_ADPCM != pwfx->wfx.wFormatTag)
+ {
+ return FALSE;
+ }
+
+ if(sizeof(*pwfx) - sizeof(pwfx->wfx) != pwfx->wfx.cbSize)
+ {
+ return FALSE;
+ }
+
+ if((pwfx->wfx.nChannels < 1) || (pwfx->wfx.nChannels > IMAADPCM_MAX_CHANNELS))
+ {
+ return FALSE;
+ }
+
+ if(IMAADPCM_BITS_PER_SAMPLE != pwfx->wfx.wBitsPerSample)
+ {
+ return FALSE;
+ }
+
+ if(CalculateEncodeAlignment(pwfx->wfx.nChannels, pwfx->wSamplesPerBlock) != pwfx->wfx.nBlockAlign)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/****************************************************************************
+ *
+ * CalcDifference
+ *
+ * Description:
+ * Calculates the error between two audio buffers. The error is clamped
+ * at (ULONGLONG)-1. Also, the error of a block acts as a percentage of
+ * the maximum possible contribution of a block.
+ *
+ * Arguments:
+ * LPBYTE [in]: First buffer
+ * LPBYTE [in]: Second buffer
+ * UINT [in]: Number of blocks-worth to compare
+ * UINT [in]: Total number of blocks being converted
+ * DWORD [in]: Size of a single block in bytes
+ *
+ * Returns:
+ * ULONGLONG: Difference of the two buffers
+ *
+ ****************************************************************************/
+ULONGLONG CImaAdpcmCodec::CalcDifference(LPBYTE pvBuffer1, LPBYTE pvBuffer2, UINT cBlocks, UINT cTotalBlocks, DWORD dwBlockSize)
+{
+ ULONGLONG ullDiff = 0;
+
+ // Each block worth of error can contribute a maximum of this value
+ const ULONGLONG ullMaxBlockContribution = ( (ULONGLONG)-1 / cTotalBlocks );
+
+ // The maximum error in a block is
+ // (2^16)^2 * m_wfxEncode.wSamplesPerBlock
+ // = ( 1 << 32 ) * m_wfxEncode.wSamplesPerBlock
+ const ULONGLONG ullMaxBlockDiff = ( (ULONGLONG)1 << 32 ) * m_wfxEncode.wSamplesPerBlock;
+
+ // Now we go through the buffers sample by sample and find the difference
+ // on a block-by-block basis. The factored difference of a block is
+ // ullBlockDiff / ullMaxBlockDiff * ullMaxBlockContribution
+ for( UINT i = 0; i < cBlocks; ++i )
+ {
+ PSHORT pSamples1 = (PSHORT)(pvBuffer1 + i * dwBlockSize);
+ PSHORT pSamples2 = (PSHORT)(pvBuffer2 + i * dwBlockSize);
+ ULONGLONG ullBlockDiff = 0;
+
+ // Find the block difference
+ for( UINT j = 0; j < m_wfxEncode.wSamplesPerBlock; ++j )
+ {
+ ULONGLONG ullSampleDiff = (ULONGLONG)(pSamples2[j]) - (ULONGLONG)(pSamples1[j]);
+ ullBlockDiff += ( ullSampleDiff * ullSampleDiff );
+ }
+
+ // Assert that we didn't go over the maximum possible
+ assert( ullBlockDiff <= ullMaxBlockDiff );
+
+ // Add the contribution of this block to the error
+ ullDiff += (ULONGLONG)( ( (DOUBLE)ullBlockDiff / (DOUBLE)ullMaxBlockDiff ) * ullMaxBlockContribution );
+ }
+
+ assert( ullDiff <= cBlocks * ullMaxBlockContribution );
+
+ return ullDiff;
+}
+
+
+/****************************************************************************
+ *
+ * EncodeSample
+ *
+ * Description:
+ * Encodes a sample.
+ *
+ * Arguments:
+ * int [in]: the sample to be encoded.
+ * LPINT [in/out]: the predicted value of the sample.
+ * int [in]: the quantization step size used to encode the sample.
+ *
+ * Returns:
+ * int: the encoded ADPCM sample.
+ *
+ ****************************************************************************/
+
+int
+CImaAdpcmCodec::EncodeSample
+(
+ int nInputSample,
+ LPINT pnPredictedSample,
+ int nStepSize
+)
+{
+ int nPredictedSample;
+ LONG lDifference;
+ int nEncodedSample;
+
+ nPredictedSample = *pnPredictedSample;
+
+ lDifference = nInputSample - nPredictedSample;
+ nEncodedSample = 0;
+
+ if(lDifference < 0)
+ {
+ nEncodedSample = 8;
+ lDifference = -lDifference;
+ }
+
+ if(lDifference >= nStepSize)
+ {
+ nEncodedSample |= 4;
+ lDifference -= nStepSize;
+ }
+
+ nStepSize >>= 1;
+
+ if(lDifference >= nStepSize)
+ {
+ nEncodedSample |= 2;
+ lDifference -= nStepSize;
+ }
+
+ nStepSize >>= 1;
+
+ if(lDifference >= nStepSize)
+ {
+ nEncodedSample |= 1;
+ lDifference -= nStepSize;
+ }
+
+ if(nEncodedSample & 8)
+ {
+ nPredictedSample = nInputSample + lDifference - (nStepSize >> 1);
+ }
+ else
+ {
+ nPredictedSample = nInputSample - lDifference + (nStepSize >> 1);
+ }
+
+ if(nPredictedSample > 32767)
+ {
+ nPredictedSample = 32767;
+ }
+ else if(nPredictedSample < -32768)
+ {
+ nPredictedSample = -32768;
+ }
+
+ *pnPredictedSample = nPredictedSample;
+
+ return nEncodedSample;
+}
+
+
+/****************************************************************************
+ *
+ * DecodeSample
+ *
+ * Description:
+ * Decodes an encoded sample.
+ *
+ * Arguments:
+ * int [in]: the sample to be decoded.
+ * int [in]: the predicted value of the sample.
+ * int [i]: the quantization step size used to encode the sample.
+ *
+ * Returns:
+ * int: the decoded PCM sample.
+ *
+ ****************************************************************************/
+
+int
+CImaAdpcmCodec::DecodeSample
+(
+ int nEncodedSample,
+ int nPredictedSample,
+ int nStepSize
+)
+{
+ LONG lDifference;
+ LONG lNewSample;
+
+ lDifference = nStepSize >> 3;
+
+ if(nEncodedSample & 4)
+ {
+ lDifference += nStepSize;
+ }
+
+ if(nEncodedSample & 2)
+ {
+ lDifference += nStepSize >> 1;
+ }
+
+ if(nEncodedSample & 1)
+ {
+ lDifference += nStepSize >> 2;
+ }
+
+ if(nEncodedSample & 8)
+ {
+ lDifference = -lDifference;
+ }
+
+ lNewSample = nPredictedSample + lDifference;
+
+ if((LONG)(short)lNewSample != lNewSample)
+ {
+ if(lNewSample < -32768)
+ {
+ lNewSample = -32768;
+ }
+ else
+ {
+ lNewSample = 32767;
+ }
+ }
+
+ return (int)lNewSample;
+}
+
+
+/****************************************************************************
+ *
+ * Conversion Routines
+ *
+ * Description:
+ * Converts a PCM buffer to ADPCM, or the reverse.
+ *
+ * Arguments:
+ * LPBYTE [in]: source buffer.
+ * LPBYTE [out]: destination buffer.
+ * UINT [in]: block count.
+ * UINT [in]: block alignment of the ADPCM data, in bytes.
+ * UINT [in]: the number of samples in each ADPCM block (not used in
+ * decoding).
+ * LPINT [in/out]: left-channel stepping index.
+ * LPINT [in/out]: right-channel stepping index.
+ *
+ * Returns:
+ * BOOL: TRUE on success.
+ *
+ ****************************************************************************/
+
+BOOL
+CImaAdpcmCodec::EncodeM16
+(
+ LPBYTE pbSrc,
+ LPBYTE pbDst,
+ UINT cBlocks,
+ UINT nBlockAlignment,
+ UINT cSamplesPerBlock,
+ LPINT pnStepIndexL,
+ LPINT
+)
+{
+ LPBYTE pbBlock;
+ UINT cSamples;
+ int nSample;
+ int nStepSize;
+ int nEncSample1;
+ int nEncSample2;
+ int nPredSample;
+ int nStepIndex;
+
+ //
+ // Save a local copy of the step index so we're not constantly
+ // dereferencing a pointer.
+ //
+
+ nStepIndex = *pnStepIndexL;
+
+ //
+ // Enter the main loop
+ //
+
+ while(cBlocks--)
+ {
+ pbBlock = pbDst;
+ cSamples = cSamplesPerBlock - 1;
+
+ //
+ // Block header
+ //
+
+ nPredSample = *(short *)pbSrc;
+ pbSrc += sizeof(short);
+
+ *(LONG *)pbBlock = MAKELONG(nPredSample, nStepIndex);
+ pbBlock += sizeof(LONG);
+
+ //
+ // We have written the header for this block--now write the data
+ // chunk (which consists of a bunch of encoded nibbles). Note
+ // that if we don't have enough data to fill a complete byte, then
+ // we add a 0 nibble on the end.
+ //
+
+ while(cSamples)
+ {
+ //
+ // Sample 1
+ //
+
+ nSample = *(short *)pbSrc;
+ pbSrc += sizeof(short);
+ cSamples--;
+
+ nStepSize = m_asStep[nStepIndex];
+ nEncSample1 = EncodeSample(nSample, &nPredSample, nStepSize);
+ nStepIndex = NextStepIndex(nEncSample1, nStepIndex);
+
+ //
+ // Sample 2
+ //
+
+ if(cSamples)
+ {
+ nSample = *(short *)pbSrc;
+ pbSrc += sizeof(short);
+ cSamples--;
+
+ nStepSize = m_asStep[nStepIndex];
+ nEncSample2 = EncodeSample(nSample, &nPredSample, nStepSize);
+ nStepIndex = NextStepIndex(nEncSample2, nStepIndex);
+ }
+ else
+ {
+ nEncSample2 = 0;
+ }
+
+ //
+ // Write out encoded byte.
+ //
+
+ *pbBlock++ = (BYTE)(nEncSample1 | (nEncSample2 << 4));
+ }
+
+ //
+ // Skip padding
+ //
+
+ pbDst += nBlockAlignment;
+ }
+
+ //
+ // Restore the value of the step index to be used on the next buffer.
+ //
+
+ *pnStepIndexL = nStepIndex;
+
+ return TRUE;
+}
+
+
+BOOL
+CImaAdpcmCodec::EncodeS16
+(
+ LPBYTE pbSrc,
+ LPBYTE pbDst,
+ UINT cBlocks,
+ UINT nBlockAlignment,
+ UINT cSamplesPerBlock,
+ LPINT pnStepIndexL,
+ LPINT pnStepIndexR
+)
+{
+ LPBYTE pbBlock;
+ UINT cSamples;
+ UINT cSubSamples;
+ int nSample;
+ int nStepSize;
+ DWORD dwLeft;
+ DWORD dwRight;
+ int nEncSampleL;
+ int nPredSampleL;
+ int nStepIndexL;
+ int nEncSampleR;
+ int nPredSampleR;
+ int nStepIndexR;
+ UINT i;
+
+ //
+ // Save a local copy of the step indices so we're not constantly
+ // dereferencing a pointer.
+ //
+
+ nStepIndexL = *pnStepIndexL;
+ nStepIndexR = *pnStepIndexR;
+
+ //
+ // Enter the main loop
+ //
+
+ while(cBlocks--)
+ {
+ pbBlock = pbDst;
+ cSamples = cSamplesPerBlock - 1;
+
+ //
+ // LEFT channel block header
+ //
+
+ nPredSampleL = *(short *)pbSrc;
+ pbSrc += sizeof(short);
+
+ *(LONG *)pbBlock = MAKELONG(nPredSampleL, nStepIndexL);
+ pbBlock += sizeof(LONG);
+
+ //
+ // RIGHT channel block header
+ //
+
+ nPredSampleR = *(short *)pbSrc;
+ pbSrc += sizeof(short);
+
+ *(LONG *)pbBlock = MAKELONG(nPredSampleR, nStepIndexR);
+ pbBlock += sizeof(LONG);
+
+ //
+ // We have written the header for this block--now write the data
+ // chunk. This consists of 8 left samples (one DWORD of output)
+ // followed by 8 right samples (also one DWORD). Since the input
+ // samples are interleaved, we create the left and right DWORDs
+ // sample by sample, and then write them both out.
+ //
+
+ while(cSamples)
+ {
+ dwLeft = 0;
+ dwRight = 0;
+
+ cSubSamples = min(cSamples, 8);
+
+ for(i = 0; i < cSubSamples; i++)
+ {
+ //
+ // LEFT channel
+ //
+
+ nSample = *(short *)pbSrc;
+ pbSrc += sizeof(short);
+
+ nStepSize = m_asStep[nStepIndexL];
+
+ nEncSampleL = EncodeSample(nSample, &nPredSampleL, nStepSize);
+
+ nStepIndexL = NextStepIndex(nEncSampleL, nStepIndexL);
+ dwLeft |= (DWORD)nEncSampleL << (4 * i);
+
+ //
+ // RIGHT channel
+ //
+
+ nSample = *(short *)pbSrc;
+ pbSrc += sizeof(short);
+
+ nStepSize = m_asStep[nStepIndexR];
+
+ nEncSampleR = EncodeSample(nSample, &nPredSampleR, nStepSize);
+
+ nStepIndexR = NextStepIndex(nEncSampleR, nStepIndexR);
+ dwRight |= (DWORD)nEncSampleR << (4 * i);
+ }
+
+ //
+ // Write out encoded DWORDs.
+ //
+
+ *(LPDWORD)pbBlock = dwLeft;
+ pbBlock += sizeof(DWORD);
+
+ *(LPDWORD)pbBlock = dwRight;
+ pbBlock += sizeof(DWORD);
+
+ cSamples -= cSubSamples;
+ }
+
+ //
+ // Skip padding
+ //
+
+ pbDst += nBlockAlignment;
+ }
+
+ //
+ // Restore the value of the step index to be used on the next buffer.
+ //
+
+ *pnStepIndexL = nStepIndexL;
+ *pnStepIndexR = nStepIndexR;
+
+ return TRUE;
+
+}
+
+
+BOOL
+CImaAdpcmCodec::DecodeM16
+(
+ LPBYTE pbSrc,
+ LPBYTE pbDst,
+ UINT cBlocks,
+ UINT nBlockAlignment,
+ UINT cSamplesPerBlock,
+ LPINT,
+ LPINT
+)
+{
+ BOOL fSuccess = TRUE;
+ LPBYTE pbBlock;
+ UINT cSamples;
+ BYTE bSample;
+ int nStepSize;
+ int nEncSample;
+ int nPredSample;
+ int nStepIndex;
+ DWORD dwHeader;
+
+ //
+ // Enter the main loop
+ //
+
+ while(cBlocks--)
+ {
+ pbBlock = pbSrc;
+ cSamples = cSamplesPerBlock - 1;
+
+ //
+ // Block header
+ //
+
+ dwHeader = *(LPDWORD)pbBlock;
+ pbBlock += sizeof(DWORD);
+
+ nPredSample = (int)(short)LOWORD(dwHeader);
+ nStepIndex = (int)(BYTE)HIWORD(dwHeader);
+
+ if(!ValidStepIndex(nStepIndex))
+ {
+ //
+ // The step index is out of range - this is considered a fatal
+ // error as the input stream is corrupted. We fail by returning
+ // zero bytes converted.
+ //
+
+ fSuccess = FALSE;
+ break;
+ }
+
+ //
+ // Write out first sample
+ //
+
+ *(short *)pbDst = (short)nPredSample;
+ pbDst += sizeof(short);
+
+ //
+ // Enter the block loop
+ //
+
+ while(cSamples)
+ {
+ bSample = *pbBlock++;
+
+ //
+ // Sample 1
+ //
+
+ nEncSample = (bSample & (BYTE)0x0F);
+ nStepSize = m_asStep[nStepIndex];
+ nPredSample = DecodeSample(nEncSample, nPredSample, nStepSize);
+ nStepIndex = NextStepIndex(nEncSample, nStepIndex);
+
+ *(short *)pbDst = (short)nPredSample;
+ pbDst += sizeof(short);
+
+ cSamples--;
+
+ //
+ // Sample 2
+ //
+
+ if(cSamples)
+ {
+ nEncSample = (bSample >> 4);
+ nStepSize = m_asStep[nStepIndex];
+ nPredSample = DecodeSample(nEncSample, nPredSample, nStepSize);
+ nStepIndex = NextStepIndex(nEncSample, nStepIndex);
+
+ *(short *)pbDst = (short)nPredSample;
+ pbDst += sizeof(short);
+
+ cSamples--;
+ }
+ }
+
+ //
+ // Skip padding
+ //
+
+ pbSrc += nBlockAlignment;
+ }
+
+ return fSuccess;
+}
+
+
+BOOL
+CImaAdpcmCodec::DecodeS16
+(
+ LPBYTE pbSrc,
+ LPBYTE pbDst,
+ UINT cBlocks,
+ UINT nBlockAlignment,
+ UINT cSamplesPerBlock,
+ LPINT,
+ LPINT
+)
+{
+ BOOL fSuccess = TRUE;
+ LPBYTE pbBlock;
+ UINT cSamples;
+ UINT cSubSamples;
+ int nStepSize;
+ DWORD dwHeader;
+ DWORD dwLeft;
+ DWORD dwRight;
+ int nEncSampleL;
+ int nPredSampleL;
+ int nStepIndexL;
+ int nEncSampleR;
+ int nPredSampleR;
+ int nStepIndexR;
+ UINT i;
+
+ //
+ // Enter the main loop
+ //
+
+ while(cBlocks--)
+ {
+ pbBlock = pbSrc;
+ cSamples = cSamplesPerBlock - 1;
+
+ //
+ // LEFT channel header
+ //
+
+ dwHeader = *(LPDWORD)pbBlock;
+ pbBlock += sizeof(DWORD);
+
+ nPredSampleL = (int)(short)LOWORD(dwHeader);
+ nStepIndexL = (int)(BYTE)HIWORD(dwHeader);
+
+ if(!ValidStepIndex(nStepIndexL))
+ {
+ //
+ // The step index is out of range - this is considered a fatal
+ // error as the input stream is corrupted. We fail by returning
+ // zero bytes converted.
+ //
+
+ fSuccess = FALSE;
+ break;
+ }
+
+ //
+ // RIGHT channel header
+ //
+
+ dwHeader = *(LPDWORD)pbBlock;
+ pbBlock += sizeof(DWORD);
+
+ nPredSampleR = (int)(short)LOWORD(dwHeader);
+ nStepIndexR = (int)(BYTE)HIWORD(dwHeader);
+
+ if(!ValidStepIndex(nStepIndexR))
+ {
+ //
+ // The step index is out of range - this is considered a fatal
+ // error as the input stream is corrupted. We fail by returning
+ // zero bytes converted.
+ //
+
+ fSuccess = FALSE;
+ break;
+ }
+
+ //
+ // Write out first sample
+ //
+
+ *(LPDWORD)pbDst = MAKELONG(nPredSampleL, nPredSampleR);
+ pbDst += sizeof(DWORD);
+
+ //
+ // The first DWORD contains 4 left samples, the second DWORD
+ // contains 4 right samples. We process the source in 8-byte
+ // chunks to make it easy to interleave the output correctly.
+ //
+
+ while(cSamples)
+ {
+ dwLeft = *(LPDWORD)pbBlock;
+ pbBlock += sizeof(DWORD);
+ dwRight = *(LPDWORD)pbBlock;
+ pbBlock += sizeof(DWORD);
+
+ cSubSamples = min(cSamples, 8);
+
+ for(i = 0; i < cSubSamples; i++)
+ {
+ //
+ // LEFT channel
+ //
+
+ nEncSampleL = (dwLeft & 0x0F);
+ nStepSize = m_asStep[nStepIndexL];
+ nPredSampleL = DecodeSample(nEncSampleL, nPredSampleL, nStepSize);
+ nStepIndexL = NextStepIndex(nEncSampleL, nStepIndexL);
+
+ //
+ // RIGHT channel
+ //
+
+ nEncSampleR = (dwRight & 0x0F);
+ nStepSize = m_asStep[nStepIndexR];
+ nPredSampleR = DecodeSample(nEncSampleR, nPredSampleR, nStepSize);
+ nStepIndexR = NextStepIndex(nEncSampleR, nStepIndexR);
+
+ //
+ // Write out sample
+ //
+
+ *(LPDWORD)pbDst = MAKELONG(nPredSampleL, nPredSampleR);
+ pbDst += sizeof(DWORD);
+
+ //
+ // Shift the next input sample into the low-order 4 bits.
+ //
+
+ dwLeft >>= 4;
+ dwRight >>= 4;
+ }
+
+ cSamples -= cSubSamples;
+ }
+
+ //
+ // Skip padding
+ //
+
+ pbSrc += nBlockAlignment;
+ }
+
+ return fSuccess;
+}
+
+
+int XboxADPCMSize( int sampleCount, int channelCount, int sampleRate )
+{
+ CImaAdpcmCodec codec;
+ IMAADPCMWAVEFORMAT wfxEncode;
+
+ // Create an APDCM format structure based off the source format
+ codec.CreateImaAdpcmFormat( (WORD)channelCount, sampleRate, XBOX_ADPCM_SAMPLES_PER_BLOCK, &wfxEncode );
+
+ // Calculate number of ADPCM blocks and length of ADPCM data
+ DWORD dwDestBlocks = sampleCount / XBOX_ADPCM_SAMPLES_PER_BLOCK;
+ DWORD dwDestLength = dwDestBlocks * wfxEncode.wfx.nBlockAlign;
+
+ return dwDestLength;
+}
+
+void Convert16ToXboxADPCM( const short *pInputBuffer, byte *pOutputBuffer, byte *pOutFormat, int sampleCount, int channelCount, int sampleRate )
+{
+ CImaAdpcmCodec codec;
+ IMAADPCMWAVEFORMAT wfxEncode;
+
+ // Create an APDCM format structure based off the source format
+ codec.CreateImaAdpcmFormat( (WORD)channelCount, sampleRate, XBOX_ADPCM_SAMPLES_PER_BLOCK, &wfxEncode );
+ if ( pOutFormat )
+ {
+ memcpy( pOutFormat, &wfxEncode, sizeof(wfxEncode) );
+ }
+
+ // Initialize the codec
+ if ( FALSE == codec.Initialize( &wfxEncode, CODEC_MODE_ENCODE_OPTIMIZE_EACH_BLOCK ) )
+ {
+ printf( "Couldn't initialize codec.\n" );
+ return;
+ }
+
+ // Convert the data
+ DWORD dwDestBlocks = sampleCount / XBOX_ADPCM_SAMPLES_PER_BLOCK;
+ if ( FALSE == codec.Convert( (const byte *)pInputBuffer, pOutputBuffer, dwDestBlocks ) )
+ return;
+}
diff --git a/utils/xbox/MakeGameData/imaadpcm.h b/utils/xbox/MakeGameData/imaadpcm.h
new file mode 100644
index 0000000..a9fdd08
--- /dev/null
+++ b/utils/xbox/MakeGameData/imaadpcm.h
@@ -0,0 +1,153 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+/***************************************************************************
+ *
+ * Copyright (C) 2001 Microsoft Corporation. All Rights Reserved.
+ *
+ * File: imaadpcm.h
+ * Content: IMA ADPCM CODEC.
+ * History:
+ * Date By Reason
+ * ==== == ======
+ * 04/29/01 dereks Created.
+ *
+ ****************************************************************************/
+
+#ifndef __IMAADPCM_H__
+#define __IMAADPCM_H__
+
+#include <windows.h>
+#include <windowsx.h>
+#include <mmsystem.h>
+#include <ctype.h>
+#include <mmreg.h>
+#include <msacm.h>
+
+#define XBOX_ADPCM_SAMPLES_PER_BLOCK 64
+
+#define WAVE_FORMAT_XBOX_ADPCM 0x0069
+
+#define IMAADPCM_BITS_PER_SAMPLE 4
+#define IMAADPCM_HEADER_LENGTH 4
+
+#define IMAADPCM_MAX_CHANNELS 2
+#define IMAADPCM_PCM_BITS_PER_SAMPLE 16
+
+#define NUMELMS(a) (sizeof(a) / sizeof(a[0]))
+
+typedef const WAVEFORMATEX *LPCWAVEFORMATEX;
+typedef const IMAADPCMWAVEFORMAT *LPCIMAADPCMWAVEFORMAT;
+
+#ifdef __cplusplus
+
+//
+// IMA ADPCM encoder function prototype
+//
+
+typedef BOOL (*LPFNIMAADPCMCONVERT)(LPBYTE pbSrc, LPBYTE pbDst, UINT cBlocks, UINT nBlockAlignment, UINT cSamplesPerBlock, LPINT pnStepIndexL, LPINT pnStepIndexR);
+
+//
+// Codec mode
+//
+
+enum CODEC_MODE
+{
+ CODEC_MODE_DECODE,
+ CODEC_MODE_ENCODE_NORMAL,
+ CODEC_MODE_ENCODE_OPTIMIZE_WHOLE_FILE,
+ CODEC_MODE_ENCODE_OPTIMIZE_EACH_BLOCK,
+};
+
+//
+// IMA ADPCM CODEC
+//
+
+class CImaAdpcmCodec
+{
+private:
+ static const short m_asNextStep[16]; // Step increment array
+ static const short m_asStep[89]; // Step value array
+ IMAADPCMWAVEFORMAT m_wfxEncode; // Encoded format description
+ CODEC_MODE m_cmCodecMode; // Codec mode
+ int m_nStepIndexL; // Left-channel stepping index
+ int m_nStepIndexR; // Right-channel stepping index
+ LPFNIMAADPCMCONVERT m_pfnConvert; // Conversion function
+
+public:
+ CImaAdpcmCodec(void);
+ ~CImaAdpcmCodec(void);
+
+public:
+ // Initialization
+ BOOL Initialize(LPCIMAADPCMWAVEFORMAT pwfxEncode, CODEC_MODE cmCodecMode);
+
+ // Size conversions
+ WORD GetEncodeAlignment(void);
+ WORD GetDecodeAlignment(void);
+ WORD GetSourceAlignment(void);
+ WORD GetDestinationAlignment(void);
+
+ // Data conversions
+ BOOL Convert(LPCVOID pvSrc, LPVOID pvDst, UINT cBlocks);
+ void Reset(void);
+
+ // Format descriptions
+ static void CreatePcmFormat(WORD nChannels, DWORD nSamplesPerSec, LPWAVEFORMATEX pwfxFormat);
+ static void CreateImaAdpcmFormat(WORD nChannels, DWORD nSamplesPerSec, WORD nSamplesPerBlock, LPIMAADPCMWAVEFORMAT pwfxFormat);
+
+ static BOOL IsValidPcmFormat(LPCWAVEFORMATEX pwfxFormat);
+ static BOOL IsValidImaAdpcmFormat(LPCIMAADPCMWAVEFORMAT pwfxFormat);
+
+private:
+ // En/decoded data alignment
+ static WORD CalculateEncodeAlignment(WORD nSamplesPerBlock, WORD nChannels);
+
+ // Data conversion functions
+ static BOOL EncodeM16(LPBYTE pbSrc, LPBYTE pbDst, UINT cBlocks, UINT nBlockAlignment, UINT cSamplesPerBlock, LPINT pnStepIndexL, LPINT pnStepIndexR);
+ static BOOL EncodeS16(LPBYTE pbSrc, LPBYTE pbDst, UINT cBlocks, UINT nBlockAlignment, UINT cSamplesPerBlock, LPINT pnStepIndexL, LPINT pnStepIndexR);
+ static BOOL DecodeM16(LPBYTE pbSrc, LPBYTE pbDst, UINT cBlocks, UINT nBlockAlignment, UINT cSamplesPerBlock, LPINT pnStepIndexL, LPINT pnStepIndexR);
+ static BOOL DecodeS16(LPBYTE pbSrc, LPBYTE pbDst, UINT cBlocks, UINT nBlockAlignment, UINT cSamplesPerBlock, LPINT pnStepIndexL, LPINT pnStepIndexR);
+
+ static int EncodeSample(int nInputSample, int *nPredictedSample, int nStepSize);
+ static int DecodeSample(int nInputSample, int nPredictedSample, int nStepSize);
+
+ static int NextStepIndex(int nEncodedSample, int nStepIndex);
+ static BOOL ValidStepIndex(int nStepIndex);
+
+ /*static ULONGLONG CalcDifference(LPDWORD pvBuffer1, LPDWORD pvBuffer2, DWORD dwLength);*/
+ ULONGLONG CalcDifference(LPBYTE pvBuffer1, LPBYTE pvBuffer2, UINT cBlocks, UINT cTotalBlocks, DWORD dwBlockSize);
+};
+
+__inline WORD CImaAdpcmCodec::GetSourceAlignment(void)
+{
+ return ( m_cmCodecMode == CODEC_MODE_DECODE ) ? GetEncodeAlignment() : GetDecodeAlignment();
+}
+
+__inline WORD CImaAdpcmCodec::GetDestinationAlignment(void)
+{
+ return ( m_cmCodecMode == CODEC_MODE_DECODE ) ? GetDecodeAlignment() : GetEncodeAlignment();
+}
+
+__inline int CImaAdpcmCodec::NextStepIndex(int nEncodedSample, int nStepIndex)
+{
+ nStepIndex += m_asNextStep[nEncodedSample];
+
+ if(nStepIndex < 0)
+ {
+ nStepIndex = 0;
+ }
+ else if(nStepIndex >= NUMELMS(m_asStep))
+ {
+ nStepIndex = NUMELMS(m_asStep) - 1;
+ }
+
+ return nStepIndex;
+}
+
+__inline BOOL CImaAdpcmCodec::ValidStepIndex(int nStepIndex)
+{
+ return (nStepIndex >= 0) && (nStepIndex < NUMELMS(m_asStep));
+}
+
+#endif // __cplusplus
+
+#endif // __IMAADPCM_H__
diff --git a/utils/xbox/MakeGameData/resample.cpp b/utils/xbox/MakeGameData/resample.cpp
new file mode 100644
index 0000000..a0ce0a7
--- /dev/null
+++ b/utils/xbox/MakeGameData/resample.cpp
@@ -0,0 +1,381 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+
+#include <windows.h>
+#include <mmreg.h>
+#include "../toollib/toollib.h"
+#include "tier1/strtools.h"
+#include "resample.h"
+
+#define clamp(a,b,c) ( (a) > (c) ? (c) : ( (a) < (b) ? (b) : (a) ) )
+
+const int NUM_COEFFS = 7;
+static const float g_ResampleCoefficients[NUM_COEFFS] =
+{
+ 0.0457281f, 0.168088f, 0.332501f, 0.504486f, 0.663202f, 0.803781f, 0.933856f
+};
+
+
+// generates 1 output sample for 2 input samples
+inline float DecimateSamplePair(float input0, float input1, const float pCoefficients[7], float xState[2], float yState[7] )
+{
+ float tmp_0 = xState[0];
+ float tmp_1 = xState[1];
+
+ xState[0] = input0;
+ xState[1] = input1;
+
+ input0 = (input0 - yState[0]) * pCoefficients[0] + tmp_0;
+ input1 = (input1 - yState[1]) * pCoefficients[1] + tmp_1;
+ tmp_0 = yState[0];
+ tmp_1 = yState[1];
+ yState[0] = input0;
+ yState[1] = input1;
+
+ input0 = (input0 - yState[2]) * pCoefficients[2] + tmp_0;
+ input1 = (input1 - yState[3]) * pCoefficients[3] + tmp_1;
+ tmp_0 = yState[2];
+ tmp_1 = yState[3];
+ yState[2] = input0;
+ yState[3] = input1;
+
+ input0 = (input0 - yState[4]) * pCoefficients[4] + tmp_0;
+ input1 = (input1 - yState[5]) * pCoefficients[5] + tmp_1;
+ tmp_0 = yState[4];
+ yState[4] = input0;
+ yState[5] = input1;
+
+ input0 = (input0 - yState[6]) * pCoefficients[6] + tmp_0;
+ yState[6] = input0;
+
+ return (input0 + input1);
+}
+
+static void ExtractFloatSamples( float *pOut, const short *pInputBuffer, int sampleCount, int stride )
+{
+ for ( int i = 0; i < sampleCount; i++ )
+ {
+ pOut[i] = pInputBuffer[0] * 1.0f / 32768.0f;
+ pInputBuffer += stride;
+ }
+}
+
+static void ExtractShortSamples( short *pOut, const float *pInputBuffer, float scale, int sampleCount, int stride )
+{
+ for ( int i = 0; i < sampleCount; i++ )
+ {
+ int sampleOut = (int)(pInputBuffer[i] * scale);
+ sampleOut = clamp( sampleOut, -32768, 32767 );
+
+ pOut[0] = (short)(sampleOut);
+ pOut += stride;
+ }
+}
+
+struct decimatestate_t
+{
+ float xState[2];
+ float yState[7];
+};
+void DecimateSampleBlock( float *pInOut, int sampleCount )
+{
+ decimatestate_t block;
+ int outCount = sampleCount >> 1;
+ int pos = 0;
+ memset( &block, 0, sizeof(block) );
+ do
+ {
+ float input0 = pInOut[pos*2+0];
+ float input1 = pInOut[pos*2+1];
+ pInOut[pos] = DecimateSamplePair( input0, input1, g_ResampleCoefficients, block.xState, block.yState );
+ pos++;
+ } while( pos < outCount );
+}
+
+void DecimateSampleRateBy2_16( const short *pInputBuffer, short *pOutputBuffer, int sampleCount, int channelCount )
+{
+ float *pTmpBuf = new float[sampleCount];
+ for ( int i = 0; i < channelCount; i++ )
+ {
+ ExtractFloatSamples( pTmpBuf, pInputBuffer+i, sampleCount, channelCount );
+ DecimateSampleBlock( pTmpBuf, sampleCount );
+ ExtractShortSamples( pOutputBuffer+i, pTmpBuf, 0.5f * 32768.0f, sampleCount>>1, channelCount );
+ }
+ delete [] pTmpBuf;
+}
+
+
+struct adpcmstate_t
+{
+ const ADPCMWAVEFORMAT *pFormat;
+ const ADPCMCOEFSET *pCoefficients;
+ int blockSize;
+};
+
+static int error_sign_lut[] = { 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1 };
+static int error_coefficients_lut[] = { 230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307, 230, 230, 230 };
+
+void ParseADPCM( adpcmstate_t &out, const byte *pFormatChunk )
+{
+ out.pFormat = (const ADPCMWAVEFORMAT *)pFormatChunk;
+ if ( out.pFormat )
+ {
+ out.pCoefficients = out.pFormat->aCoef;
+
+ // number of bytes for samples
+ out.blockSize = ((out.pFormat->wSamplesPerBlock - 2) * out.pFormat->wfx.nChannels ) / 2;
+ // size of channel header
+ out.blockSize += 7 * out.pFormat->wfx.nChannels;
+ }
+}
+
+void DecompressBlockMono( const adpcmstate_t &state, short *pOut, const char *pIn, int count )
+{
+ int pred = *pIn++;
+ int co1 = state.pCoefficients[pred].iCoef1;
+ int co2 = state.pCoefficients[pred].iCoef2;
+
+ // read initial delta
+ int delta = *((short *)pIn);
+ pIn += 2;
+
+ // read initial samples for prediction
+ int samp1 = *((short *)pIn);
+ pIn += 2;
+
+ int samp2 = *((short *)pIn);
+ pIn += 2;
+
+ // write out the initial samples (stored in reverse order)
+ *pOut++ = (short)samp2;
+ *pOut++ = (short)samp1;
+
+ // subtract the 2 samples in the header
+ count -= 2;
+
+ // this is a toggle to read nibbles, first nibble is high
+ int high = 1;
+
+ int error, sample=0;
+
+ // now process the block
+ while ( count )
+ {
+ // read the error nibble from the input stream
+ if ( high )
+ {
+ sample = (unsigned char) (*pIn++);
+ // high nibble
+ error = sample >> 4;
+ // cache low nibble for next read
+ sample = sample & 0xf;
+ // Next read is from cache, not stream
+ high = 0;
+ }
+ else
+ {
+ // stored in previous read (low nibble)
+ error = sample;
+ // next read is from stream
+ high = 1;
+ }
+ // convert to signed with LUT
+ int errorSign = error_sign_lut[error];
+
+ // interpolate the new sample
+ int predSample = (samp1 * co1) + (samp2 * co2);
+ // coefficients are fixed point 8-bit, so shift back to 16-bit integer
+ predSample >>= 8;
+
+ // Add in current error estimate
+ predSample += (errorSign * delta);
+
+ // Correct error estimate
+ delta = (delta * error_coefficients_lut[error]) >> 8;
+ // Clamp error estimate
+ if ( delta < 16 )
+ delta = 16;
+
+ // clamp
+ if ( predSample > 32767L )
+ predSample = 32767L;
+ else if ( predSample < -32768L )
+ predSample = -32768L;
+
+ // output
+ *pOut++ = (short)predSample;
+ // move samples over
+ samp2 = samp1;
+ samp1 = predSample;
+
+ count--;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Decode a single block of stereo ADPCM audio
+// Input : *pOut - 16-bit output buffer
+// *pIn - ADPCM encoded block data
+// count - number of sample pairs to decode
+//-----------------------------------------------------------------------------
+void DecompressBlockStereo( const adpcmstate_t &state, short *pOut, const char *pIn, int count )
+{
+ int pred[2], co1[2], co2[2];
+ int i;
+
+ for ( i = 0; i < 2; i++ )
+ {
+ pred[i] = *pIn++;
+ co1[i] = state.pCoefficients[pred[i]].iCoef1;
+ co2[i] = state.pCoefficients[pred[i]].iCoef2;
+ }
+
+ int delta[2], samp1[2], samp2[2];
+
+ for ( i = 0; i < 2; i++, pIn += 2 )
+ {
+ // read initial delta
+ delta[i] = *((short *)pIn);
+ }
+
+ // read initial samples for prediction
+ for ( i = 0; i < 2; i++, pIn += 2 )
+ {
+ samp1[i] = *((short *)pIn);
+ }
+ for ( i = 0; i < 2; i++, pIn += 2 )
+ {
+ samp2[i] = *((short *)pIn);
+ }
+
+ // write out the initial samples (stored in reverse order)
+ *pOut++ = (short)samp2[0]; // left
+ *pOut++ = (short)samp2[1]; // right
+ *pOut++ = (short)samp1[0]; // left
+ *pOut++ = (short)samp1[1]; // right
+
+ // subtract the 2 samples in the header
+ count -= 2;
+
+ // this is a toggle to read nibbles, first nibble is high
+ int high = 1;
+
+ int error, sample=0;
+
+ // now process the block
+ while ( count )
+ {
+ for ( i = 0; i < 2; i++ )
+ {
+ // read the error nibble from the input stream
+ if ( high )
+ {
+ sample = (unsigned char) (*pIn++);
+ // high nibble
+ error = sample >> 4;
+ // cache low nibble for next read
+ sample = sample & 0xf;
+ // Next read is from cache, not stream
+ high = 0;
+ }
+ else
+ {
+ // stored in previous read (low nibble)
+ error = sample;
+ // next read is from stream
+ high = 1;
+ }
+ // convert to signed with LUT
+ int errorSign = error_sign_lut[error];
+
+ // interpolate the new sample
+ int predSample = (samp1[i] * co1[i]) + (samp2[i] * co2[i]);
+ // coefficients are fixed point 8-bit, so shift back to 16-bit integer
+ predSample >>= 8;
+
+ // Add in current error estimate
+ predSample += (errorSign * delta[i]);
+
+ // Correct error estimate
+ delta[i] = (delta[i] * error_coefficients_lut[error]) >> 8;
+ // Clamp error estimate
+ if ( delta[i] < 16 )
+ delta[i] = 16;
+
+ // clamp
+ if ( predSample > 32767L )
+ predSample = 32767L;
+ else if ( predSample < -32768L )
+ predSample = -32768L;
+
+ // output
+ *pOut++ = (short)predSample;
+ // move samples over
+ samp2[i] = samp1[i];
+ samp1[i] = predSample;
+ }
+ count--;
+ }
+}
+
+int ADPCMSampleCountShortBlock( const adpcmstate_t &state, int shortBlockSize )
+{
+ if ( shortBlockSize < 8 )
+ return 0;
+
+ int sampleCount = state.pFormat->wSamplesPerBlock;
+
+ // short block?, fixup sample count (2 samples per byte, divided by number of channels per sample set)
+ sampleCount -= ((state.blockSize - shortBlockSize) * 2) / state.pFormat->wfx.nChannels;
+ return sampleCount;
+}
+
+int ADPCMSampleCount( const byte *pFormatChunk, const byte *pDataChunk, int dataSize )
+{
+ adpcmstate_t state;
+ ParseADPCM( state, pFormatChunk );
+ int numBlocks = dataSize / state.blockSize;
+ int mod = dataSize % state.blockSize;
+ return numBlocks * state.pFormat->wSamplesPerBlock + ADPCMSampleCountShortBlock(state, mod);
+}
+
+void DecompressADPCMSamples( const byte *pFormatChunk, const byte *pDataChunk, int dataSize, short *pOutputBuffer )
+{
+ adpcmstate_t state;
+ ParseADPCM( state, pFormatChunk );
+
+ while ( dataSize > 0 )
+ {
+ int block = dataSize;
+ int sampleCount = state.pFormat->wSamplesPerBlock;
+ if ( block > state.blockSize )
+ {
+ block = state.blockSize;
+ }
+ else
+ {
+ sampleCount = ADPCMSampleCountShortBlock( state, block );
+ }
+ if ( state.pFormat->wfx.nChannels == 1 )
+ {
+ DecompressBlockMono( state, pOutputBuffer, (const char *)pDataChunk, sampleCount );
+ }
+ else
+ {
+ DecompressBlockStereo( state, pOutputBuffer, (const char *)pDataChunk, sampleCount );
+ }
+ pOutputBuffer += sampleCount * state.pFormat->wfx.nChannels;
+ dataSize -= block;
+ pDataChunk += block;
+ }
+}
+
+void Convert8To16( const byte *pInputBuffer, short *pOutputBuffer, int sampleCount, int channelCount )
+{
+ for ( int i = 0; i < sampleCount*channelCount; i++ )
+ {
+ unsigned short signedSample = (byte)((int)((unsigned)pInputBuffer[i]) - 128);
+ pOutputBuffer[i] = (short) (signedSample | (signedSample<<8));
+ }
+}
+
diff --git a/utils/xbox/MakeGameData/resample.h b/utils/xbox/MakeGameData/resample.h
new file mode 100644
index 0000000..8382d7d
--- /dev/null
+++ b/utils/xbox/MakeGameData/resample.h
@@ -0,0 +1,18 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef RESAMPLE_H
+#define RESAMPLE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+void DecimateSampleRateBy2_16( const short *pInputBuffer, short *pOutputBuffer, int sampleCount, int channelCount );
+void DecompressADPCMSamples( const byte *pFormatChunk, const byte *pDataChunk, int dataSize, short *pOutputBuffer );
+int ADPCMSampleCount( const byte *pFormatChunk, const byte *pDataChunk, int dataSize );
+void Convert8To16( const byte *pInputBuffer, short *pOutputBuffer, int sampleCount, int channelCount );
+
+#endif // RESAMPLE_H
diff --git a/utils/xbox/MakeGameData/sound_io.cpp b/utils/xbox/MakeGameData/sound_io.cpp
new file mode 100644
index 0000000..44e0323
--- /dev/null
+++ b/utils/xbox/MakeGameData/sound_io.cpp
@@ -0,0 +1,68 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+/*****************************************************************************
+ SOUND_IO.CPP
+
+ IO class for RIFF
+*****************************************************************************/
+#include "../toollib/toollib.h"
+#include "tier2/riff.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Implements Audio IO on the engine's COMMON filesystem
+//-----------------------------------------------------------------------------
+class COM_IOReadBinary : public IFileReadBinary
+{
+public:
+ int open( const char *pFileName );
+ int read( void *pOutput, int size, int file );
+ void seek( int file, int pos );
+ unsigned int tell( int file );
+ unsigned int size( int file );
+ void close( int file );
+};
+
+
+int COM_IOReadBinary::open( const char *pFileName )
+{
+ int hFile = -1;
+
+ _sopen_s( &hFile, pFileName, _O_RDONLY|_O_BINARY, _SH_DENYWR, _S_IREAD );
+
+ return hFile;
+}
+
+int COM_IOReadBinary::read( void *pOutput, int size, int file )
+{
+ return _read( file, pOutput, size );
+}
+
+void COM_IOReadBinary::seek( int file, int pos )
+{
+ _lseek( file, pos, SEEK_SET );
+}
+
+unsigned int COM_IOReadBinary::tell( int file )
+{
+ return _lseek( file, 0, SEEK_CUR );
+}
+
+unsigned int COM_IOReadBinary::size( int file )
+{
+ long pos;
+ long length;
+
+ pos = _lseek( file, 0, SEEK_CUR );
+ length = _lseek( file, 0, SEEK_END );
+ _lseek( file, pos, SEEK_SET );
+
+ return length;
+}
+
+void COM_IOReadBinary::close( int file )
+{
+ _close( file );
+}
+
+static COM_IOReadBinary io;
+IFileReadBinary *g_pSndIO = &io;
+