/*
 * calculate bytes needed to get from crc value <from> to <to>
 *
 * fix crc to any desired value
 *
 * by W.J.Hengeveld  sep1995
 *   itsme@xs4all.nl
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>


// crc0 = from
// crc1 = crc0>>8 ^ tab[crc0.l^byte0]
// crc2 = crc1>>8 ^ tab[crc1.l^byte1]
// crc3 = crc2>>8 ^ tab[crc2.l^byte2]
// crc4 = crc3>>8 ^ tab[crc3.l^byte3]
// ...
// to = crc4
//                                                 known
// crc4 = to                                       bits 1f-00
// crc3 = crc4<<8 ^ invtab[crc4.h] ^ byte3         bits 1f-08
// crc2 = crc3<<8 ^ invtab[crc3.h] ^ byte2         bits 1f-10
// crc1 = crc2<<8 ^ invtab[crc2.h] ^ byte1         bits 1f-18
// crc0 = crc1<<8 ^ invtab[crc1.h] ^ byte0
// from = crc0
//
*/
typedef unsigned long ulong;
typedef unsigned char  byte;
typedef unsigned short word;

#define isoff(flags, flag)    (((flags)&(flag))==0)
#define ison(flags, flag)     (((flags)&(flag))!=0)
#define isallon(flags, flag)     (((flags)&(flag))==(flag))

#define on(flags, flag)       ((flags)|=(flag))
#define off(flags, flag)      ((flags)&=~(flag))
#define toggle(flags, flag)   ((flags)^=(flag))

#define bit(n)                (1<<(n))

#define ZERO_ARRAY(a)         memset((a), 0, sizeof(a))
#define ARRAY_SIZE(a)         (sizeof(a)/sizeof((a)[0]))

int verbose=0;

ulong crc32tab[256];
ulong invcrc32tab[256];

/* crcBase = 0xEDB88320L */
ulong calccrc32tab(byte c, ulong crcBase)
{
   ulong val = c;
   int i;
   for (i=0 ; i<8 ; i++)
   {
      if (val&1)
        val = (val>>1)^crcBase;
      else
        val = (val>>1);
   }
   return val;
}

void initcrc(ulong crcBase)
{
   ulong val;
   int i;
   for (i=0 ; i<256 ; i++)
   {
      val = calccrc32tab(i, crcBase);
      crc32tab[i]=val;
      invcrc32tab[(int)(val>>24)] = (val<<8) ^ i;
   }
}

ulong crc32(ulong prev, byte c)
{
   return (prev>>8) ^ crc32tab[(int)((prev&0xff)^c)];
}

ulong invcrc32(ulong crc, byte c)
{
   return (crc<<8)^invcrc32tab[(byte)(crc>>24)]^c;
}

/* calculate CRC at <targetOffset>, given crcStart at <startOffset>
// for file <f>
*/
ulong fileCRC(FILE *f, ulong crcStart, long startOffset, long targetOffset)
{
   long offset;
   int   c;
   if (startOffset<targetOffset)
   {
      fseek(f, startOffset, SEEK_SET);
      for (offset = startOffset ; offset<targetOffset ; offset++)
      {
         c=fgetc(f);
         if (c==EOF) break;
         crcStart=crc32(crcStart, c);
      }
   }
   else if (startOffset>targetOffset)
   {
      fseek(f, startOffset-1, SEEK_SET);
      for (offset = startOffset ; offset>targetOffset ; offset--)
      {
         c=fgetc(f);  fseek(f, -2, SEEK_CUR);
         crcStart=invcrc32(crcStart, c);
      }
   }
   return crcStart;
}

void calcBytes(ulong crcStart, ulong crcTarget, byte *byteList)
{
   ulong crcList[5];
   ulong val;

   crcList[0]=crcStart;
   crcList[4]=crcTarget;
   crcList[3]=invcrc32(crcList[4], 0)&0xffffff00L;
   crcList[2]=invcrc32(crcList[3], 0)&0xffff0000L;
   crcList[1]=invcrc32(crcList[2], 0)&0xff000000L;

   val=crcList[0] ^ invcrc32tab[(byte)(crcList[1]>>24)];
   byteList[0]=val&0xff;
   crcList[1]|=val>>8;

   val=crcList[1] ^ invcrc32tab[(byte)(crcList[2]>>24)];
   byteList[1]=val&0xff;
   crcList[2]|=val>>8;

   val=crcList[2] ^ invcrc32tab[(byte)(crcList[3]>>24)];
   byteList[2]=val&0xff;
   crcList[3]|=val>>8;

   val=crcList[3] ^ invcrc32tab[(byte)(crcList[4]>>24)];
   byteList[3]=val&0xff;
}

