diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /public/xzp.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'public/xzp.cpp')
| -rw-r--r-- | public/xzp.cpp | 1323 |
1 files changed, 1323 insertions, 0 deletions
diff --git a/public/xzp.cpp b/public/xzp.cpp new file mode 100644 index 0000000..8a8a0f0 --- /dev/null +++ b/public/xzp.cpp @@ -0,0 +1,1323 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <malloc.h> +#ifdef _WIN32 +#include <process.h> +#include <io.h> +#endif +#include <stddef.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "tier1/utlbuffer.h" +#include "tier1/strtools.h" +#include "tier2/riff.h" + +#if defined( _WIN32 ) && !defined( _X360 ) +#include <windows.h> +#endif + +#ifdef MAKE_GAMEDATA_TOOL + #include "../public/materialsystem/shader_vcs_version.h" + #include "../public/materialsystem/imaterial.h" + #include "../public/materialsystem/hardwareverts.h" + #include "../public/vtf/vtf.h" +#else + #include "materialsystem/shader_vcs_version.h" + #include "materialsystem/imaterial.h" + #include "materialsystem/hardwareverts.h" +#endif + +#include "xwvfile.h" +#include "xzp.h" + +CByteswap g_xzpSwap; +extern IFileReadBinary *g_pSndIO; + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Datadesc blocks for byteswapping: +//----------------------------------------------------------------------------- +BEGIN_BYTESWAP_DATADESC( xZipHeader_t ) + DEFINE_FIELD( Magic, FIELD_INTEGER ), + DEFINE_FIELD( Version, FIELD_INTEGER ), + DEFINE_FIELD( PreloadDirectoryEntries, FIELD_INTEGER ), + DEFINE_FIELD( DirectoryEntries, FIELD_INTEGER ), + DEFINE_FIELD( PreloadBytes, FIELD_INTEGER ), + DEFINE_FIELD( HeaderLength, FIELD_INTEGER ), + DEFINE_FIELD( FilenameEntries, FIELD_INTEGER ), + DEFINE_FIELD( FilenameStringsOffset, FIELD_INTEGER ), + DEFINE_FIELD( FilenameStringsLength, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( xZipDirectoryEntry_t ) + DEFINE_FIELD( FilenameCRC, FIELD_INTEGER ), + DEFINE_FIELD( Length, FIELD_INTEGER ), + DEFINE_FIELD( StoredOffset, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( xZipFilenameEntry_t ) + DEFINE_FIELD( FilenameCRC, FIELD_INTEGER ), + DEFINE_FIELD( FilenameOffset, FIELD_INTEGER ), + DEFINE_FIELD( TimeStamp, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +BEGIN_BYTESWAP_DATADESC( xZipFooter_t ) + DEFINE_FIELD( Size, FIELD_INTEGER ), + DEFINE_FIELD( Magic, FIELD_INTEGER ), +END_BYTESWAP_DATADESC() + +CXZip::CXZip() +{ + // Ensure that the header doesn't contain a valid magic yet. + m_Header.Magic = 0; + m_pPreloadedData = NULL; + m_nPreloadStart = 0; + m_pDirectory = NULL; + m_pPreloadDirectory = NULL; + m_nRegular2PreloadEntryMapping = NULL; + + m_bByteSwapped = false; + + m_pFilenames = NULL; + m_hZip = NULL; + + m_pRead = NULL; + m_hUser = 0; + m_nMonitorLevel = 0; +} + +CXZip::CXZip( const char* filename ) +{ + // Ensure that the header doesn't contain a valid magic yet. + m_Header.Magic = 0; + m_nPreloadStart = 0; + m_pPreloadedData = NULL; + m_pDirectory = NULL; + m_pPreloadDirectory = NULL; + m_nRegular2PreloadEntryMapping = NULL; + + m_bByteSwapped = false; + + m_pFilenames = NULL; + m_hZip = NULL; + + m_pRead = NULL; + m_hUser = 0; + m_nMonitorLevel = 0; + + Load( filename ); +} + +CXZip::CXZip( FILE* handle, int offset, int size ) // file handle and offset of the zip file +{ + m_pRead = NULL; + m_hUser = 0; + m_nPreloadStart = 0; + m_pDirectory = NULL; + m_pPreloadDirectory = NULL; + m_nRegular2PreloadEntryMapping = NULL; + + m_bByteSwapped = false; + + m_pFilenames = NULL; + m_pPreloadedData = NULL; + m_nMonitorLevel = 0; + + Load( handle, offset, size ); +} + +CXZip::~CXZip() +{ + Unload(); +} + +bool CXZip::InstallAlternateIO( int (*read)( void* buffer, int offset, int length, int nDestLength, int hUser), int hUser ) +{ + m_pRead = read; + m_hUser = hUser; + return true; +} + + +// Loads an xZip file into memory: +bool CXZip::Load( const char* filename, bool bPreload ) +{ + FILE* hZip = fopen( filename, "rb" ); + fseek(hZip,0,SEEK_END); + int nSize = ftell( hZip ); + return Load( hZip, 0, nSize, bPreload ); +} + +bool CXZip::Load( FILE* handle, int nOffset, int nSize, bool bPreload ) // Load a pack file into this instance. Returns true on success. +{ + Unload(); + + m_bByteSwapped = false; + + m_hZip = handle; + m_nOffset = nOffset; + m_nSize = nSize; + + // Hacky, clean up: + if( m_hZip && !m_pRead ) + { + InstallAlternateIO( defaultRead, (int)m_hZip ); + } + + if( m_hZip == NULL && m_pRead == NULL ) + { + return false; + } + + // Read the header: + m_pRead( &m_Header, 0, -1, sizeof(m_Header), m_hUser ); + + // Validate the Magic number and at the same time determine if I am reading a regular or swappped xZip file: + switch( m_Swap.SourceIsNativeEndian<int>( m_Header.Magic, xZipHeader_t::MAGIC ) ) + { + // Does the magic match exactly? + case 1: + m_Swap.ActivateByteSwapping( false ); + m_bByteSwapped = false; + break; + + // Does the magic match, but is swapped? + case 0: + m_bByteSwapped = true; + m_Swap.ActivateByteSwapping( true ); // We must be reading the opposite endianness. + m_Swap.SwapFieldsToTargetEndian<xZipHeader_t>( &m_Header ); + break; + + default: + assert( 0 ); + // Fail gently in release: + + // The magic doesn't match in any respect: + case -1: + { + printf("Invalid xZip file\n"); + + if( m_hZip ) + { + fclose( m_hZip ); + m_hZip = NULL; + } + return false; + } + } + + // Validate the archive version: + if( m_Header.Version != xZipHeader_t::VERSION ) + { + // Backward compatable support for version 1 + Msg("Incorrect xZip version found %u - expected %u\n", m_Header.Version, xZipHeader_t::VERSION ); + if( m_hZip ) + { + fclose( m_hZip ); + m_hZip = NULL; + } + + m_Header.Magic = xZipHeader_t::FREE; + return false; + } + + // Read the directory: + { + MEM_ALLOC_CREDIT(); + + m_pDirectory = (xZipDirectoryEntry_t*)malloc( sizeof(xZipDirectoryEntry_t) * m_Header.DirectoryEntries ); + m_pRead( m_pDirectory, m_Header.HeaderLength, -1, sizeof( xZipDirectoryEntry_t ) * m_Header.DirectoryEntries, m_hUser ); + + // Swap the directory entries if nessecary + if( m_bByteSwapped ) + { + for( unsigned nDirectoryEntry = 0; nDirectoryEntry < m_Header.DirectoryEntries; nDirectoryEntry++ ) + { + m_Swap.SwapFieldsToTargetEndian<xZipDirectoryEntry_t>( &( m_pDirectory[nDirectoryEntry] ) ); + } + } + + + m_nPreloadStart = m_Header.HeaderLength + ( sizeof( xZipDirectoryEntry_t ) * m_Header.DirectoryEntries ); + } + + // Preload the preload chunk if desired: + if( bPreload ) + { + PreloadData(); + } + + return true; +} + +void CXZip::Unload() +{ + DiscardPreloadedData(); + + // Dump the directory: + if( m_pDirectory ) + { + free( m_pDirectory ); + m_pDirectory = NULL; + } + + if( m_pFilenames ) + { + free( m_pFilenames ); + m_pFilenames = NULL; + } + + // Invalidate the header: + m_Header.Magic = 0; + + if( m_hZip ) + { + fclose( m_hZip ); + m_hZip = NULL; + } + +} + +//----------------------------------------------------------------------------- +// CXZip::PreloadData +// +// Loads the preloaded data if it isn't already. +//----------------------------------------------------------------------------- + +void CXZip::PreloadData() +{ + Assert( IsValid() ); + + // Ensure it isn't already preloaded + if( m_pPreloadedData ) + return; + + // If I don't have a preloaded section, ignore the request. + if( !m_Header.PreloadBytes || !m_Header.PreloadDirectoryEntries ) + return; + + // Allocate and read the data block in: +#ifndef _X360 + MEM_ALLOC_CREDIT_( "xZip" ); + m_pPreloadedData = malloc( m_Header.PreloadBytes ); + + // Just drop out if allocation fails; + if ( !m_pPreloadedData ) + return; + + m_pRead( m_pPreloadedData, m_nPreloadStart, -1, m_Header.PreloadBytes, m_hUser ); +#else + int nAlignedStart = AlignValue( ( m_nPreloadStart - XBOX_HDD_SECTORSIZE ) + 1, XBOX_HDD_SECTORSIZE ); + int nBytesToRead = AlignValue( ( m_nPreloadStart - nAlignedStart ) + m_Header.PreloadBytes, XBOX_HDD_SECTORSIZE ); + int nBytesBuffer = AlignValue( nBytesToRead, XBOX_HDD_SECTORSIZE ); + byte *pReadData = (byte *)malloc( nBytesBuffer ); + + // Just drop out if allocation fails; + if ( !pReadData ) + return; + + MEM_ALLOC_CREDIT_( "xZip" ); + m_pRead( pReadData, nAlignedStart, nBytesBuffer,nBytesToRead, m_hUser ); + m_pPreloadedData = pReadData + ( m_nPreloadStart - nAlignedStart ); +#endif + + // Set up the preload directory: + m_pPreloadDirectory = (xZipDirectoryEntry_t*)m_pPreloadedData; + + // Swap the preload directory: + if ( m_bByteSwapped ) + { + for ( unsigned nDirectoryEntry = 0; nDirectoryEntry < m_Header.PreloadDirectoryEntries; nDirectoryEntry++ ) + { + m_Swap.SwapFieldsToTargetEndian<xZipDirectoryEntry_t>( &( m_pPreloadDirectory[nDirectoryEntry] ) ); + } + } + + // Set up the regular 2 preload mapping section: + m_nRegular2PreloadEntryMapping = (unsigned short*)(((unsigned char*)m_pPreloadDirectory) + ( sizeof(xZipDirectoryEntry_t) * m_Header.PreloadDirectoryEntries )); + + // Swap the regular to preload mapping + if ( m_bByteSwapped ) + { + m_Swap.SwapBufferToTargetEndian<short>( (short *)m_nRegular2PreloadEntryMapping, (short *)m_nRegular2PreloadEntryMapping, m_Header.DirectoryEntries ); + } + +} + +//----------------------------------------------------------------------------- +// CXZip::DiscardPreloadedData +// +// frees the preloaded data cache if it's present. +//----------------------------------------------------------------------------- + +void CXZip::DiscardPreloadedData() +{ + if ( m_pPreloadedData ) + { +#ifndef _X360 + free( m_pPreloadedData ); +#else + int nAlignedStart = AlignValue( ( m_nPreloadStart - XBOX_HDD_SECTORSIZE ) + 1, XBOX_HDD_SECTORSIZE ); + byte *pReadData = (byte *)m_pPreloadedData - ( m_nPreloadStart - nAlignedStart ); + free( pReadData ); +#endif + m_pPreloadedData = NULL; + m_pPreloadDirectory = NULL; + m_nRegular2PreloadEntryMapping = NULL; + } +} + +int CXZip::defaultRead( void* buffer, int offset, int destLength, int length, int hUser) +{ + fseek( (FILE*)hUser, offset, SEEK_SET ); + return fread( buffer, 1, length, (FILE*)hUser ); +} + +char* CXZip::GetEntryFileName( unsigned CRC, char* pDefault ) +{ + Assert( IsValid() ); + + if( IsRetail() ) + { + return pDefault; + } + else + { + + // Make sure I have a filename section: + if( m_Header.FilenameStringsOffset == 0 || m_Header.FilenameEntries == 0 || CRC == 0 ) + { + return pDefault; + } + + // If the filename chunk isn't here, load it up: + if( !m_pFilenames ) + { + MEM_ALLOC_CREDIT_("xZip"); + m_pFilenames = (xZipFilenameEntry_t*)malloc( m_Header.FilenameStringsLength ); + m_pRead( m_pFilenames, m_Header.FilenameStringsOffset, -1, m_Header.FilenameStringsLength, m_hUser ); + + // TODO: Swap! + for( unsigned int i=0; i< m_Header.FilenameEntries;i++ ) + { + m_Swap.SwapFieldsToTargetEndian<xZipFilenameEntry_t>(&m_pFilenames[i]); + } + } + + // Find this entry in the preload directory + xZipFilenameEntry_t entry; + entry.FilenameCRC = CRC; + + xZipFilenameEntry_t* found = (xZipFilenameEntry_t*)bsearch( &entry, m_pFilenames, m_Header.FilenameEntries, sizeof(xZipFilenameEntry_t), xZipFilenameEntry_t::xZipFilenameEntryCompare ); + + if( !found ) + return pDefault; + + return (((char*)m_pFilenames) + found->FilenameOffset) - m_Header.FilenameStringsOffset; + } +} + +// Sanity checks that the zip file is ready and readable: +bool CXZip::IsValid() +{ + if( m_Header.Magic != xZipHeader_t::MAGIC ) + return false; + + if( m_Header.Version > xZipHeader_t::VERSION ) + return false; + + if( !m_pDirectory ) + return false; + + return true; +} + +void CXZip::WarningDir() +{ + Assert( IsValid()); + + for( unsigned i = 0; i< m_Header.DirectoryEntries; i++ ) + { + Msg( GetEntryFileName( m_pDirectory[i].FilenameCRC ) ); + } +} + + +int CXZip::ReadIndex( int nEntryIndex, int nFileOffset, int nDestBytes, int nLength, void* pBuffer ) +{ + Assert( IsValid() ); + + if( nLength <=0 || nEntryIndex < 0 ) + return 0; + + // HACK HACK HACK - convert the pack file index to a local file index (ie, assuming the full file index is being passed in) + nFileOffset -= m_pDirectory[nEntryIndex].StoredOffset; + // HACK HACK HACK + + // If I've got my preload section loaded, first check there: + xZipDirectoryEntry_t* pPreloadEntry = GetPreloadEntry(nEntryIndex); + + if( pPreloadEntry ) + { + Assert( pPreloadEntry->FilenameCRC == m_pDirectory[nEntryIndex].FilenameCRC ); + + if( nFileOffset + nLength <= (int)pPreloadEntry->Length ) + { + if( m_nMonitorLevel >= 2 ) + { + char* filename = GetEntryFileName( m_pDirectory[nEntryIndex].FilenameCRC, "(!!! unknown !!!)" ); + + Msg("PACK(preload) %s: length:%i offset:%i",filename,nLength, nFileOffset); + + } + + memcpy( pBuffer, (char*)m_pPreloadedData + pPreloadEntry->StoredOffset + nFileOffset - m_nPreloadStart, nLength ); + return nLength; + } + } + + // Offset int the zip to start the read: + int ZipOffset = m_pDirectory[nEntryIndex].StoredOffset + nFileOffset; + int nBytesRead = m_pRead( pBuffer, ZipOffset, nDestBytes, nLength, m_hUser); + + if( m_nMonitorLevel ) + { + char* filename = GetEntryFileName( m_pDirectory[nEntryIndex].FilenameCRC, "(!!! unknown !!!)" ); + + unsigned preload = 0; + if( m_pPreloadedData && m_nRegular2PreloadEntryMapping[nEntryIndex] != 0xFFFF ) + { + // Find this entry in the preload directory + xZipDirectoryEntry_t* entry = &(m_pPreloadDirectory[m_nRegular2PreloadEntryMapping[nEntryIndex]]); + Assert(entry->FilenameCRC == m_pDirectory[nEntryIndex].FilenameCRC); + + preload = entry->Length; + } + + Msg("PACK %s: length:%i offset:%i (preload bytes:%i)",filename,nLength, nFileOffset, preload); + } + + return nBytesRead; +} + +bool CXZip::GetSimpleFileOffsetLength( const char* FileName, int& nBaseIndex, int &nFileOffset, int &nLength ) +{ + Assert( IsValid() ); + + xZipDirectoryEntry_t entry; + entry.FilenameCRC = xZipCRCFilename( FileName ); + + xZipDirectoryEntry_t* found = (xZipDirectoryEntry_t*)bsearch( &entry, m_pDirectory, m_Header.DirectoryEntries, sizeof(xZipDirectoryEntry_t), xZipDirectoryEntry_t::xZipDirectoryEntryFindCompare ); + + if( found == NULL ) + return false; + + nFileOffset = found[0].StoredOffset; + nLength = found[0].Length; + nBaseIndex = (((int)((char*)found - (char*)m_pDirectory))/sizeof(xZipDirectoryEntry_t)); + + return true; +} + +bool CXZip::ExtractFile( const char* FileName ) +{ + return false; +} + +// Compares to xZipDirectoryEntries. +// +// Sorts in the following order: +// FilenameCRC +// FileOffset +// Length +// StoredOffset +// +// The sort function may look overly complex, but it is actually useful for locating different pieces of +// the same file in a meaningful order. +// +int __cdecl xZipDirectoryEntry_t::xZipDirectoryEntrySortCompare( const void* left, const void* right ) +{ + xZipDirectoryEntry_t *l = (xZipDirectoryEntry_t*)left, + *r = (xZipDirectoryEntry_t*)right; + + if( l->FilenameCRC < r->FilenameCRC ) + { + return -1; + } + + else if( l->FilenameCRC > r->FilenameCRC ) + { + return 1; + } + + // else l->FileOffset == r->FileOffset + if( l->Length < r->Length ) + { + return -1; + } + else if( l->Length > r->Length ) + { + return 1; + } + + // else l->Length == r->Length + if( l->StoredOffset < r->StoredOffset ) + { + return -1; + } + else if( l->StoredOffset > r->StoredOffset ) + { + return 1; + } + + // else everything is identical: + return 0; + +} + +// Find an entry with matching CRC only +int __cdecl xZipDirectoryEntry_t::xZipDirectoryEntryFindCompare( const void* left, const void* right ) +{ + xZipDirectoryEntry_t *l = (xZipDirectoryEntry_t*)left, + *r = (xZipDirectoryEntry_t*)right; + + if( l->FilenameCRC < r->FilenameCRC ) + { + return -1; + } + + else if( l->FilenameCRC > r->FilenameCRC ) + { + return 1; + } + + return 0; + +} + +int __cdecl xZipFilenameEntry_t::xZipFilenameEntryCompare( const void* left, const void* right ) +{ + xZipFilenameEntry_t *l = (xZipFilenameEntry_t*)left, + *r = (xZipFilenameEntry_t*)right; + + if( l->FilenameCRC < r->FilenameCRC ) + { + return -1; + } + + else if( l->FilenameCRC > r->FilenameCRC ) + { + return 1; + } + + return 0; + +} + + +// CRC's an individual xZip filename: +unsigned xZipCRCFilename( const char* filename ) +{ + unsigned hash = 0xAAAAAAAA; // Alternating 1's and 0's + + for( ; *filename ; filename++ ) + { + char c = *filename; + + // Fix slashes + if( c == '/' ) + c = '\\'; + else + c = (char)tolower(c); + + hash = hash * 33 + c; + } + + return hash; +} + +#if defined( MAKE_GAMEDATA_TOOL ) + +// ------------ +xZipHeader_t Header; +xZipDirectoryEntry_t *pDirectoryEntries = NULL; +xZipDirectoryEntry_t *pPreloadDirectoryEntries = NULL; +xZipFilenameEntry_t *pFilenameEntries = NULL; +char *pFilenameData = NULL; +unsigned nFilenameDataLength = 0; + +unsigned InputFileBytes = 0; + +char* CleanFilename( char* filename ) +{ + // Trim leading white space: + while( isspace(*filename) ) + filename++; + + // Trim trailing white space: + while( isspace( filename[strlen(filename)-1] ) ) + { + filename[strlen(filename)-1] = '\0'; + } + + return filename; +} + + +bool CopyFileBytes( FILE* hDestination, FILE* hSource, unsigned nBytes ) +{ + char buffer[16384]; + + while( nBytes > 0 ) + { + int nBytesRead = fread( buffer, 1, nBytes > sizeof(buffer) ? sizeof(buffer) : nBytes, hSource ); + fwrite(buffer, 1, nBytesRead, hDestination ); + nBytes -= nBytesRead; + } + + return true; +} + +bool WriteFileBytes( FILE* hDestination, CUtlBuffer &source, unsigned nBytes ) +{ + unsigned int nBytesWritten = fwrite(source.Base(), 1, nBytes, hDestination ); + return (nBytesWritten == nBytes); +} + +void PadFileBytes(FILE* hFile, int nPreloadPadding ) +{ + if( nPreloadPadding < 0 || nPreloadPadding >= 512) + { + puts("Invalid padding"); + return; + } + + char padding[512]; + memset(padding,0,nPreloadPadding); + fwrite(padding,1,nPreloadPadding,hFile); +} + +void AddFilename( const char* filename ) +{ + unsigned CRCfilename = xZipCRCFilename( filename ); + + // If we already have this filename don't add it again: + for( int i = 0; i < (int)Header.FilenameEntries; i++ ) + { + if( pFilenameEntries[i].FilenameCRC == CRCfilename ) + { + return; + } + } + + Header.FilenameEntries++; + + // Add the file to the file string table: + pFilenameEntries = (xZipFilenameEntry_t*)realloc( pFilenameEntries, sizeof(xZipFilenameEntry_t) * Header.FilenameEntries ); + + int filenameLength = (int)strlen(filename) + 1; + pFilenameEntries[Header.FilenameEntries-1].FilenameCRC = CRCfilename; + pFilenameEntries[Header.FilenameEntries-1].FilenameOffset = nFilenameDataLength; + + // Grab the timestamp for the file: + struct stat buf; + if( stat( filename, &buf ) != -1 ) + { + pFilenameEntries[Header.FilenameEntries - 1].TimeStamp = buf.st_mtime; + } + else + { + pFilenameEntries[Header.FilenameEntries - 1].TimeStamp = 0; + } + + nFilenameDataLength += filenameLength; + pFilenameData = (char*)realloc(pFilenameData, nFilenameDataLength); + memcpy(pFilenameData + nFilenameDataLength - filenameLength, filename, filenameLength); +} + +FILE* hTempFilePreload; +FILE* hTempFileData; +FILE* hOutputFile; + +bool xZipAddFile( const char* filename, CUtlBuffer &fileBuff, bool bPrecacheEntireFile, bool bProcessPrecacheHeader, bool bProcessPrecacheHeaderOnly ) +{ + unsigned int fileSize = fileBuff.TellMaxPut(); + + // Track total input bytes for stats reasons + InputFileBytes += fileSize; + + unsigned customPreloadSize = 0; + + if( bPrecacheEntireFile ) + { + customPreloadSize = fileSize; + } + else if( bProcessPrecacheHeader ) + { + customPreloadSize = xZipComputeCustomPreloads( filename ); + } + else if( bProcessPrecacheHeaderOnly ) + { + customPreloadSize = xZipComputeCustomPreloads( filename ); + fileSize = min( fileSize, customPreloadSize ); + } + + unsigned CRC = xZipCRCFilename( filename ); + + // Does this file have a split header? + if( customPreloadSize > 0 ) + { + // Initialize the entry header: + xZipDirectoryEntry_t entry; + memset( &entry, 0, sizeof( entry ) ); + + entry.FilenameCRC = CRC; + entry.Length = customPreloadSize; + entry.StoredOffset = ftell(hTempFilePreload); + + // Add the directory entry to the preload table: + Header.PreloadDirectoryEntries++; + pPreloadDirectoryEntries = (xZipDirectoryEntry_t*)realloc( pPreloadDirectoryEntries, sizeof( xZipDirectoryEntry_t ) * Header.PreloadDirectoryEntries ); + memcpy( pPreloadDirectoryEntries + Header.PreloadDirectoryEntries - 1, &entry, sizeof( entry ) ); + + // Concatenate the data in the preload file: + fileBuff.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); + WriteFileBytes( hTempFilePreload, fileBuff, entry.Length ); + fileBuff.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); + + + // Add the filename entry: + AddFilename( filename ); + + // Spew it: + printf("+Preload: \"%s\": Length:%u\n", filename, entry.Length ); + } + + // Copy the file to the regular data region: + xZipDirectoryEntry_t entry; + memset(&entry,0,sizeof(entry)); + entry.FilenameCRC = CRC; + entry.Length = fileSize; + entry.StoredOffset = ftell(hTempFileData); + + // Add the directory entry to the table: + Header.DirectoryEntries++; + pDirectoryEntries = (xZipDirectoryEntry_t*)realloc( pDirectoryEntries, sizeof( xZipDirectoryEntry_t ) * Header.DirectoryEntries ); + memcpy( pDirectoryEntries + Header.DirectoryEntries - 1, &entry, sizeof( entry ) ); + + WriteFileBytes( hTempFileData, fileBuff, entry.Length ); + + // Align the data region to a 512 byte boundry: (has to be on last entry as well to ensure enough space to perform the final read, + // and initial alignment is taken careof by assembexzip) + int nPadding = ( XBOX_HDD_SECTORSIZE - ( ftell( hTempFileData ) % XBOX_HDD_SECTORSIZE) ) % XBOX_HDD_SECTORSIZE; + + PadFileBytes( hTempFileData, nPadding ); + + // Add the file to the file string table: + AddFilename( filename ); + + // Print a summary + printf("+File: \"%s\": Length:%u Padding:%i\n", filename, entry.Length, nPadding ); + + return true; +} + +bool xZipAddFile( const char* zipname, bool bPrecacheEntireFile, bool bProcessPrecacheHeader, bool bProcessPrecacheHeaderOnly ) +{ + // Clean up the filename: + char buffer[MAX_PATH]; + strcpy(buffer, zipname); + + // Fix slashes and convert it to lower case: + char *filename; + for( filename = buffer; *filename; filename++ ) + { + if( *filename == '/' ) + *filename = '\\'; + else + { + *filename = (char)tolower(*filename); + } + } + + // Skip leading white space: + for( filename = buffer; isspace(*filename); filename++ ) + ; + + // Obliterate trailing white space: + for(;;) + { + int len = (int)strlen( filename ); + if( len <= 0 ) + { + printf("!!!! BAD FILENAME: \"%s\"\n", filename ); + return false; + } + + if( isspace( filename[len-1] ) ) + filename[len-1]='\0'; + else + break; + } + + // Ensure we don't already have this file: + unsigned CRC = xZipCRCFilename( filename ); + + for( unsigned i=0; i < Header.DirectoryEntries; i++ ) + { + if( pDirectoryEntries[i].FilenameCRC == CRC ) + { + printf("!!!! NOT ADDING DUPLICATE FILENAME: \"%s\"\n", filename ); + return false; + } + } + + // Attempt to open the file: + FILE* hFile = fopen( filename, "rb" ); + if( !hFile ) + { + printf("!!!! FAILED TO OPEN FILE: \"%s\"\n", filename ); + return false; + } + + // Get the length of the file: + fseek(hFile,0,SEEK_END); + unsigned fileSize = ftell(hFile); + fseek(hFile,0,SEEK_SET); + + CUtlBuffer fileBuff; + fileBuff.EnsureCapacity( fileSize ); + fread( fileBuff.Base(), fileSize, 1, hFile ); + fclose( hFile ); + + fileBuff.SeekPut( CUtlBuffer::SEEK_HEAD, fileSize ); + + return xZipAddFile( zipname, fileBuff, bPrecacheEntireFile, bProcessPrecacheHeader, bProcessPrecacheHeaderOnly ); +} + +int xZipBegin( const char* fileNameXzip ) +{ + // Create and initialize the header: + memset( &Header, 0, sizeof(Header) ); // Zero out the header: + Header.Magic = xZipHeader_t::MAGIC; + Header.Version = xZipHeader_t::VERSION; + Header.HeaderLength = sizeof(Header); + + // Open the output file: + hOutputFile = fopen(fileNameXzip,"wb+"); + if( !hOutputFile ) + { + printf("Failed to open \"%s\" for writing.\n", fileNameXzip); + exit( EXIT_FAILURE); + } + + // Create a temporary file for storing the preloaded data: + hTempFilePreload = tmpfile(); + if( !hTempFilePreload ) + { + printf( "Error: failed to create temporary file\n" ); + return EXIT_FAILURE; + } + + // Create a temporary file for storing the non preloaded data + hTempFileData = tmpfile(); + if( !hTempFileData ) + { + printf( "Error: failed to create temporary file\n"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +bool xZipEnd() +{ + int nPreloadDirectorySize = sizeof(xZipDirectoryEntry_t)*Header.PreloadDirectoryEntries; + int nRegular2PreloadSize = sizeof(unsigned short) * Header.DirectoryEntries; + + // Compute the size of the preloaded section: + if( Header.PreloadDirectoryEntries ) + { + fseek( hTempFilePreload, 0, SEEK_END ); + Header.PreloadBytes = ftell(hTempFilePreload) + nPreloadDirectorySize + nRegular2PreloadSize; // Raw# of bytes to preload + fseek( hTempFilePreload, 0, SEEK_SET ); + } + else + { + Header.PreloadBytes = 0; + } + + // Number of bytes preceeding the preloaded section: + int nPreloadOffset = sizeof( Header ) + ( sizeof( xZipDirectoryEntry_t ) * Header.DirectoryEntries ); + + // Number of bytes to pad between the end of the preload section and the start of the data section: + int nPadding = ( 512 - ( ( nPreloadOffset + Header.PreloadBytes ) % 512) ) %512; // Number of alignment bytes after the preload section + + // Offset past the preload section: + int nDataOffset = nPreloadOffset + Header.PreloadBytes + nPadding; + + // Write out the header: (will need to be rewritten at the end as well) - note: not even bothering to byteswap at this point + fwrite(&Header,sizeof(Header),1,hOutputFile); + + + // Fixup each of the directory entries to make them relative to the beginning of the file. + for( unsigned i=0; i< Header.DirectoryEntries;i++ ) + { + xZipDirectoryEntry_t* pDir = &(pDirectoryEntries[i]); + + // Adjust files in the regular data area: + pDir->StoredOffset = nDataOffset + pDir->StoredOffset; + } + + // Sort and write the directory: + printf("Sorting and writing %i directory entries...\n",Header.DirectoryEntries); + qsort(pDirectoryEntries,Header.DirectoryEntries,sizeof(xZipDirectoryEntry_t),&xZipDirectoryEntry_t::xZipDirectoryEntrySortCompare); + + // Swap the directory entries: + for( unsigned i=0; i < Header.DirectoryEntries; i++ ) + { + g_xzpSwap.SwapFieldsToTargetEndian<xZipDirectoryEntry_t>(&pDirectoryEntries[i]); + } + + fwrite(pDirectoryEntries,Header.DirectoryEntries*sizeof(xZipDirectoryEntry_t),1, hOutputFile); + + // Swap the directory back for later use: + for( unsigned i=0; i < Header.DirectoryEntries; i++ ) + { + g_xzpSwap.SwapFieldsToTargetEndian<xZipDirectoryEntry_t>(&pDirectoryEntries[i]); + } + + // Copy the preload section: + if( Header.PreloadBytes > 0 ) + { + printf("Generating the preload section...(%u)\n", Header.PreloadBytes); + + + // Fixup each of the directory entries to make them relative to the beginning of the file. + for( unsigned i=0; i< Header.PreloadDirectoryEntries;i++ ) + { + xZipDirectoryEntry_t* pDir = &(pPreloadDirectoryEntries[i]); + + // Shift preload data down by preload bytes (and skipping over the directory): + pDir->StoredOffset += nPreloadOffset + nPreloadDirectorySize + nRegular2PreloadSize; + } + + printf("Sorting %u preload directory entries...\n",Header.PreloadDirectoryEntries); + qsort(pPreloadDirectoryEntries,Header.PreloadDirectoryEntries,sizeof(xZipDirectoryEntry_t),&xZipDirectoryEntry_t::xZipDirectoryEntrySortCompare); + + printf("Building regular to preload mapping table for %u entries...\n", Header.DirectoryEntries ); + unsigned short* Regular2Preload = (unsigned short*)malloc( nRegular2PreloadSize ); + for( unsigned i = 0; i < Header.DirectoryEntries; i++ ) + { + unsigned short j; + for( j = 0; j < Header.PreloadDirectoryEntries; j++ ) + { + if( pDirectoryEntries[i].FilenameCRC == pPreloadDirectoryEntries[j].FilenameCRC ) + break; + } + + // If I couldn't find it mark it as non-existant: + if( j == Header.PreloadDirectoryEntries ) + j = 0xFFFF; + + Regular2Preload[i] = j; + } + + printf("Writing preloaded directory entreis...\n" ); + + // Swap the preload directory entries: + for( unsigned i=0; i < Header.PreloadDirectoryEntries; i++ ) + { + g_xzpSwap.SwapFieldsToTargetEndian<xZipDirectoryEntry_t>(&pPreloadDirectoryEntries[i]); + } + + fwrite( pPreloadDirectoryEntries, Header.PreloadDirectoryEntries*sizeof(xZipDirectoryEntry_t),1, hOutputFile ); + + // Swap them back: + for( unsigned i=0; i < Header.PreloadDirectoryEntries; i++ ) + { + g_xzpSwap.SwapFieldsToTargetEndian<xZipDirectoryEntry_t>(&pPreloadDirectoryEntries[i]); + } + + printf("Writing regular to preload mapping (%u bytes)...\n", sizeof(unsigned short)*Header.DirectoryEntries ); + + // Swap regular to preload mapping: + g_xzpSwap.SwapBufferToTargetEndian<short>((short*)Regular2Preload, (short*)Regular2Preload, nRegular2PreloadSize / sizeof(short) ); + + fwrite( Regular2Preload, nRegular2PreloadSize,1,hOutputFile ); + + // Swap it back + g_xzpSwap.SwapBufferToTargetEndian<short>((short*)Regular2Preload, (short*)Regular2Preload, nRegular2PreloadSize / sizeof(short) ); + + printf("Copying %u Preloadable Bytes...\n", Header.PreloadBytes - nPreloadDirectorySize - nRegular2PreloadSize ); + fseek(hTempFilePreload,0,SEEK_SET); + CopyFileBytes(hOutputFile, hTempFilePreload, Header.PreloadBytes - nPreloadDirectorySize - nRegular2PreloadSize ); + } + + // Align the data section following the preload section: + if( nPadding ) + { + printf("Aligning Data Section Start by %u bytes...\n", nPadding ); + PadFileBytes(hOutputFile, nPadding ); + } + + // Copy the data section: + fseek(hTempFileData, 0, SEEK_END ); + unsigned length = ftell( hTempFileData ); + fseek(hTempFileData, 0, SEEK_SET ); + printf("Copying %u Bytes...\n",length); + + CopyFileBytes(hOutputFile, hTempFileData, length); + + // Write out the filename data if present: + if( nFilenameDataLength && Header.FilenameEntries ) + { + Header.FilenameStringsOffset = ftell(hOutputFile); + Header.FilenameStringsLength = (Header.FilenameEntries*sizeof(xZipFilenameEntry_t)) + nFilenameDataLength; + + // Adjust the offset in each of the filename offsets to absolute position in the file. + for( unsigned i=0;i<Header.FilenameEntries;i++ ) + { + pFilenameEntries[i].FilenameOffset += ( Header.FilenameStringsOffset + (Header.DirectoryEntries*sizeof(xZipFilenameEntry_t))); + } + + printf("Sorting and writing %u filename directory entries...\n",Header.FilenameEntries); + + // Sort the data: + qsort(pFilenameEntries,Header.FilenameEntries,sizeof(xZipFilenameEntry_t),&xZipFilenameEntry_t::xZipFilenameEntryCompare); + + // Write the data out: + for( unsigned int i = 0; i < Header.FilenameEntries; i++ ) + { + g_xzpSwap.SwapFieldsToTargetEndian<xZipFilenameEntry_t>(&pFilenameEntries[i]); + } + + fwrite(pFilenameEntries,1,Header.FilenameEntries*sizeof(xZipFilenameEntry_t),hOutputFile); + + // Swap them back: + for( unsigned int i = 0; i < Header.FilenameEntries; i++ ) + { + g_xzpSwap.SwapFieldsToTargetEndian<xZipFilenameEntry_t>(&pFilenameEntries[i]); + } + + printf("Writing %u bytes of filename data...\n",nFilenameDataLength); + fwrite(pFilenameData,1,nFilenameDataLength,hOutputFile); + } + + // Compute the total file size, including the size of the footer: + unsigned OutputFileBytes = ftell(hOutputFile) + sizeof(xZipFooter_t); + + // Write the footer: (block used to keep possibly swapped footer from being used later) + { + xZipFooter_t footer; + footer.Magic = xZipFooter_t::MAGIC; + footer.Size = OutputFileBytes; + + g_xzpSwap.SwapFieldsToTargetEndian<xZipFooter_t>( &footer ); // Swap the footer + fwrite( &footer, 1, sizeof(footer), hOutputFile ); + } + + // Seek back and rewrite the header (filename data changes it for example) + fseek(hOutputFile,0,SEEK_SET); + g_xzpSwap.SwapFieldsToTargetEndian<xZipHeader_t>( &Header ); // Swap it to write out: + fwrite(&Header,1,sizeof(Header),hOutputFile); + g_xzpSwap.SwapFieldsToTargetEndian<xZipHeader_t>( &Header ); // But then swap it back so we can use it in memory + + // Shut down + fclose(hOutputFile); + + // Print the summary + printf("\n\nSummary: Input:%u, XZip:%u, Directory Entries:%u (%u preloaded), Preloaded Bytes:%u\n\n",InputFileBytes,OutputFileBytes,Header.DirectoryEntries, Header.PreloadDirectoryEntries, Header.PreloadBytes); + + // Shut down: + fclose(hTempFileData); + fclose(hTempFilePreload); + + return true; +} + +#define PADD_ID MAKEID('P','A','D','D') + +//----------------------------------------------------------------------------- +// xZipComputeWAVPreload +// +// Returns the number of bytes from a xbox compliant WAV file that should go into +// the preload section: +//----------------------------------------------------------------------------- +unsigned xZipComputeWAVPreload( char *pFileName ) +{ + InFileRIFF riff( pFileName, *g_pSndIO ); + if ( riff.RIFFName() != RIFF_WAVE ) + { + return 0; + } + + IterateRIFF walk( riff, riff.RIFFSize() ); + + while ( walk.ChunkAvailable() ) + { + // xbox compliant wavs have a single PADD chunk + if ( walk.ChunkName() == PADD_ID ) + { + // want to preload data up through PADD chunk header + // and not the actual pad bytes + return walk.ChunkFilePosition() + 2*sizeof( int ); + } + walk.ChunkNext(); + } + + return 0; +} + + +//----------------------------------------------------------------------------- +// xZipComputeXWVPreload +// +// Returns the number of bytes from a XWV file that should go into the preload +// section: +//----------------------------------------------------------------------------- +unsigned xZipComputeXWVPreload( const char* filename ) +{ + FILE* hFile = fopen( filename, "rb" ); + if ( !hFile ) + { + printf( "Failed to open xwv file: %s\n", filename ); + return 0; + } + + // Read and validate the XWV header: + xwvHeader_t header; + memset( &header, 0, sizeof(header) ); + fread( &header, 1, sizeof(header), hFile ); + fclose( hFile ); + + if ( header.id != XWV_ID || header.headerSize != sizeof(header) ) + return 0; + + return header.GetPreloadSize(); +} + +unsigned xZipComputeXTFPreload( const char* filename ) +{ +#if 0 // X360TBD: Not using XTF anymore + FILE* hFile = fopen( filename, "rb" ); + if ( !hFile ) + { + printf("Failed to open file: %s\n", filename); + return 0; + } + + XTFFileHeader_t header; + memset( &header,0, sizeof( header ) ); + fread( &header,1,sizeof(header),hFile); + + fclose(hFile); + + if ( !strncmp( header.fileTypeString, "XTF", 4 ) ) + return header.preloadDataSize; +#endif + return 0; +} + +// TODO: ONLY store them in the preload section: +unsigned xZipComputeVMTPreload( const char* filename ) +{ + // Store VMT's entirely + if ( !strstr(filename,".vmt") ) + return 0; + + FILE* hFile = fopen( filename, "rb" ); + if ( !hFile ) + { + printf("Failed to open file: %s\n", filename); + return 0; + } + + fseek( hFile, 0, SEEK_END ); + unsigned offset = ftell( hFile ); + fclose( hFile ); + return offset; +} + +// TODO: ONLY store them in the preload section: +unsigned xZipComputeVHVPreload( const char* filename ) +{ + // Store VMT's entirely + if ( !strstr(filename,".vhv") ) + return 0; + + FILE* hFile = fopen( filename, "rb" ); + if ( !hFile ) + { + printf("Failed to open file: %s\n", filename); + return 0; + } + + fclose( hFile ); + + // Just load the header: + return sizeof(HardwareVerts::FileHeader_t); +} + +unsigned xZipComputeXCSPreload( const char* filename ) +{ + if( !strstr(filename,".vcs") ) + return 0; + + FILE* hFile = fopen( filename, "rb" ); + if ( !hFile ) + { + printf("Failed to open file: %s\n", filename); + return 0; + } + + XShaderHeader_t header; + fread(&header,1,sizeof(XShaderHeader_t), hFile); + fseek(hFile,0,SEEK_END); + fclose(hFile); + + if (!header.IsValid()) + return 0; + + return header.BytesToPreload(); +} + +unsigned xZipComputeCustomPreloads( const char* filename ) +{ + // X360TBD: These all need to act on a utlbuffer + Assert( 0 ); + return 0; + +// strlwr(filename); + + unsigned offset = xZipComputeXWVPreload( filename ); + if ( offset ) + return offset; + + offset = xZipComputeVMTPreload( filename ); + if ( offset ) + return offset; + + offset = xZipComputeXCSPreload( filename ); + if ( offset ) + return offset; + + offset = xZipComputeVHVPreload( filename ); + if ( offset ) + return offset; + + return xZipComputeXTFPreload( filename ); +} + +#endif // MAKE_GAMEDATA_TOOL |