source-engine/engine/checksum_engine.cpp

416 lines
9.3 KiB
C++
Raw Normal View History

2020-04-23 00:56:21 +08:00
//========= Copyright 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);
}
// 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[1024];
int nBytesRead;
int nSize;
nSize = COM_OpenFile(pszFileName, &fp);
if ( !fp || ( nSize == -1 ) )
return FALSE;
// 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;
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 ( nBytesRead <= 0 || !g_pFileSystem->IsOk(fp) )
{
if ( fp )
g_pFileSystem->Close(fp);
return FALSE;
}
}
if ( fp )
g_pFileSystem->Close(fp);
return TRUE;
}
// 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
==================
*/
bool CRC_MapFile(CRC32_t *crcvalue, const char *pszFileName)
{
FileHandle_t fp;
byte chunk[1024];
int i, l;
int nBytesRead;
dheader_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(dheader_t), fp) == 0)
{
ConMsg("Could not read BSP header for map [%s].\n", pszFileName);
g_pFileSystem->Close(fp);
return false;
}
i = header.version;
if ( i < MINBSPVERSION || i > BSPVERSION )
{
g_pFileSystem->Close(fp);
ConMsg("Map [%s] has incorrect BSP version (%i should be %i).\n", pszFileName, i, 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;
}
// CRC across all lumps except for the Entities lump
for (l = 0; l < HEADER_LUMPS; l++)
{
if (l == LUMP_ENTITIES)
continue;
curLump = &header.lumps[l];
nSize = curLump->filelen;
g_pFileSystem->Seek( fp, startOfs + curLump->fileofs, FILESYSTEM_SEEK_HEAD );
// 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;
CRC32_ProcessBuffer(crcvalue, chunk, nBytesRead);
}
// If there was a disk error, indicate failure.
if ( !g_pFileSystem->IsOk(fp) )
{
if ( fp )
g_pFileSystem->Close(fp);
return false;
}
}
}
if ( fp )
g_pFileSystem->Close(fp);
return true;
}
bool MD5_MapFile(MD5Value_t *md5value, const char *pszFileName)
{
FileHandle_t fp;
byte chunk[1024];
int i, l;
int nBytesRead;
dheader_t header;
int nSize;
lump_t *curLump;
long startOfs;
nSize = COM_OpenFile(pszFileName, &fp);
if ( !fp || ( nSize == -1 ) )
return false;
MD5Context_t ctx;
V_memset( &ctx, 0, sizeof(MD5Context_t) );
MD5Init( &ctx );
startOfs = g_pFileSystem->Tell(fp);
// Don't MD5 the header.
if (g_pFileSystem->Read(&header, sizeof(dheader_t), fp) == 0)
{
ConMsg("Could not read BSP header for map [%s].\n", pszFileName);
g_pFileSystem->Close(fp);
return false;
}
i = header.version;
if ( i < MINBSPVERSION || i > BSPVERSION )
{
g_pFileSystem->Close(fp);
ConMsg("Map [%s] has incorrect BSP version (%i should be %i).\n", pszFileName, i, BSPVERSION);
return false;
}
if ( IsX360() )
{
// 360 bsp's store the pc checksum in the flags lump header
g_pFileSystem->Close(fp);
char versionString[65] = {0};
V_snprintf( versionString, ARRAYSIZE(versionString), "%d", header.lumps[LUMP_MAP_FLAGS].version );
V_memcpy( md5value->bits, versionString, MD5_DIGEST_LENGTH );
return true;
}
// MD5 across all lumps except for the Entities lump
for (l = 0; l < HEADER_LUMPS; l++)
{
if (l == LUMP_ENTITIES)
continue;
curLump = &header.lumps[l];
nSize = curLump->filelen;
g_pFileSystem->Seek( fp, startOfs + curLump->fileofs, FILESYSTEM_SEEK_HEAD );
// 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 );
}
// If there was a disk error, indicate failure.
if ( !g_pFileSystem->IsOk(fp) )
{
if ( fp )
g_pFileSystem->Close(fp);
return false;
}
}
}
if ( fp )
g_pFileSystem->Close(fp);
MD5Final( md5value->bits, &ctx );
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], const 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;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool MD5_Hash_Buffer( unsigned char pDigest[16], const unsigned char *pBuffer, int nSize, bool bSeed /* = FALSE */, unsigned int seed[4] /* = NULL */ )
{
MD5Context_t ctx;
if ( !pBuffer || !nSize )
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 1024 chunks
const unsigned char *pChunk = pBuffer;
while ( nSize > 0 )
{
const int nChunkSize = MIN( 1024, nSize );
MD5Update( &ctx, pChunk, nChunkSize );
nSize -= nChunkSize;
pChunk += nChunkSize; AssertValidReadPtr( pChunk );
}
MD5Final( pDigest, &ctx );
return true;
}