void usage(void)
{
   printf("Usage: crc [options]\n");
   printf("   -f : specify file\n");
   printf("   -b : CRC polynomial (default=0xEDB88320 - used my McAfee's VALIDATE)\n");
   printf("   -o : offset in file (default=filelength-4)\n");
   printf("   -s : starting CRC (default=0xffffffff)\n");
   printf("   -t : target CRC (default=CRC of file)\n");
   printf(" example:\n");
   printf("    crc -fcommand.com -oc600 -tdeadd0d0\n");
   printf("will calculate the bytes you'd have to enter at position 0xc600\n");
   printf("to make command.com have a CRC of 0xDEADD0D0\n");
   printf("(0xc600 is somewhere in microsofts copyright notice)\n");
   printf("\nuse crc -ffile -v  to see what the current crc is\n");
}

#define PRM_CRCSTART    bit(0)
#define PRM_CRCTARGET   bit(1)

int main(int argc, char **argv)
{
   ulong crcBase=0xEDB88320L;
   ulong crcStart=0xffffffffL;
   ulong crcTarget;
   byte  byteList[4];
   ulong parmFlags=0;
   int   i;
   char *fileName=NULL;
   long offset=-1L;
   FILE *f;
   ulong fileLength;
   ulong origCRC;    /* original CRC of file */

   for (i=1 ; i<argc ; i++)
   {
      if (argv[i][0]=='-')
         switch(argv[i][1])
         {
            case 'f':  fileName=argv[i]+2;                 break;
            case 'b':  crcBase=strtoul(argv[i]+2, 0, 16);  break;
            case 'o':  offset=strtoul(argv[i]+2, 0, 16);   break;
            case 's':  crcStart=strtoul(argv[i]+2, 0, 16); on(parmFlags, PRM_CRCSTART); break;
            case 't':  crcTarget=strtoul(argv[i]+2, 0, 16);on(parmFlags, PRM_CRCTARGET); break;
            case 'v':  verbose++;  break;
            case '?':
            case 'h':
               usage();
               return 0;
            default:
               printf("Invalid Commandline parameter: %s\n", argv[i]);
               return 1;
         }
      else
      {
         printf("Invalid Commandline parameter: %s\n", argv[i]);
         return 1;
      }
   }
   initcrc(crcBase);

   if (isoff(parmFlags, PRM_CRCSTART))
      crcStart=0xffffffffL;

   if (fileName)
   {
      f=fopen(fileName, "rb");
      fseek(f, 0, SEEK_END);
      fileLength=ftell(f);
      if (offset<0)
         offset=fileLength-4;
      crcStart=fileCRC(f, crcStart, 0, offset);
      on(parmFlags, PRM_CRCSTART);

      if (ison(parmFlags, PRM_CRCTARGET))
         origCRC=crcTarget;
      else
         origCRC=fileCRC(f, crcStart, offset, fileLength);

      crcTarget=fileCRC(f, origCRC, fileLength, offset+4);
      on(parmFlags, PRM_CRCTARGET);

      if (verbose)
         printf("fileLen=%08lx crcStart=%08lx crcTarget=%08lx origCRC=%08lx\n",
            fileLength, crcStart, crcTarget, origCRC);

      fclose(f);
   }
   else if (offset>=0)
   {
      printf("Offset specified without filename!\n");
      return 1;
   }

   if (!isallon(parmFlags, PRM_CRCSTART|PRM_CRCTARGET))
   {
      printf("Need starting & target CRC values\n");
      return 1;
   }

   calcBytes(crcStart, crcTarget, byteList);

   for (i=0 ; i<4 ; i++)
     printf("%02x ", byteList[i]);
   putchar('\n');
   return 0;
}

