360 lines
8.2 KiB
C++
360 lines
8.2 KiB
C++
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: Engine specific CRC functions
|
|
//
|
|
//=============================================================================//
|
|
|
|
|
|
#include "checksum_engine.h"
|
|
#include "bspfile.h"
|
|
#include "filesystem.h"
|
|
#include "filesystem_engine.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: For proxy protecting
|
|
// Turns the passed data into a
|
|
// single byte block CRC based on current network sequence number.
|
|
// Input : *base -
|
|
// length -
|
|
// sequence -
|
|
// Output : byte COM_BlockSequenceCRCByte
|
|
//-----------------------------------------------------------------------------
|
|
byte COM_BlockSequenceCRCByte( byte *base, int length, int sequence )
|
|
{
|
|
CRC32_t crc;
|
|
byte *p;
|
|
byte chkb[60 + 4];
|
|
|
|
if (sequence < 0)
|
|
{
|
|
Sys_Error("sequence < 0, in COM_BlockSequenceCRCByte\n");
|
|
}
|
|
|
|
CRC32_t entry;
|
|
entry = CRC32_GetTableEntry( ( sequence + 1 ) % ( 256 * sizeof(CRC32_t) - 4 ) );
|
|
p = (byte *)&entry;
|
|
|
|
// Use up to the first 60 bytes of data and a 4 byte value from the
|
|
// CRC lookup table (divided into the sequence)
|
|
|
|
if (length > 60)
|
|
{
|
|
length = 60;
|
|
}
|
|
|
|
memcpy (chkb, base, length);
|
|
|
|
chkb[length+0] = p[0];
|
|
chkb[length+1] = p[1];
|
|
chkb[length+2] = p[2];
|
|
chkb[length+3] = p[3];
|
|
|
|
length += 4;
|
|
|
|
// Compute a crc based on the buffer.
|
|
CRC32_Init(&crc);
|
|
CRC32_ProcessBuffer(&crc, chkb, length);
|
|
CRC32_Final(&crc);
|
|
|
|
// Chop down to byte size
|
|
return (byte)(crc & 0xFF);
|
|
}
|
|
|
|
#if defined(_X360)
|
|
const int CRC_BUFSIZE = 16384;
|
|
#else
|
|
const int CRC_BUFSIZE = 65536;
|
|
#endif
|
|
|
|
// YWB: 5/18
|
|
/*
|
|
===================
|
|
bool CRC_File(unsigned short *crcvalue, char *pszFileName)
|
|
|
|
Computes CRC for given file. If there is an error opening/reading the file, returns false,
|
|
otherwise returns true and sets the crc value passed to it. The value should be initialized
|
|
with CRC_Init
|
|
==================
|
|
*/
|
|
bool CRC_File(CRC32_t *crcvalue, const char *pszFileName)
|
|
{
|
|
// Always re-init the CRC buffer
|
|
CRC32_Init( crcvalue );
|
|
|
|
FileHandle_t fp;
|
|
byte chunk[CRC_BUFSIZE];
|
|
int nBytesRead;
|
|
|
|
int nSize;
|
|
|
|
nSize = COM_OpenFile(pszFileName, &fp);
|
|
if ( !fp || ( nSize == -1 ) )
|
|
return FALSE;
|
|
|
|
// Now read in chunks
|
|
while (nSize > 0)
|
|
{
|
|
if (nSize > sizeof(chunk))
|
|
nBytesRead = g_pFileSystem->Read(chunk, sizeof(chunk), fp);
|
|
else
|
|
nBytesRead = g_pFileSystem->Read(chunk, nSize, fp);
|
|
|
|
// If any data was received, CRC it.
|
|
if (nBytesRead > 0)
|
|
{
|
|
nSize -= nBytesRead;
|
|
CRC32_ProcessBuffer(crcvalue, chunk, nBytesRead);
|
|
}
|
|
|
|
// We we are end of file, break loop and return
|
|
if ( g_pFileSystem->EndOfFile( fp ) )
|
|
{
|
|
g_pFileSystem->Close( fp );
|
|
fp = 0;
|
|
break;
|
|
}
|
|
// If there was a disk error, indicate failure.
|
|
else if ( !g_pFileSystem->IsOk(fp) )
|
|
{
|
|
if ( fp )
|
|
g_pFileSystem->Close(fp);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if ( fp )
|
|
g_pFileSystem->Close(fp);
|
|
return TRUE;
|
|
}
|
|
|
|
static BSPHeader_t *g_pMapHeader = NULL;
|
|
|
|
int __cdecl LumpCompare( const void *pElem0, const void *pElem1 )
|
|
{
|
|
const int *pLump0 = (const int *)pElem0;
|
|
const int *pLump1 = (const int *)pElem1;
|
|
return g_pMapHeader->lumps[*pLump0].fileofs - g_pMapHeader->lumps[*pLump1].fileofs;
|
|
}
|
|
|
|
// YWB: 5/18
|
|
/*
|
|
===================
|
|
bool CRC_MapFile(unsigned short *crcvalue, char *pszFileName)
|
|
|
|
Computes CRC for given map file. If there is an error opening/reading the file, returns false,
|
|
otherwise returns true and sets the crc value passed to it. The value should be initialized
|
|
with CRC_Init
|
|
|
|
For map (.bsp) files, the entity lump is not included in the CRC.
|
|
//FIXME make this work
|
|
==================
|
|
*/
|
|
|
|
ConVar debug_map_crc( "debug_map_crc", "0", FCVAR_RELEASE, "Prints CRC for each map lump loaded" );
|
|
|
|
bool CRC_MapFile(CRC32_t *crcvalue, const char *pszFileName)
|
|
{
|
|
FileHandle_t fp;
|
|
byte chunk[CRC_BUFSIZE];
|
|
int nBytesRead;
|
|
BSPHeader_t header;
|
|
int nSize;
|
|
lump_t *curLump;
|
|
long startOfs;
|
|
|
|
nSize = COM_OpenFile(pszFileName, &fp);
|
|
if ( !fp || ( nSize == -1 ) )
|
|
return false;
|
|
|
|
startOfs = g_pFileSystem->Tell(fp);
|
|
|
|
// Don't CRC the header.
|
|
if (g_pFileSystem->Read(&header, sizeof(BSPHeader_t), fp) == 0)
|
|
{
|
|
ConMsg("Could not read BSP header for map [%s].\n", pszFileName);
|
|
g_pFileSystem->Close(fp);
|
|
return false;
|
|
}
|
|
|
|
if ( header.m_nVersion < MINBSPVERSION || header.m_nVersion > BSPVERSION )
|
|
{
|
|
g_pFileSystem->Close(fp);
|
|
ConMsg("Map [%s] has incorrect BSP version (%i should be %i).\n", pszFileName, header.m_nVersion, BSPVERSION);
|
|
return false;
|
|
}
|
|
|
|
if ( IsX360() )
|
|
{
|
|
// 360 bsp's store the pc checksum in the flags lump header
|
|
g_pFileSystem->Close(fp);
|
|
*crcvalue = header.lumps[LUMP_MAP_FLAGS].version;
|
|
return true;
|
|
}
|
|
|
|
static char gamedir[MAX_OSPATH];
|
|
Q_FileBase( com_gamedir, gamedir, sizeof( gamedir ) );
|
|
|
|
g_pMapHeader = &header;
|
|
int lumpList[HEADER_LUMPS];
|
|
for ( int i = 0; i < HEADER_LUMPS; i++ )
|
|
{
|
|
lumpList[i] = i;
|
|
}
|
|
qsort( lumpList, HEADER_LUMPS, sizeof(lumpList[0]), LumpCompare );
|
|
|
|
CRC32_t lumpCRC;
|
|
|
|
// CRC across all lumps except for the Entities lump
|
|
for (int i = 0; i < HEADER_LUMPS; i++)
|
|
{
|
|
int l = lumpList[i];
|
|
if (l == LUMP_ENTITIES)
|
|
continue;
|
|
|
|
CRC32_Init(&lumpCRC);
|
|
|
|
// INFESTED_DLL - Alien Swarm wants a more relaxed CRC check so each client can compile the map themselves
|
|
if ( !Q_stricmp( gamedir, "infested" ) )
|
|
{
|
|
if ( l == LUMP_LIGHTING ||
|
|
l == LUMP_DISPINFO ||
|
|
l == LUMP_GAME_LUMP ||
|
|
l == LUMP_PRIMITIVES ||
|
|
l == LUMP_LEAF_AMBIENT_INDEX_HDR ||
|
|
l == LUMP_LEAF_AMBIENT_INDEX ||
|
|
l == LUMP_LEAF_AMBIENT_LIGHTING_HDR ||
|
|
l == LUMP_LEAF_AMBIENT_LIGHTING ||
|
|
l == LUMP_PAKFILE ||
|
|
l == LUMP_PHYSCOLLIDE ||
|
|
l == LUMP_TEXINFO ||
|
|
l == LUMP_PHYSDISP )
|
|
continue;
|
|
}
|
|
|
|
curLump = &header.lumps[l];
|
|
nSize = curLump->filelen;
|
|
if ( nSize <= 0 )
|
|
continue;
|
|
|
|
g_pFileSystem->Seek( fp, startOfs + curLump->fileofs, FILESYSTEM_SEEK_HEAD );
|
|
|
|
// Now read chunks
|
|
while (nSize > 0)
|
|
{
|
|
if (nSize > sizeof(chunk))
|
|
nBytesRead = g_pFileSystem->Read(chunk, sizeof(chunk), fp);
|
|
else
|
|
nBytesRead = g_pFileSystem->Read(chunk, nSize, fp);
|
|
|
|
// If any data was received, CRC it.
|
|
if (nBytesRead > 0)
|
|
{
|
|
nSize -= nBytesRead;
|
|
CRC32_ProcessBuffer(crcvalue, chunk, nBytesRead);
|
|
|
|
if ( debug_map_crc.GetBool() )
|
|
{
|
|
CRC32_ProcessBuffer(&lumpCRC, chunk, nBytesRead);
|
|
}
|
|
}
|
|
|
|
// If there was a disk error, indicate failure.
|
|
if ( !g_pFileSystem->IsOk(fp) )
|
|
{
|
|
if ( fp )
|
|
g_pFileSystem->Close(fp);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ( debug_map_crc.GetBool() )
|
|
{
|
|
Msg( "Lump %d crc %lu\n", l, lumpCRC );
|
|
}
|
|
}
|
|
|
|
if ( fp )
|
|
g_pFileSystem->Close(fp);
|
|
|
|
if ( debug_map_crc.GetBool() )
|
|
{
|
|
Msg( "Map file '%s' CRC = %lu\n", pszFileName, *crcvalue );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : digest[16] -
|
|
// *pszFileName -
|
|
// bSeed -
|
|
// seed[4] -
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool MD5_Hash_File(unsigned char digest[16], char *pszFileName, bool bSeed /* = FALSE */, unsigned int seed[4] /* = NULL */)
|
|
{
|
|
FileHandle_t fp;
|
|
byte chunk[1024];
|
|
int nBytesRead;
|
|
MD5Context_t ctx;
|
|
|
|
int nSize;
|
|
|
|
nSize = COM_OpenFile( pszFileName, &fp );
|
|
if ( !fp || ( nSize == -1 ) )
|
|
return false;
|
|
|
|
memset(&ctx, 0, sizeof(MD5Context_t));
|
|
|
|
MD5Init(&ctx);
|
|
|
|
if (bSeed)
|
|
{
|
|
// Seed the hash with the seed value
|
|
MD5Update( &ctx, (const unsigned char *)&seed[0], 16 );
|
|
}
|
|
|
|
// Now read in 1K chunks
|
|
while (nSize > 0)
|
|
{
|
|
if (nSize > 1024)
|
|
nBytesRead = g_pFileSystem->Read(chunk, 1024, fp);
|
|
else
|
|
nBytesRead = g_pFileSystem->Read(chunk, nSize, fp);
|
|
|
|
// If any data was received, CRC it.
|
|
if (nBytesRead > 0)
|
|
{
|
|
nSize -= nBytesRead;
|
|
MD5Update(&ctx, chunk, nBytesRead);
|
|
}
|
|
|
|
// We we are end of file, break loop and return
|
|
if ( g_pFileSystem->EndOfFile( fp ) )
|
|
{
|
|
g_pFileSystem->Close( fp );
|
|
fp = NULL;
|
|
break;
|
|
}
|
|
// If there was a disk error, indicate failure.
|
|
else if ( !g_pFileSystem->IsOk(fp) )
|
|
{
|
|
if ( fp )
|
|
g_pFileSystem->Close(fp);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if ( fp )
|
|
g_pFileSystem->Close(fp);
|
|
|
|
MD5Final(digest, &ctx);
|
|
|
|
return TRUE;
|
|
}
|
|
|