-5

This is an exported function in a CRC.dll used by our company's main application. I need to replicate it's incorrect calculation of checksums in our new application written in C#, in order to maintain backward compatibility.

It's all way above my head, but I know there are quite a few very intelligent people on Stackoverflow so I thought I'd try my luck. What's wrong with it? How can I replicate it in C#?

  CRC_16_TABLE_1 : array[0..255] of Byte =
   (
    $00, $80, $80, $00, $80, $00, $00, $80, $80, $00, $00, $80, $00, $80, $80, $00,
    $80, $00, $00, $80, $00, $80, $80, $00, $00, $80, $80, $00, $80, $00, $00, $80,
    $80, $00, $00, $80, $00, $80, $80, $00, $00, $80, $80, $00, $80, $00, $00, $80,
    $00, $80, $80, $00, $80, $00, $00, $80, $80, $00, $00, $80, $00, $80, $80, $00,
    $81, $01, $01, $81, $01, $81, $81, $01, $01, $81, $81, $01, $81, $01, $01, $81,
    $01, $81, $81, $01, $81, $01, $01, $81, $81, $01, $01, $81, $01, $81, $81, $01,
    $01, $81, $81, $01, $81, $01, $01, $81, $81, $01, $01, $81, $01, $81, $81, $01,
    $81, $01, $01, $81, $01, $81, $81, $01, $01, $81, $81, $01, $81, $01, $01, $81,
    $83, $03, $03, $83, $03, $83, $83, $03, $03, $83, $83, $03, $83, $03, $03, $83,
    $03, $83, $83, $03, $83, $03, $03, $83, $83, $03, $03, $83, $03, $83, $83, $03,
    $03, $83, $83, $03, $83, $03, $03, $83, $83, $03, $03, $83, $03, $83, $83, $03,
    $83, $03, $03, $83, $03, $83, $83, $03, $03, $83, $83, $03, $83, $03, $03, $83,
    $02, $82, $82, $02, $82, $02, $02, $82, $82, $02, $02, $82, $02, $82, $82, $02,
    $82, $02, $02, $82, $02, $82, $82, $02, $02, $82, $82, $02, $82, $02, $02, $82,
    $82, $02, $02, $82, $02, $82, $82, $02, $02, $82, $82, $02, $82, $02, $02, $82,
    $02, $82, $82, $02, $82, $02, $02, $82, $82, $02, $02, $82, $02, $82, $82, $02
   );

  CRC_16_TABLE_2 : array[0..255] of Byte =
   (
    $00, $05, $0f, $0a, $1b, $1e, $14, $11, $33, $36, $3c, $39, $28, $2d, $27, $22,
    $63, $66, $6c, $69, $78, $7d, $77, $72, $50, $55, $5f, $5a, $4b, $4e, $44, $41,
    $c3, $c6, $cc, $c9, $d8, $dd, $d7, $d2, $f0, $f5, $ff, $fa, $eb, $ee, $e4, $e1,
    $a0, $a5, $af, $aa, $bb, $be, $b4, $b1, $93, $96, $9c, $99, $88, $8d, $87, $82,
    $83, $86, $8c, $89, $98, $9d, $97, $92, $b0, $b5, $bf, $ba, $ab, $ae, $a4, $a1,
    $e0, $e5, $ef, $ea, $fb, $fe, $f4, $f1, $d3, $d6, $dc, $d9, $c8, $cd, $c7, $c2,
    $40, $45, $4f, $4a, $5b, $5e, $54, $51, $73, $76, $7c, $79, $68, $6d, $67, $62,
    $23, $26, $2c, $29, $38, $3d, $37, $32, $10, $15, $1f, $1a, $0b, $0e, $04, $01,
    $03, $06, $0c, $09, $18, $1d, $17, $12, $30, $35, $3f, $3a, $2b, $2e, $24, $21,
    $60, $65, $6f, $6a, $7b, $7e, $74, $71, $53, $56, $5c, $59, $48, $4d, $47, $42,
    $c0, $c5, $cf, $ca, $db, $de, $d4, $d1, $f3, $f6, $fc, $f9, $e8, $ed, $e7, $e2,
    $a3, $a6, $ac, $a9, $b8, $bd, $b7, $b2, $90, $95, $9f, $9a, $8b, $8e, $84, $81,
    $80, $85, $8f, $8a, $9b, $9e, $94, $91, $b3, $b6, $bc, $b9, $a8, $ad, $a7, $a2,
    $e3, $e6, $ec, $e9, $f8, $fd, $f7, $f2, $d0, $d5, $df, $da, $cb, $ce, $c4, $c1,
    $43, $46, $4c, $49, $58, $5d, $57, $52, $70, $75, $7f, $7a, $6b, $6e, $64, $61,
    $20, $25, $2f, $2a, $3b, $3e, $34, $31, $13, $16, $1c, $19, $08, $0d, $07, $02
   );

