0

I lost a couple of hours struggling to find what might be causing my program to not pass the check, but to reveals all the 50 images from the test set. I even compiled else's passing code just to get the correct test images and compared their hexdumps to mine and they were the same. I'm out of ideas and kindly ask if someone could help me.

My code:

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>

const char FIRST_BYTE  = 0xff;
const char SECOND_BYTE = 0xd8;
const char THIRD_BYTE  = 0xff;
const char FORTH_BYTE_PARTIAL = 0b11100000;
const int FORTH_BYTE_BITS_TO_COMPARE_NO = 4;

typedef struct MemoryBlock {
    char bytes[512];
} MemoryBlock;

bool AreBytesBeginningSame(char first, char second, int numberOfBitsToCompare);
MemoryBlock GetStartingImageMemoryBlock(FILE* pFile);

int main(int argc, char* argv[]) {
    FILE* pRaw = fopen(argv[1], "r");
    bool isOpened = pRaw != NULL;
    if (!isOpened) {
        fprintf(stderr, "Can't open raw file\n");
        return 1;
    }

    FILE* pImage = fopen("000.jpg", "w");
    bool isCreated = pImage != NULL;
    if (!isCreated) {
        fprintf(stderr, "Can't create image file\n");
        return 1;
    }

    MemoryBlock startingBlock = GetStartingImageMemoryBlock(pRaw);
    fwrite(&startingBlock, sizeof(MemoryBlock), 1, pImage);

    int currentImageNo = 0;
    while (true) {
        MemoryBlock currentBlock;
        bool isReadSuccessful = fread(&currentBlock, sizeof(MemoryBlock), 1, pRaw) > 0;

        if (isReadSuccessful) {
            if ((currentBlock.bytes[0] == FIRST_BYTE)  &&
                (currentBlock.bytes[1] == SECOND_BYTE) &&
                (currentBlock.bytes[2] == THIRD_BYTE)  &&
                (AreBytesBeginningSame(currentBlock.bytes[3], FORTH_BYTE_PARTIAL, FORTH_BYTE_BITS_TO_COMPARE_NO))) {

                //printf("found new image at %li\n", ftell(pRaw) - 512);

                fclose(pImage);
                ++currentImageNo;

                char newImageName[10];
                sprintf(newImageName, "%03i.jpg", currentImageNo);

                pImage = fopen(newImageName, "w");
                bool isCreated = pImage != NULL;
                if (!isCreated) {
                    fprintf(stderr, "Can't create image %s\n", newImageName);
                    return 1;
                }
            }

            fwrite(&currentBlock, sizeof(MemoryBlock), 1, pImage);
            continue;
        }
        else {
            fprintf(stderr, feof(pRaw) ? "File end reached!\n" : "File end not reached!\n");
            break;
        }
    }

    fclose(pRaw);
    fclose(pImage);
    return 0;
}

// used to compare bits in a byte (8 bits MAX)
// starts comparing from left side
bool AreBytesBeginningSame(char first, char second, int numberOfBitsToCompare) {
    const unsigned int BYTE_SIZE = 7; // because we start from 0

    bool isOutOfRange = numberOfBitsToCompare < 1 || numberOfBitsToCompare > 8;
    if (isOutOfRange) {
        printf("Bit comparisong error: out of range");
        exit(1);
    }

    char cropByte = 0;
    for (unsigned int i = 0; i < numberOfBitsToCompare; ++i) {
        cropByte |= 1UL << (BYTE_SIZE - i);
    }

    first  &= cropByte;
    // printf("First:  "BYTE_TO_BINARY_PATTERN"\n", BYTE_TO_BINARY(first));
    second &= cropByte;
    // printf("Second: "BYTE_TO_BINARY_PATTERN"\n", BYTE_TO_BINARY(second));

    return first == second;
}

MemoryBlock GetStartingImageMemoryBlock(FILE* pFile) {
    MemoryBlock block;
    do {
        fread(&block, sizeof(MemoryBlock), 1, pFile);
    } while (block.bytes[0] != FIRST_BYTE);

    return block;
}
1

Your code seems to be working with the raw file provided. However, check50 uses a different data file to test with. That file has a block in the leading garbage data that reveals an error in your code.

First, the code above didn't compile because of a couple of minor errors, unrelated to the problem, so I fixed them and ran the code. The problem is that the code isn't verifying that a full signature is found for the first file. It only checks for 0xff in the first byte. If any 512 byte block starts with 0xff, it will be treated as a valid signature. This is exactly what is happening. The result is that the first file is invalid and is an extra file.

If this answers your question, please click on the check mark to accept. Let's keep up on forum maintenance. ;-)

4
  • Oh, so that was it. It's a little bit funny because in a previous version I commented that part as a potential bug, but discarded it as it worked fine with the test data... #life Anyway, thank you very much! – Z0B Nov 26 '17 at 11:38
  • And btw, which errors did you encounter? I'm curious as it compiles fine for me. – Z0B Nov 26 '17 at 11:41
  • Two issues: unused variable argc and redeclaration of isCreated, creating a shadow variable. If you have disabled the flag that treats warnings as errors, the code would compile locally but shouldn't compile in check50. Is it possible that you're working with a newer version of the code? – Cliff B Nov 26 '17 at 22:47
  • Sorry for not responding sooner. For some reason I didn't get a notification. About the errors, it becomes obvious now that you say it, but no (as far as I remember) I didn't get any errors with that version of the code. Not even a warning... And I tried it also with check50 because it failing on the first picture was the reason for asking this question. – Z0B Dec 7 '17 at 0:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .