mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-08 01:39:36 +00:00
1
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -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 );
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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__
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user