function CRC_16( var buf : array of Byte; Length : Word; Flag : Byte )
         : Word; export;
var
  i          : Word;
  a1, a2, a3 : Byte;
begin
  a1 := 0;
  a2 := 0;
  if (Flag = CRC_MAKE) then
    begin
    buf[Length-2] := 0;
    buf[Length-1] := 0;
    end;
  for i := 0 to Length-1 do
    begin
    a3 := a1;
    a1 := CRC_16_TABLE_1[a3] xor a2;
    a2 := CRC_16_TABLE_2[a3] xor Buf[i];
    end;
  if (Flag = CRC_MAKE) then
    begin
    buf[Length-2] := a1;
    buf[Length-1] := a2;
    end;
  result := a1 * 256 + a2;
end;
NoPyGod
  • 4,905
  • 3
  • 44
  • 72
  • What's your question? Can't you just copy that code to your new application? – Greg Hewgill Feb 14 '12 at 04:21
  • New application is written in C# – NoPyGod Feb 14 '12 at 04:21
  • 2
    So are you looking for somebody to convert the code for you, or are you actually interested in why that Delphi implementation doesn't match your expectations? Do you have a test suite? – Greg Hewgill Feb 14 '12 at 04:24
  • Both. I'd love to know why it doesn't work as expected. More importantly though, I need to copy the bug in C# so I can move the project forward. – NoPyGod Feb 14 '12 at 04:26
  • My test suite consists of a Delphi application making calls to the DLL heh :) – NoPyGod Feb 14 '12 at 04:27
  • Downvoted because all "help! please rewrite teh codez" questions seem lazy to me. – Warren P Feb 14 '12 at 16:19
  • Didn't ask for it to be rewritten, asked "Does anyone know what's wrong with this Delphi CRC16 algorithm?". Thought somebody who has done this before might look at the code and recognize what's wrong with it. If someone wanted to rewrite it for me in C# I wouldn't have complained. – NoPyGod Feb 14 '12 at 19:45

2 Answers2

3

Here is a C# version of your Delphi code:

static int[] CRC_16_TABLE_1 = {
    0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0x00,
    0x80, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x80,
    0x80, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x80,
    0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0x00,
    0x81, 0x01, 0x01, 0x81, 0x01, 0x81, 0x81, 0x01, 0x01, 0x81, 0x81, 0x01, 0x81, 0x01, 0x01, 0x81,
    0x01, 0x81, 0x81, 0x01, 0x81, 0x01, 0x01, 0x81, 0x81, 0x01, 0x01, 0x81, 0x01, 0x81, 0x81, 0x01,
    0x01, 0x81, 0x81, 0x01, 0x81, 0x01, 0x01, 0x81, 0x81, 0x01, 0x01, 0x81, 0x01, 0x81, 0x81, 0x01,
    0x81, 0x01, 0x01, 0x81, 0x01, 0x81, 0x81, 0x01, 0x01, 0x81, 0x81, 0x01, 0x81, 0x01, 0x01, 0x81,
    0x83, 0x03, 0x03, 0x83, 0x03, 0x83, 0x83, 0x03, 0x03, 0x83, 0x83, 0x03, 0x83, 0x03, 0x03, 0x83,
    0x03, 0x83, 0x83, 0x03, 0x83, 0x03, 0x03, 0x83, 0x83, 0x03, 0x03, 0x83, 0x03, 0x83, 0x83, 0x03,
    0x03, 0x83, 0x83, 0x03, 0x83, 0x03, 0x03, 0x83, 0x83, 0x03, 0x03, 0x83, 0x03, 0x83, 0x83, 0x03,
    0x83, 0x03, 0x03, 0x83, 0x03, 0x83, 0x83, 0x03, 0x03, 0x83, 0x83, 0x03, 0x83, 0x03, 0x03, 0x83,
    0x02, 0x82, 0x82, 0x02, 0x82, 0x02, 0x02, 0x82, 0x82, 0x02, 0x02, 0x82, 0x02, 0x82, 0x82, 0x02,
    0x82, 0x02, 0x02, 0x82, 0x02, 0x82, 0x82, 0x02, 0x02, 0x82, 0x82, 0x02, 0x82, 0x02, 0x02, 0x82,
    0x82, 0x02, 0x02, 0x82, 0x02, 0x82, 0x82, 0x02, 0x02, 0x82, 0x82, 0x02, 0x82, 0x02, 0x02, 0x82,
    0x02, 0x82, 0x82, 0x02, 0x82, 0x02, 0x02, 0x82, 0x82, 0x02, 0x02, 0x82, 0x02, 0x82, 0x82, 0x02
};

