Hi. I'm trying to learn something about binary and about the MD2 quake format.
Anyway. look at this code:
[code]
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <iostream>
using namespace std;
/* MD2 header */
struct md2_header
{
int magic_num;
int version;
};
struct md2_header md2file;
int main()
{
FILE *fp;
fp = fopen ("box.md2", "rb");
fread (&md2file, 1, sizeof (struct md2_header), fp);
cout << "ident " << md2file.magic_num << endl ;
cout << "version " << md2file.version << endl ;
getch();
return 0;
}
}
[/code]
As you can see I load data from a file called "box.md2" and the data in this file is:
[HTML]IDP28[/HTML]
But now the question, why and how can the varible magic_num be 844121161 and the variable version be 56? Ok the variable version is 56 because of the '8' in the the box.md2 file, but how can my program know where to break/ change variable to write to. if I delete the '8' in the file version will be 0. and I change 'IDP2' so something else the magic_num variable will be something else, You probably got the point, if not, im sorry for my english.
[editline]10:15AM[/editline]
and please tell me if there's something I can improve in my code :)
Your struct expects 2 four-byte integers in binary format while your text file has 5 text characters (five bytes of ANSI ASCII text).
What you're doing simply makes no sense.
Note that fread returns the number of bytes successfully read:
[cpp]
size_t read = fread (&md2file, 1, sizeof (struct md2_header), fp);
assert(read == sizeof(struct md2_header));
[/cpp]
[QUOTE=jA_cOp;20038162]Your struct expects 2 four-byte integers in binary format while your text file has 5 text characters (five bytes of ANSI ASCII text).
What you're doing simply makes no sense.
Note that fread returns the number of bytes successfully read:
[cpp]
size_t read = fread (&md2file, 1, sizeof (struct md2_header), fp);
assert(read == sizeof(struct md2_header));
[/cpp][/QUOTE]
Thanks you so much :O
But but how can I read 3 bytes from the file to my magic_number variable??
[QUOTE=likesoursugar;20038307]Thanks you so much :O
But but how can I read 3 bytes from the file to my magic_number variable??[/QUOTE]
IDP is the signature/magic number?
The straight-forward way:
[cpp]
char magic[4];
magic[3] = '\0';
size_t read = fread(magic, 1, 3, fp);
assert(read == 3);
printf("Magic: %s\n", magic);
[/cpp]
Won't the statement in the assert be ignored in non-debug builds?
"IDP2" is the magic number, four 8bit characters totalling 32bits. It simply reads the first 32 bits (The size of an int.) into the first variable in the structure then moves on to the next. (The version, which is just "8".)
See more here about the file format and parsing it:
[url]http://tfc.duke.free.fr/coding/md2-specs-en.html[/url]
Also FYI, you don't need the keyword struct in "struct md2_header md2file;" as it already knows that it's a structure. Both ways will compile.
oho, cool. but what does 'assert' exactly do?
[editline]11:15AM[/editline]
[QUOTE=yngndrw;20038503]"IDP2" is the magic number, four 8bit characters totalling 32bits. It simply reads the first 32 bits (The size of an int.) into the first variable in the structure then moves on to the next. (The version, which is just "8".)
See more here about the file format and parsing it:
[url]http://tfc.duke.free.fr/coding/md2-specs-en.html[/url]
Also FYI, you don't need the keyword struct in "struct md2_header md2file;" as it already knows that it's a structure. Both ways will compile.[/QUOTE]
thanks :D
[editline]11:19AM[/editline]
[QUOTE=jA_cOp;20038417]IDP is the signature/magic number?
The straight-forward way:
[cpp]
char magic[4];
magic[3] = '\0';
assert(fread(magic, 1, 3, fp) == 3);
printf("Magic: %s\n", magic);
[/cpp][/QUOTE]
hmm. But how to read the 3 first bytes to the magic_num and then the 5:th byte to the version variable?I can use 1 single line of code for reading then? I need to use fSeek then?
[QUOTE=yngndrw;20038503]Also FYI, you don't need the keyword struct in "struct md2_header md2file;" as it already knows that it's a structure. Both ways will compile.[/QUOTE]
You need to type 'struct blagh' unless you typedef the struct
[QUOTE=likesoursugar;20038507]hmm. But how to read the 3 first bytes to the magic_num and then the 5:th byte to the version variable?I can use 1 single line of code for reading then? I need to use fSeek then?[/QUOTE]The '2' is part of the magic number, don't skip over it. If you wanted to skip over a character anyway for some other part, add some padding into the structure. (A dummy char-type variable which will be discarded.) You may need to set the packing to '1' for this.
[QUOTE=turby;20038550]You need to type 'struct blagh' unless you typedef the struct[/QUOTE]
The structure of type 'md2_header' is already defined in the above block:
[cpp]struct md2_header
{
int magic_num;
int version;
};[/cpp]
I'm referring to the next line where he is declaring the variable 'md2file':
[cpp]struct md2_header md2file;[/cpp]
[QUOTE]If you wanted to skip over a character anyway for some other part, add some padding into the structure. (A dummy char-type variable which will be discarded.)[/QUOTE]
How to do this? :) A link for some guide would be nice if you don't want to explain :)
[QUOTE=ZeekyHBomb;20038494]Won't the statement in the assert be ignored in non-debug builds?[/QUOTE]
Good point! I've been using assert a lot in Lua lately, and it looks like it has leaked into my C.
[QUOTE=yngndrw;20038503]
Also FYI, you don't need the keyword struct in "struct md2_header md2file;" as it already knows that it's a structure. Both ways will compile.[/QUOTE]
This only applies to C++. In C, the struct needs to be typedef'd if you want that kind of behaviour.
Here's an example showing packing to 1byte:
[cpp]#pragma pack( push, 1 ) // 1 Byte Packing
struct SExampleHeader
{
unsigned int MagicNumber;
char Packing1;
unsigned int Version;
char Packing2[3];
char OtherInterestingData[2];
};
#pragma pack( pop )[/cpp]
In this example, you have:
[cpp]MagicNumber - 4bytes
Packing1 - 1byte
Version - 4bytes
Packing2 - 3bytes
OtherInterestingData - 2bytes[/cpp]
Both packing values would be discarded.
[QUOTE=jA_cOp;20038659]This only applies to C++. In C, the struct needs to be typedef'd if you want that kind of behaviour.[/QUOTE]
Treat this as a question as I'm unsure about some of the specific differences between C++ and C, but wouldn't it be the same as the type already exists from the structure ?
I.e:
[cpp]struct <Typename>
{
<Structure Data>
} <Variable>;[/cpp]
With both Typename and Variable being optional. Typename then becomes a type which can be used to define a variable in the same way that you would use the type "int" to define a variable, for example.
[cpp]int MyInteger;
TStruct MyObject;[/cpp]
[QUOTE]Here's an example showing packing to 1byte:[/QUOTE]
of course :D that's the way to do it :)
But how to do it if I want to load 3 bytes into an integer?
I cant use arrays for it :O
[QUOTE=yngndrw;20038663]
Treat this as a question as I'm unsure about some of the specific differences between C++ and C, but wouldn't it be the same as the type already exists from the structure ?
I.e:
[code]struct <Typename>
{
<Structure Data>
} <Variable>;[/code]
With both Typename and Variable being optional. Typename then becomes a type which can be used to define a variable in the same way that you would use the type "int" to define a variable, for example.
[code]int MyInteger;
TStruct MyObject;[/code][/QUOTE]
C requires the 'struct' and 'enum' keywords to declare instances of struct and enum types. C++ automatically typedefs them, allowing both ways automatically (using the same identifier, which is not possible to emulate in C).
In C:
[cpp]
struct S
{
int a;
};
enum MY_ENUM
{
MY_ENUM_FIRST
};
int main()
{
struct S s = {1};
enum MY_ENUM first = MY_ENUM_FIRST;
return 0;
}
[/cpp]
It's very common to do the following instead, allowing for C++ style declarations:
[cpp]
typedef struct
{
int a;
} S;
typedef enum
{
MY_ENUM_FIRST
} MY_ENUM;
int main()
{
S s;
MY_ENUM first = MY_ENUM_FIRST;
return 0;
}
[/cpp]
[QUOTE=likesoursugar;20038718]of course :D that's the way to do it :)
But how to do it if I want to load 3 bytes into an integer?
I cant use arrays for it :O[/QUOTE]
I'd personally read the data into a three character array then assign that value into an integer.
Could use something like this:
[cpp]#pragma pack( push, 1 ) // 1 Byte Packing
struct SInputData
{
// ...
char Data[3];
// ...
} InputData;
struct SOutputData
{
// ...
unsigned int Data;
// ...
} OutputData;
union U3ByteToInt
{
unsigned int Output;
struct
{
char Data[3];
char Packing;
} Input;
U3ByteToInt()
{
Input.Packing = 0;
}
} Converter3ByteToInt;
#pragma pack( pop )
memcpy( Converter3ByteToInt.Input.Data, InputData.Data, 3 );
OutputData.Data = Converter3ByteToInt.Output;[/cpp]
Not sure it's the best method though ...
[b]Edit:[/b] Fixed it.
[QUOTE=jA_cOp;20038757]C requires the 'struct' and 'enum' keywords to declare instances of struct and enum types. C++ automatically typedefs them, allowing both ways automatically (using the same identifier, which is not possible to emulate in C).[/QUOTE]
Ah I see, thanks. :)
hmm. But anyway, the MD2 file format use almost only integers and some shorts. But that mean that the values in the md2 file must use 4 bytes, or the file reading will be totally wrong? and the values for all the variables will be even more wrong?
I'm not quite sure on the question you're asking, but here's what I think you're asking.
"Seems as the Magic Number and Version variables are both 4byte, does the start of the MD2 file need to be "IDP28000" rather than "IDP28" ?"
Yes, however keep in mind that the above is an ASCII representation and as such the 8000 part (It's read backwards.) should really be 0x08 0x00 0x00 0x00 in Hex.
[QUOTE=yngndrw;20039285]I'm not quite sure on the question you're asking, but here's what I think you're asking.
"Seems as the Magic Number and Version variables are both 4byte, does the start of the MD2 file need to be "IDP28000" rather than "IDP28" ?"
Yes, however keep in mind that the above is an ASCII representation and as such the 8000 part (It's read backwards.) should really be 0x08 0x00 0x00 0x00 in Hex.[/QUOTE]
ok now I probably will be dumb but... 8000 isn't close to 8 at all :( hmm you can't mean that IDP28000 = IDP28 :oooo
*snip*
[QUOTE=likesoursugar;20039653]ok now I probably will be dumb but... 8000 isn't close to 8 at all :( hmm you can't mean that IDP28000 = IDP28 :oooo[/QUOTE]
The bytes are read backwards for integers.
For example, 0x01 0x02 0x03 0x04 would be 4321 (In Hex ?) so in this case it ends up as 0008.
IDP28000 is read the characters I D P 2 and the number 8. (But when the characters are converted to a number, they are also reversed.)
[code]( '2' << 24 ) + ( 'P' << 16 ) + ( 'D' << 8 ) + 'I'[/code]
[QUOTE=yngndrw;20040375]The bytes are read backwards for integers.[/QUOTE]
Depends on the [url=http://en.wikipedia.org/wiki/Endianness]endianness[/url] of the computer, actually. The x86 architecture is little-endian so numbers are stored "backwords", but when saving to files or sending data over a network, you should convert to a machine-independent standard byte order (called "network byte order", which happens to be big-endian) for portability.
POSIX provides the htonl(), ntohl(), htons(), and ntohs() functions for this. Windows probably has something similar.
I just mixed binary, decimal and hex... But now I know how it work. :D I'm using a hex editor to edit my binary files :D thanks everyone
Sorry, you need to Log In to post a reply to this thread.