user2790105
user2790105

Reputation: 3

C recovering image from SD logic

I'm trying to recover data from an SD card FAT32 knowing that when I find a .jpg there, it will be there in order till the end of file. Right now the program is able to recover 1 jpeg but stops. How can I improve it, or can someone point me the logic flaw?

Also at: http://pastebin.com/Rak6L5Z0

EDIT: card file http://www.mediafire.com/?et8pvhvacpy9kv1

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

 typedef uint8_t BYTE;

 #define BLOCKSIZE 512

 int main(void)
 {
    // define 512 byte block
    BYTE block[BLOCKSIZE];

    // open memory card
    FILE* fp = fopen("card.raw", "r");

    // check if file opened
    if (fp == NULL)
    {
        printf("Error opening file\n");
        return 1;
    }
    // open output file
    FILE* outfile;
    outfile = NULL;
    int num = 0;
    char filename[14];
    int x = 0;
    int y = 1;

    // main block till end of file
    while (y == 1)
    {
        if ( x == 0)
        {
        // read every 512 block
        fread(&block, sizeof(block), 1 , fp);
        }

        // if 4 first characters jpg
        if ((block[0] == 0xff && block[1] == 0xd8 && block[2] == 0xff && block[3] == 0xe0) || (block[0] == 0xff && block[1] == 0xd8 && block[2] == 0xff && block[3] == 0xe1))
        {
            sprintf(filename, "%03d_output.jpg", num);
            outfile = fopen(filename, "a");
            fwrite(&block, sizeof(block), 1, outfile);
            //fseek(fp, sizeof(block), SEEK_CUR);

            fread(&block, sizeof(block), 1 , fp);
            while (!((block[0] == 0xff && block[1] == 0xd8 && block[2] == 0xff && block[3] == 0xe0) || (block[0] == 0xff && block[1] == 0xd8 && block[2] == 0xff && block[3] == 0xe1)))
            {
                fwrite(&block, sizeof(block), 1, outfile);
                y = fread(&block, sizeof(block), 1, fp);
            }
            //fseek(fp, -(sizeof(block)), SEEK_CUR);
            fclose(outfile);
            num++;
            x = 1;
            //y = fread(&block, sizeof(block), 1, fp);
        }
        //printf("%s\n", block);

    }

    // close memory card
    fclose(fp);

    return 0;
 }

Upvotes: 0

Views: 935

Answers (1)

Jonathan Leffler
Jonathan Leffler

Reputation: 754810

One problem is the outer loop:

while (y == 512)
{
    ...lots of code...
    y = fread(&block, sizeof(block), 1, fp);
    ...some code...
}

The fread() sets y to 0 or 1, so it is no longer 512, so the loop terminates.

I think there is also a problem with the if (x == 0) followed by x = 1;, but I'm not sure I understand the logic behind that part of the code. Certainly, x is not reset to zero after the first file is read — if that matters.


This extracts files 000..050 from the sample data, and file describes them all as one of these three types:

  • JPEG image data, EXIF standard
  • JPEG image data, JFIF standard 1.01
  • JPEG image data, JFIF standard 1.02

Working code

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

typedef uint8_t BYTE;

#define BLOCKSIZE 512

static inline int is_jpeg_file_header(const BYTE *block)
{
    if (block[0] == 0xff && block[1] == 0xd8 && block[2] == 0xff &&
            (block[3] == 0xe0 || block[3] == 0xe1))
        return 1;
    return 0;
}

int main(void)
{
    BYTE block[BLOCKSIZE];
    const char file[] = "card.raw";

    FILE* fp = fopen(file, "r");
    if (fp == NULL)
    {
        fprintf(stderr, "Error opening file %s for reading\n", file);
        return 1;
    }

    int num = 0;

    while (fread(&block, sizeof(block), 1, fp) == 1)
    {
        if (is_jpeg_file_header(block))
        {
            char filename[20];
            snprintf(filename, sizeof(filename), "%03d_output.jpg", num);
            FILE* outfile = fopen(filename, "a");
            if (outfile == 0)
            {
                fprintf(stderr, "Error opening file %s for writing\n", filename);
                return 1;
            }
            printf("%s\n", filename);

            fwrite(&block, sizeof(block), 1, outfile);
            while (fread(&block, sizeof(block), 1 , fp) == 1 &&
                    !is_jpeg_file_header(block))
            {
                fwrite(&block, sizeof(block), 1, outfile);
            }
            fclose(outfile);
            num++;
            fseek(fp, -BLOCKSIZE, SEEK_CUR);
        }
    }

    fclose(fp);

    return 0;
}

This uses the fact that we're reading a disk image to seek back one block when you read the header of the next file. It is simpler than continuing the code.

The first 11 recovered images are all displayable in a web browser.

Upvotes: 1

Related Questions