Andy
Andy

Reputation: 275

Matching JPEG signature

I am trying to scan a file looking for 1MB JPEGs that would be stored contiguously. My approach is create a structure to match the first 4 bytes with the JPEG signature and, if true, write the entire 512 buffer to a named file until I find another jpeg signature then I create a new file. The code below creates 2 files, neither of which are readable as the first few bytes are not part of the jpeg signature. Any ideas where I went wrong? I tried variations of my if statement where I test for the signature but no luck thus far.

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

typedef uint8_t  BYTE;

typedef struct
{
    BYTE first;
    BYTE second;
    BYTE third;
    BYTE fourth;
}
JPGTEST;

int main(int argc, char* argv[])
{

    FILE* inptr = fopen("card.raw", "r");

    if (inptr == NULL)

    {

        printf("Could not open file\n");

        return 2;

    }

    FILE* outptr;

    //initialize jpeg count and variable for filename
    int count = 0;
    char name[8];

    //allocate memory
    char buffer[512];
    JPGTEST myjpg;
    int is_open = 0;

    while (fread(&buffer, 512, 1, inptr) != 0)
    {

        //test first 4 bytes to see if jpeg
        fread(&myjpg, sizeof(JPGTEST), 1, inptr);

        //if match, name and write to file

        if (myjpg.first == 0xff && myjpg.second == 0xd8 && myjpg.third == 0xff && (myjpg.fourth == 0xe0 || myjpg.fourth == 0xe1))
        {
            sprintf(name, "%03d.jpg", count);
            if (is_open == 0)
            {          
                outptr = fopen(name, "w");
                fwrite(buffer, sizeof(buffer),1,outptr);
                is_open = 1;
            }
            if (is_open == 1)
            {          
                fclose(outptr);
                outptr = fopen(name, "w");
                fwrite(buffer, sizeof(buffer),1,outptr);
                count++;
            }  

        }
        else
        {
            if (is_open == 1)
            {
                fwrite(buffer, sizeof(buffer),1,outptr);
            }
        }
    }
    fclose(inptr);
    fclose(outptr);
    return 0;
}

Upvotes: 0

Views: 1300

Answers (4)

Mansoor Ahmed Memon
Mansoor Ahmed Memon

Reputation: 121

You can do something like the following to simplify your signature comparisons:

#include ...

#define JPEG_SIGNATURE 0xFFD8

void
reverse_bytes (void const *data, long unsigned size)
{
  char *ptr = (char *)data;
  int i = 0;
  while (i < size / 2)
    {
      char temp = ptr[i];
      ptr[i] = ptr[size - i - 1];
      ptr[size - i - 1] = temp;
      i++;
    }
}

int
main()
{
  FILE *fptr = fopen("path/to/image.jpeg", "rb");

  short bytes = 0;
  fread(&bytes, sizeof(char), sizeof(bytes), fptr);

  reverse_bytes(bytes, sizeof(bytes)); // Refer to [1]

  switch (bytes)
  {
    case JPEG_SIGNATURE: 
      printf("JPEG image!");
      break;
    default:
      printf("Unknown format!");
      break;
  }

  return 0;
}

This can be extended to many different formats by adding more cases in the switch statement and a little more work.

This is indeed not a full or proper answer but, I hope it is helpful for others who come through this post!

NOTE: I omitted things like exception handling for brevity!

References:

  1. Why is Fread reading unsigned-int in reverse order?

Upvotes: 0

Remy Lebeau
Remy Lebeau

Reputation: 598448

You are opening the files in text mode. You need to open them in binary mode instead:

FILE* inptr = fopen("card.raw", "rb");

outptr = fopen(name, "wb");

Aside from that, you are calling fread() too many times, and just generally not managing the files correctly.

Try something more this instead:

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

typedef uint8_t  BYTE;

#pragma pack(push, 1)
typedef struct
{
    BYTE first;
    BYTE second;
    BYTE third;
    BYTE fourth;
}
JPGTEST;
#pragma pack(pop)

int main(int argc, char* argv[])
{
    FILE* inptr = fopen("card.raw", "rb");
    if (inptr == NULL)
    {
        printf("Could not open file\n");
        return 2;
    }

    FILE* outptr = NULL;

    //initialize jpeg count and variable for filename
    int count = 0;
    char name[8];

    //allocate memory
    char buffer[512];
    JPGTEST myjpg;

    while (fread(buffer, sizeof(buffer), 1, inptr) > 0)
    {
        //test first 4 bytes to see if jpeg
        memcpy(&myjpg, buffer, sizeof(JPGTEST));

        //if match, name and write to file
        if ((myjpg.first == 0xff) && (myjpg.second == 0xd8) && (myjpg.third == 0xff) && ((myjpg.fourth == 0xe0) || (myjpg.fourth == 0xe1)))
        {
            if (outptr != NULL)
            {          
                fclose(outptr);
                outptr = NULL;
            }

            ++count;
            sprintf(name, "%03d.jpg", count);

            outptr = fopen(name, "wb");
        }

        if (outptr != NULL)
            fwrite(buffer, sizeof(buffer), 1, outptr);
    }

    fclose(inptr);

    if (outptr != NULL)
        fclose(outptr);

    return 0;
}

Upvotes: 2

David C. Rankin
David C. Rankin

Reputation: 84652

The problem is you are not resetting the file position indicator after you read into buffer, to do so use, fseek. I.e.:

    //test first 4 bytes to see if jpeg
    fseek (inptr, SEEK_SET, 0);
    fread(&myjpg, sizeof(JPGTEST), 1, inptr);

That will provide you with a test of the jpg header in myjpg:

sizeof (myjpg): 4
first: ff  second: d8  third ff  fourth:  e0

However, that will also cause logic issues you will have to rework. It is better to simply read the values from buffer as suggested by the other answer.

Upvotes: 0

mSatyam
mSatyam

Reputation: 541

Replace your second fread inside the loop with the below:

        memcpy((void *)&myjpg, (void *) buffer, sizeof(JPGTEST));

And include string.h header file in your code for memcpy function.

What you are doing wrong is that after reading first 512 you are not making any use of them rather you are again reading 4 bytes without checking first 4 bytes of previously read 512 bytes.

Upvotes: 1

Related Questions