Patrick Lorio
Patrick Lorio

Reputation: 5668

c get data from BMP

I find myself writing a simple program to extract data from a bmp file. I just got started and I am at one of those WTF moments.

When I run the program and supply this image: http://www.hack4fun.org/h4f/sites/default/files/bindump/lena.bmp

I get the output:

type: 19778
size: 12
res1: 0
res2: 54
offset: 2621440

The actual image size is 786,486 bytes. Why is my code reporting 12 bytes?

The header format specified in, http://en.wikipedia.org/wiki/BMP_file_format matches my BMP_FILE_HEADER structure. So why is it getting filled with wrong information?

The image file doesn't appear to be corrupt and other images are giving equally wrong outputs. What am I missing?

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

typedef struct {
    unsigned short type;
    unsigned int size;
    unsigned short res1;
    unsigned short res2;
    unsigned int offset;
} BMP_FILE_HEADER;

int main (int args, char ** argv) {
    char *file_name = argv[1];

    FILE *fp = fopen(file_name, "rb");

    BMP_FILE_HEADER file_header;

    fread(&file_header, sizeof(BMP_FILE_HEADER), 1, fp);

    if (file_header.type != 'MB') {
        printf("ERROR: not a .bmp");
        return 1;
    }

    printf("type: %i\nsize: %i\nres1: %i\nres2: %i\noffset: %i\n", file_header.type, file_header.size, file_header.res1, file_header.res2, file_header.offset);
    fclose(fp);

    return 0;
}

Upvotes: 2

Views: 2789

Answers (2)

Toribio
Toribio

Reputation: 4078

There are two mistakes I could find in your code.

First mistake: You have to pack the structure to 1, so every type size is exactly the size its meant to be, so the compiler doesn't align it for example in 4 bytes alignment. So in your code, short, instead of being 2 bytes, it was 4 bytes. The trick for this, is using a compiler directive for packing the nearest struct:

#pragma pack(1)

typedef struct {
    unsigned short type;
    unsigned int size;
    unsigned short res1;
    unsigned short res2;
    unsigned int offset;
} BMP_FILE_HEADER;

Now it should be aligned properly.

The other mistake is in here:

if (file_header.type != 'MB')

You are trying to check a short type, which is 2 bytes, with a char type (using ''), which is 1 byte. Probably the compiler is giving you a warning about that, it's canonical that single quotes contain just 1 character with 1-byte size.

To get this around, you can divide this 2 bytes into 2 1-byte characters, which are known (M and B), and put them together into a word. For example:

if (file_header.type != (('M' << 8) | 'B'))

If you see this expression, this will happen:

'M' (which is 0x4D in ASCII) shifted 8 bits to the left, will result in 0x4D00, now you can just add or or the next character to the right zeroes: 0x4D00 | 0x42 = 0x4D42 (where 0x42 is 'B' in ASCII). Thinking like this, you could just write:

if (file_header.type != 0x4D42)

Then your code should work.

Upvotes: 1

wallyk
wallyk

Reputation: 57774

Here the header in hex:

0000000 42 4d 36 00 0c 00 00 00 00 00 36 00 00 00 28 00
0000020 00 00 00 02 00 00 00 02 00 00 01 00 18 00 00 00

The length field is the bytes 36 00 0c 00`, which is in intel order; handled as a 32-bit value, it is 0x000c0036 or decimal 786,486 (which matches the saved file size).

Probably your C compiler is aligning each field to a 32-bit boundary. Enable a pack structure option, pragma, or directive.

Upvotes: 2

Related Questions