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; |
|
}
|
|
|