static int[] CRC_16_TABLE_2 = {
    0x00, 0x05, 0x0f, 0x0a, 0x1b, 0x1e, 0x14, 0x11, 0x33, 0x36, 0x3c, 0x39, 0x28, 0x2d, 0x27, 0x22,
    0x63, 0x66, 0x6c, 0x69, 0x78, 0x7d, 0x77, 0x72, 0x50, 0x55, 0x5f, 0x5a, 0x4b, 0x4e, 0x44, 0x41,
    0xc3, 0xc6, 0xcc, 0xc9, 0xd8, 0xdd, 0xd7, 0xd2, 0xf0, 0xf5, 0xff, 0xfa, 0xeb, 0xee, 0xe4, 0xe1,
    0xa0, 0xa5, 0xaf, 0xaa, 0xbb, 0xbe, 0xb4, 0xb1, 0x93, 0x96, 0x9c, 0x99, 0x88, 0x8d, 0x87, 0x82,
    0x83, 0x86, 0x8c, 0x89, 0x98, 0x9d, 0x97, 0x92, 0xb0, 0xb5, 0xbf, 0xba, 0xab, 0xae, 0xa4, 0xa1,
    0xe0, 0xe5, 0xef, 0xea, 0xfb, 0xfe, 0xf4, 0xf1, 0xd3, 0xd6, 0xdc, 0xd9, 0xc8, 0xcd, 0xc7, 0xc2,
    0x40, 0x45, 0x4f, 0x4a, 0x5b, 0x5e, 0x54, 0x51, 0x73, 0x76, 0x7c, 0x79, 0x68, 0x6d, 0x67, 0x62,
    0x23, 0x26, 0x2c, 0x29, 0x38, 0x3d, 0x37, 0x32, 0x10, 0x15, 0x1f, 0x1a, 0x0b, 0x0e, 0x04, 0x01,
    0x03, 0x06, 0x0c, 0x09, 0x18, 0x1d, 0x17, 0x12, 0x30, 0x35, 0x3f, 0x3a, 0x2b, 0x2e, 0x24, 0x21,
    0x60, 0x65, 0x6f, 0x6a, 0x7b, 0x7e, 0x74, 0x71, 0x53, 0x56, 0x5c, 0x59, 0x48, 0x4d, 0x47, 0x42,
    0xc0, 0xc5, 0xcf, 0xca, 0xdb, 0xde, 0xd4, 0xd1, 0xf3, 0xf6, 0xfc, 0xf9, 0xe8, 0xed, 0xe7, 0xe2,
    0xa3, 0xa6, 0xac, 0xa9, 0xb8, 0xbd, 0xb7, 0xb2, 0x90, 0x95, 0x9f, 0x9a, 0x8b, 0x8e, 0x84, 0x81,
    0x80, 0x85, 0x8f, 0x8a, 0x9b, 0x9e, 0x94, 0x91, 0xb3, 0xb6, 0xbc, 0xb9, 0xa8, 0xad, 0xa7, 0xa2,
    0xe3, 0xe6, 0xec, 0xe9, 0xf8, 0xfd, 0xf7, 0xf2, 0xd0, 0xd5, 0xdf, 0xda, 0xcb, 0xce, 0xc4, 0xc1,
    0x43, 0x46, 0x4c, 0x49, 0x58, 0x5d, 0x57, 0x52, 0x70, 0x75, 0x7f, 0x7a, 0x6b, 0x6e, 0x64, 0x61,
    0x20, 0x25, 0x2f, 0x2a, 0x3b, 0x3e, 0x34, 0x31, 0x13, 0x16, 0x1c, 0x19, 0x08, 0x0d, 0x07, 0x02
};

