• Hey, guest user. Hope you're enjoying NeoGAF! Have you considered registering for an account? Come join us and add your take to the daily discourse.

C File I/O help needed

Status
Not open for further replies.
I'm trying to write a bitmap file I generate for my raytracer. However, I'm having a bit of trouble writing out the headers to the file. The header contains 5 fields- the sizes being 2, 4, 2, 2, and 4 bytes respectively.

Right now I'm just taking a pointer to the beginning of the structure and writing out until I hit the end of the structure. However, in RAM, it's padding the first two-byte field so the 4-byte can start at a multiple of 4.

So the memory ends up looking like this:
01 01 00 00 02 02 02 02 03 03 04 04 (assuming each field has a value of the number of it)

Anyway, I'm being kind of long-winded here. The only way I can think of to resolve this is to write out each field in the structure individually, but that seems to make for rather messy code... I'm wondering if there's a more elegant solution to this.

Please help me, thanks :).

Here's the relevant code in question:

Code:
// I know these values are correct
typedef unsigned short u2;
typedef unsigned long u4;

// The header in question
typedef struct {
  u2 bfType;
  u4 bfSize;
  u2 bfReserved1;
  u2 bfReserved2;
  u4 bfOffBits;
} bitmapFileHeader;

// Another, unmentioned header, that presumably suffers from the same problem as the first
typedef struct {
  u4 biSize;
  u4 biWidth;
  u4 biHeight;
  u2 biPlanes;
  u2 biBitCount;
  u4 biCompression;
  u4 biSizeImage;
  u4 biXPelsPerMeter;
  u4 biYPelsPerMeter;
  u4 biClrUsed;
  u4 biClrImportant;
} bitmapInfoHeader;

...
...
...

  bitmapFileHeader bff;
  memset( &bff, 0, sizeof(bff) );

// These are the actual values I'm setting the field to.  The file, when written, looks like:
// 22 11 00 00 66 55 44 33 88 77 AA 99 EE DD CC BB (those zeroes shouldnt be there, that's my problem)
  bff.bfType = 0x1122;
  bff.bfSize = 0x33445566;
  bff.bfReserved1 = 0x7788;
  bff.bfReserved2 = 0x99AA;
  bff.bfOffBits = 0xBBCCDDEE;

  FILE *f;
  f = fopen( "out.bmp", "w" );
  fwrite( &bff, 1, sizeof( bff ), f  );
 

RepMovSB

Member
Are these your headers?

If so something like this (can't remember exactly)...

#pragma pack (1) // Set to single byte alignment

typedef struct
{
// blah,blah,blah
} bitmapInfoHeader;

#pragma pack () // set back to default alignment

Or alternatively, set your project settings...

Configuration properties -> C/C++ -> Code Generation -> Struct Member Alignment.

Umm, hope that's helpful.
 

maharg

idspispopd
RepMovSB said:
Are these your headers?

If so something like this (can't remember exactly)...

#pragma pack (1) // Set to single byte alignment

typedef struct
{
// blah,blah,blah
} bitmapInfoHeader;

#pragma pack () // set back to default alignment

Or alternatively, set your project settings...

Configuration properties -> C/C++ -> Code Generation -> Struct Member Alignment.

Umm, hope that's helpful.

This will in fact do it for Visual C. Of the two solutions, setting the packing for the structs alone is the better one. Member alignment is there for a reason, you shouldn't turn it off.

However, writing the members out individually will in fact work portably. Especially if you use the size they *should* be instead of using sizeof() on them. That will be portable at the very least to all little endian platforms (and endian portability would require additional work anyways) regardless of the actual sizes of your struct members (relying on the size of a intrinsic integral type being x bits is not portable, except in so far as they are at least a certain size).
 

RepMovSB

Member
Yep.

I was gonna mention the portability thing but didn't want to end up writing an excessive post in response to the question.

One problem with reading/writing individual fields is that it can make your load times chug pretty bad once you're dealing with signficant amounts of data. Better to read the structs in and flip the bytes around afterwards.
 

maharg

idspispopd
Ideally it shouldn't be that big of a deal. The f* functions buffer, so you shouldn't end up doing IO for every call. But I don't know how 'smart' most implementations are.
 
Cool, got it working (albiet by switching to a TGA format because I couldn't get the BMP working even after that), thanks guys.

Is the #pragma pack directive mostly compiler independent (ie, do most of them have it implemented the same way?).
 

maharg

idspispopd
Mister Zimbu said:
Is the #pragma pack directive mostly compiler independent (ie, do most of them have it implemented the same way?).

No. None of them have it the same way at all.

If you're using a #pragma directive, it's nonstandard by definition and highly unlikely to be portable in any way.
 
To make your code portable and easily maintainable by others I recommend you create input/output "stream" classes(c++)/functions(c) to handle data IO in a portable and clean way.

In your code you simply iterate over each member of the struct/class/whatever you want to serialize/deserialize and you're done.

Below is some pseudo code and a few hints on how you could maintain your simple buffer while you work...

You can increase the size of _data when needed by using "realloc" or just allocate it automatically to the right size.... remember that realloc memory must be reclaimed with "free" if you are using C++ and typically use new/delete.

Of course, if you won't be serializing/deserializing to anywhere but a file, the _data buffer is not necessary and you could read/write data in 1 byte increments directly to a file....

signed/unsigned 8bit reading
val = 0;
val = _data[_pos++];

signed/unsigned 8bit writing
_data[_pos++] = (val & 0xff);

signed/unsigned 16bit reading
val = 0;
val |= (_data[_pos++] << 8);
val |= (_data[_pos++] << 0);

signed/unsigned 16bit writing
_data[_pos++] = ((val & 0xff00) >> 8);
_data[_pos++] = (val & 0xff);

signed/unsigned 32bit reading
val = 0;
val |= (_data[_pos++] << 24);
val |= (_data[_pos++] << 16);
val |= (_data[_pos++] << 8);
val |= (_data[_pos++] << 0);

signed/unsigned 32bit writing
_data[_pos++] = ((val & 0xff000000) >> 24);
_data[_pos++] = ((val & 0x00ff0000) >> 16);
_data[_pos++] = ((val & 0x0000ff00) >> 8);
_data[_pos++] = (val & 0xff);

data blob reading/writing
simple make repeated calls to read/write over a passed in array of linear data or
just use memcpy to copy that data into your buffer.
 
Status
Not open for further replies.
Top Bottom