You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1174 lines
31 KiB
1174 lines
31 KiB
//========= 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;
|
|
}
|