static int CRC_16(byte[] buf, int length, bool make)
{
    int a1 = 0;
    int a2 = 0;
    if (make)
    {
        buf[length-2] = 0;
        buf[length-1] = 0;
    }
    for (int i=0; i<length; i++)
    {
        int a3 = a1;
        a1 = (CRC_16_TABLE_1[a3] ^ a2) & 0xff;
        a2 = (CRC_16_TABLE_2[a3] ^ buf[i]) & 0xff;
    }
    if (make)
    {
        buf[length-2] = (byte)a1;
        buf[length-1] = (byte)a2;
    }
    return a1*256+a2;
}

I've used int throughout to reduce the amount of casting that needs to be done.

As for your Delphi code, I fell foul of the fact that your length parameter is a mere 16-bit integer. My test file was >32kb in size and so that confused me for a while. I'd also comment that unsigned types are inappropriate for loop variables. Your variable i being typed as a Word will result in AVs if ever you call the function with a length of 0. Of course, the accesses of buf[Length-1] and buf[Length-2] are also a problem when the length is too small.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Thanks! I had started rewriting it myself also. The thing that strikes me as odd is that you're supposed to call the function and leave two bytes at the end of your buffer for the CRC to be placed in, and yet these two bytes at the end are actually included when calculating the CRC. That surely is an oversight by the original programmer? – NoPyGod Feb 14 '12 at 20:43
  • That's perfectly normal. That's why they are zero initialised at the start so that the CRC is repeatable. – David Heffernan Feb 14 '12 at 20:45
  • Can you explain in layman's terms what it means to make a CRC repeatable? – NoPyGod Feb 14 '12 at 21:06
  • Oh I mean so that when you calculate it in checking mode (as opposed to make mode) then you get the same value as when you were in make mode. If you didn't have the last two bytes zero initialiased, then the CRC function would give a different value in make and check mode. – David Heffernan Feb 14 '12 at 21:08
  • But if I'm calculating a CRC on a string "Testing", then doesn't this CRC function effectively calculate a CRC for "Testing\0\0" instead. Do the zeros at the end not cause the CRC to be different than if it had been calculated on just "Testing".. I hope i'm making sense. – NoPyGod Feb 14 '12 at 21:12
  • the point is that you need two extra bytes to store the crc in. That's so you can detect that the file has been modified. – David Heffernan Feb 14 '12 at 21:15
  • I understand the purpose of having two extra bytes for the CRC, but I don't understand why the for loop runs over those two extra bytes when they do not contain anything. It's like calculating the CRC on "Testing\0\0" when I want to calculate it on "Testing". – NoPyGod Feb 14 '12 at 21:17
  • I see what you mean. I guess it's arbitrary. What problems have you been having with this code? – David Heffernan Feb 14 '12 at 21:19
  • No problems whatsoever. Dropped your code in over the top of what I had written because your casting (or lack of) makes more sense. Works exactly like the original Delphi function. Really appreciate the time you took to do this for me. And yes I think it must be an arbitrary thing with the last two bytes. – NoPyGod Feb 14 '12 at 21:23
  • @NoPyGod: Some CRC algorithms are constructed such that you can take your whole string `"Testing\xab\xcd"` with the calculated CRC *included* as the final two bytes, run the CRC algorithm, and end up with a constant easily testable value such as 0 or -1 as the result (assuming the correct CRC is included). I haven't tested yours to see whether it has that property, but it would be easy for you to check. – Greg Hewgill Feb 16 '12 at 04:47
1

Well, I can't rewrite the thing for you, but its general arrangement is pretty darned obvious... Pascal's not that different from any other language!

Try this: go out to the Internet and find a CRC-16 algorithm already written in C# that works. Then, put the code of the two routines side-by-side. You're looking for a routine that also has two fixed tables of constants, that also loops through them as this routine does, but that's written in C#. Then, gosh... the necessary adjustments ought to be pretty obvious at that point, don't you think?? (I mean, the entire guts of the thing are, like, 25 lines long?) :-}

user106701
  • 175
  • 3