Link to home
Start Free TrialLog in
Avatar of TelDig
TelDigFlag for Canada

asked on

Can't get the right CRC32 of my PNG in C++

Greetings!
I want to validate PNG files before saving them in the database. But I can't seem to get the right CRC with the data. I tried to use the whole file data, and I tried with only the specified chunk data and I still can't match the CRC written in the file.
I used code from the following link http://www.libpng.org/pub/png/spec/1.2/PNG-CRCAppendix.html
I attached my code too.

Is there anyone who can tell me what I'm missing or doing wrong ?
bool CUtilityParserDlg::ValidatePNGFile(CString p_strFile)
{
   bool bIsValid = true;
 
   CFile          file;
   CFileException fileException;
   if(file.Open(p_strFile, CFile::modeRead, &fileException))
   {
      char pBuffer[9] = "";
      if(file.Read(pBuffer, 8) == 8) // Get the first 8 bytes.
      {
         char pPNG[9] = {137, 80, 78, 71, 13, 10, 26, 10, 0};
         if(strcmp(pBuffer, pPNG) == 0) // The two strings are the same.
         {
            BYTE* pBufFile =  new BYTE[file.GetLength()];
            file.SeekToBegin();
            file.Read(pBufFile, file.GetLength());
            for(int i = 8; i < file.GetLength(); ++i)
            {
               if(pBufFile[i+4] == 'I' &&
                  pBufFile[i+5] == 'D' &&
                  pBufFile[i+6] == 'A' &&
                  pBufFile[i+7] == 'T')
               {
                  // Get the data length.
                  DWORD dwDataLength = 0;
                  dwDataLength = pBufFile[i++]; // i
                  dwDataLength = (dwDataLength << 8) | pBufFile[i++]; // i + 1
                  dwDataLength = (dwDataLength << 8) | pBufFile[i++]; // i + 2
                  dwDataLength = (dwDataLength << 8) | pBufFile[i++]; // i + 3
 
                  // Get the data and calculate the CRC32
                  BYTE* pData       = &pBufFile[i]; // i + 4
                  DWORD dwFoundCRC  = crc(pData, dwDataLength);
 
                  DWORD dwTemp = crc(pData, file.GetLength());
 
                  // Read CRC
                  i += 4;
                  DWORD dwFileCRC = pBufFile[(i++) + dwDataLength]; // i + 8
                  dwFileCRC = (dwFileCRC << 8) | pBufFile[(i++) + dwDataLength]; // i + 9
                  dwFileCRC = (dwFileCRC << 8) | pBufFile[(i++) + dwDataLength]; // i + 10
                  dwFileCRC = (dwFileCRC << 8) | pBufFile[i + dwDataLength]; // i + 11
 
                  // Compare the found CRC with the one from the file.
                  if(dwFoundCRC != dwFileCRC)
                  {
                     bIsValid = false;
                  }
               }
            }
 
            delete [] pBufFile;
         }
         else
         {
            bIsValid = false;
         }
      }
      else
      {
         bIsValid = false;
      }
 
      file.Close();
   }
 
   return bIsValid;
}
 
/* Return the CRC of the bytes buf[0..len-1]. */
unsigned long CUtilityParserDlg::crc(unsigned char *buf, int len)
{
   return update_crc(0xffffffffL, buf, len) ^ 0xffffffffL;
}
 
/* Update a running CRC with the bytes buf[0..len-1]--the CRC
should be initialized to all 1's, and the transmitted value
is the 1's complement of the final running CRC (see the
crc() routine below)). */
unsigned long CUtilityParserDlg::update_crc(unsigned long crc, unsigned char *buf,
                   int len)
{
   unsigned long c = crc;
   int n;
 
   if (!crc_table_computed)
   {
      make_crc_table();
   }
 
   for (n = 0; n < len; n++)
   {
      c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
   }
   return c;
}
 
/* Make the table for a fast CRC. */
void CUtilityParserDlg::make_crc_table()
{
   unsigned long c;
   int n, k;
 
   for (n = 0; n < 256; n++)
   {
      c = (unsigned long) n;
      for (k = 0; k < 8; k++)
      {
         if (c & 1)
         {
            c = 0xedb88320L ^ (c >> 1);
         }
         else
         {
            c = c >> 1;
         }
      }
      crc_table[n] = c;
   }
   crc_table_computed = 1;
}

Open in new window

Avatar of jkr
jkr
Flag of Germany image

Isn't that a little complicated way to read a DWORD? Why nor just
               // Read CRC
                  i += 4;
                  DWORD dwFileCRC =*((DWORD*) &pBufFile[i + dwDataLength]);

Open in new window

One other thing - did you check the endianity? If the CRC is storedas big endian, yo need to convert it, e.g.


#include <winsock2.h>
 
//...
 
dwFileCRC = ntohl(dwFileCRC);

Open in new window

your error is here:

DWORD dwTemp = crc(pData, file.GetLength());

Replace with:

DWORD dwTemp = crc(pData, dwDataLength + 4);

The +4 is because you have to get the chunk type (4 bytes) along with the chunk data.

I got my info from the w3c specification: http://www.w3.org/TR/PNG/
ASKER CERTIFIED SOLUTION
Avatar of Jonez176
Jonez176
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Just so you don't have to go searching for it, I'll pick out the informative part and paste them here...  You'll see that the length is just the size of the chunk data, and the CRC is calculated on both the chunk data AND the chunk type.

PNG data format:
| Length | Chunk Type | Chunk Data | CRC |

Length: A four-byte unsigned integer giving the number of bytes in the chunk's data field. The length counts only the data field, not itself, the chunk type, or the CRC.

Chunk Type: A sequence of four bytes defining the chunk type.

Chunk Data: The data bytes appropriate to the chunk type, if any. This field can be of zero length.

CRC: A four-byte CRC (Cyclic Redundancy Code) calculated on the preceding bytes in the chunk, including the chunk type field and chunk data fields, but not including the length field. The CRC can be used to check for corruption of the data. The CRC is always present, even for chunks containing no data.
Avatar of TelDig

ASKER

Thank you very much, that was exactly my problem!