1

I'm trying to read a TGA header of the file I created in Paint.net. It seems that there is something wrong with it. If I use the header structure from the specification, like this one:

typedef struct {
    CHAR  idlength;
    CHAR  colourmaptype;
    CHAR  datatypecode;
    WORD colourmaporigin;
    WORD colourmaplength;
    CHAR  colourmapdepth;
    WORD x_origin;
    WORD y_origin;
    WORD width;
    WORD height;
    CHAR  bitsperpixel;
    CHAR  imagedescriptor;
} TGAHEADER;

I get this:

Data size: 0
Color Map type: 0
Data Type code: 2
Bits per-pixel: 0
Size: 501 x 2080

Which is wrong, since my image is 501x501, 32-bits per pixel. However, if I comment out two bytes from the structure, f.e. this one colourmaporigin, I get this:

Data size: 0
Color Map type: 0
Data Type code: 2
Bits per-pixel: 32
Size: 501 x 501

Which is correct. I was reading everything I found on this file format. It never says that any of those fields are optional or something.

How come I get results like this?

Here's the code for reading the data:

void Image::readTGA()
{
    TGAHEADER fileHeader;

    std::ifstream fileHandle(fileName, std::ios::binary);
    if (fileHandle.is_open())
    {
        fileHandle.read((char*)(&fileHeader), sizeof(TGAHEADER));
        fileHandle.close();
    }
    else
    {
        std::cout << "An error occured when opening a file." << std::endl;
    }
}

I'm using VS2015, targeting x86 platform.

Makyen
  • 31,849
  • 12
  • 86
  • 121
Bartosz Boczula
  • 349
  • 1
  • 2
  • 9
  • 3
    It's probably a padding issue. Depending on your compiler you can use a specific #pragma that disables padding of structures, but this may be a problem on certain achitectures as unaligned memory access of data that is larger than 1 byte may not work. Tell us which platform you're targeting and which compiler you use. And please read [this](https://stackoverflow.com/questions/5397447/struct-padding-in-c) and [this](https://stackoverflow.com/questions/4306186/structure-padding-and-packing). – Jabberwocky Aug 23 '17 at 15:06
  • I'm using VS2015, targeting x86 platforms. I was thinking of padding, but if you think about that, padding could only make the data end up further, but in this case they end up "earlier", right? In other words, if this was padding issue, the fix would be to ADD some field in the structure, but here i have to REMOVE a field – Bartosz Boczula Aug 23 '17 at 15:09
  • 1
    Please put complementary information into your question by [edit]ing it. Try `#pragma pack(1)` and read [this](https://stackoverflow.com/questions/3318410/pragma-pack-effect) – Jabberwocky Aug 23 '17 at 15:11
  • This totally worked... Thx! In genera, I understand padding, but i don't understand this particular case, padding usually adds "empty" bytes, so the quick solution is to add mockup fields to the structure, but here i had to remove one... Anyway, thx again for help! – Bartosz Boczula Aug 23 '17 at 15:18
  • Look at my answer below. With all that information you should be able to figure out what exactly is going on. – Jabberwocky Aug 23 '17 at 15:22
  • 2
    If you have a structure with padding, and read data into it in a way that does not take padding into account, then data will move earlier. For example, suppose you have a struct containing a char, a short and a char. You think that's 4 bytes, and you read 4 bytes a,b,c,d into it. You expect that d will be in the final char, but since b goes into a padding byte, c and d go into the short. – JWWalker Aug 23 '17 at 15:23

1 Answers1

2

It's a padding issue. With Visual Studio you can use the #pragma pack(1) compiler directive disable any padding of structures.

Demonstration

#include<stdio.h>
#include<windows.h>

// Default packing of structure with padding

typedef struct {
  CHAR  idlength;
  CHAR  colourmaptype;
  CHAR  datatypecode;
  WORD colourmaporigin;
  WORD colourmaplength;
  CHAR  colourmapdepth;
  WORD x_origin;
  WORD y_origin;
  WORD width;
  WORD height;
  CHAR  bitsperpixel;
  CHAR  imagedescriptor;
} TGAHEADER;


#pragma pack(1) // structure fields are aligned to byte boundary (no padding)

typedef struct {
  CHAR  idlength;
  CHAR  colourmaptype;
  CHAR  datatypecode;
  WORD colourmaporigin;
  WORD colourmaplength;
  CHAR  colourmapdepth;
  WORD x_origin;
  WORD y_origin;
  WORD width;
  WORD height;
  CHAR  bitsperpixel;
  CHAR  imagedescriptor;
} TGAHEADER_PACKED;

int main()
{
  printf("Offset of field bitsperpixel in TGAHEADER structure %d\n", offsetof(TGAHEADER, bitsperpixel));
  printf("Offset of field bitsperpixel in packed TGAHEADER structure %d\n", offsetof(TGAHEADER_PACKED, bitsperpixel));
}

Output:

Offset of field bitsperpixel in TGAHEADER structure 18
Offset of field bitsperpixel in packed TGAHEADER structure 16
Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
  • Just a commen, using #pragma pack(1) can be risky, in my project it caused some crashes in wierd places. The better solution would be to surrond the structure you don't want to be padded with #pragma pack(push,1) and #pragma pack(pop) – Bartosz Boczula Aug 24 '17 at 15:12
  • @BartoszBoczula good point, please feel free to improve my answer. – Jabberwocky Aug 24 '17 at 15:14