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