don't click here

Help with SZDD decompression

Discussion in 'Technical Discussion' started by BenoitRen, Dec 11, 2024.

  1. BenoitRen

    BenoitRen

    Tech Member
    848
    440
    63
    I've coded an SZDD decompression routine based on this pseudo-code. Testing it on one of Sonic CD's files gets me a segmentation fault. Next, I've tried to look up more information and compared it to other implementations. I've found one omission in the pseudo-code I took as a base. However, even after fixing that, I still get a segmentation fault.

    At this point, I'm out of ideas. What the heck is wrong with this code?
    Code (C):
    1. unsigned char* szdd_decompress(char* p_filename) {
    2.   unsigned char* p_bytes = 0;
    3.   unsigned int bytecnt = 0;
    4.   unsigned char cntbytes[4];
    5.   unsigned int bytepos = 0;
    6.   unsigned char dictionary[4096];
    7.   unsigned int pos = 4096 - 16;
    8.   FILE* fp = 0;
    9.  
    10.   fp = fopen(p_filename, "rb");
    11.   if (fp == 0) return 0;
    12.  
    13.   if (fgetc(fp) != 'S') return 0; // magic
    14.   if (fgetc(fp) != 'Z') return 0;
    15.   if (fgetc(fp) != 'D') return 0;
    16.   if (fgetc(fp) != 'D') return 0;
    17.   if (fgetc(fp) != 0x88) return 0;
    18.   if (fgetc(fp) != 0xF0) return 0;
    19.   if (fgetc(fp) != 0x27) return 0;
    20.   if (fgetc(fp) != 0x33) return 0;
    21.   if (fgetc(fp) != 'A') return 0; // mode
    22.   fgetc(fp); // last character of filename
    23.  
    24.   fread(cntbytes, 1, 4, fp);
    25.   bytecnt = read_uint_littleendian(cntbytes);
    26.   p_bytes = (unsigned char*)malloc(bytecnt);
    27.   memset(dictionary, ' ', sizeof(dictionary));
    28.  
    29.   do {
    30.     unsigned int control = fgetc(fp);
    31.     if (feof(fp)) break;
    32.  
    33.     for (unsigned int bit = 1; bit & 0xFF; bit <<= 1) {
    34.       if (control & bit) {
    35.         p_bytes[bytepos] = dictionary[pos] = fgetc(fp);
    36.         ++pos;
    37.         pos %= 4096;
    38.         ++bytepos;
    39.       }
    40.       else {
    41.         unsigned int matchpos = fgetc(fp);
    42.         unsigned int matchlen = fgetc(fp);
    43.         matchpos |= (matchlen & 0xF0) << 4;
    44.         matchlen = (matchlen & 0x0F) + 3;
    45.  
    46.         while (matchlen-- != 0) {
    47.           p_bytes[bytepos] = dictionary[pos] = dictionary[matchpos];
    48.           ++pos;
    49.           pos %= 4096;
    50.           ++matchpos;
    51.           matchpos %= 4096;
    52.           ++bytepos;
    53.         }
    54.       }
    55.     }
    56.   }
    57.   while (1);
    58.  
    59.   fclose(fp);
    60.  
    61.   return p_bytes;
    62. }
     
  2. Devon

    Devon

    La mer va embrassé moi et délivré moi lakay. Tech Member
    1,485
    1,807
    93
    your mom
    Add a feof() check after the other fgetc()'s
    Code (C):
    1.             if (control & bit) {
    2.                 p_bytes[bytepos] = dictionary[pos] = fgetc(fp);
    3.                 if (feof(fp)) break;    // ADD THIS
    4.                 ++pos;
    5.                 pos %= 4096;
    6.                 ++bytepos;
    7.             }
    8.             else {
    9.                 unsigned int matchpos = fgetc(fp);
    10.                 unsigned int matchlen = fgetc(fp);
    11.                 if (feof(fp)) break;    // ADD THIS
    12.                 matchpos |= (matchlen & 0xF0) << 4;
    13.                 matchlen = (matchlen & 0x0F) + 3;

    Basically, the final set of data can end before "bit" overflows back to 0. The breaks work with the for loop, because they'll break out of it, and then go back to the top of the do/while loop, which does another feof() check and breaks out of that.
     
    • Useful Useful x 3
    • Agree Agree x 1
    • List