X File (DirectX 3D File Format) Binary
by bkenwright@xbdev.net
Now the .x file format is one of the most complex 3D file formats... but its
also one of the best. It allows you to store a number of specific
information in the 3D file....ranging from simple meshes, to bone
animation....to materials etc.
There are two ways to import .x files into your game!...or demo... the first
is to use the .x file parsers/loaders that come with directx... the second is to
use your own .x file loader, which is what where going to do here :)
First thing first.
This tutorials a bit slow, as usual I start very simple, and am going to go
through all the pain of going from the start and then disecting the format byte
by byte from the start until we have extracted all the information. I find
its not so easy to skip over unwanted information with the .x binary file
format, but we'll see what we can do :)
Code Snippet |
/********************************************************************************/
/* */
/* File: main.cpp */
/* Auth: Ben Kenwright */
/* Email: bkenwright@xbdev.net */
/* Url: www.xbdev.net */
/* Date: 19/12/06 (xmas) */
/* */
/********************************************************************************/
/*
About?
Workings of the .x binary 3d directx file format
*/
/********************************************************************************/
#include <stdio.h> // fopen(..), sprintf(..)
#include <stdarg.h> // va_start, va_end
// Debug output to a text file 'output.txt', so we can dump our information out
// to a debug text file, so we can look at it
void dprintf(const char *fmt, ...)
{
va_list parms;
char buf[256] = {0};
// Try to print in the allocated space.
va_start(parms, fmt);
vsprintf (buf, fmt, parms);
va_end(parms);
// Write the information out to a txt file
FILE *fp = fopen("output.txt", "a+");
fprintf(fp, "%s", buf);
fclose(fp);
}// End dprintf(..)
/********************************************************************************/
// Program Entry Point
void main()
{
dprintf("Opening binary directx .x binary file (racer.x)\n\n");
FILE* fp = fopen("racer.x", "rb");
// Large temp buffer
char buf[100];
// Lets read in the first 4 characters of the file are, and see what
// they look like
int r = // The total number of items readed is returned.
(int)fread (buf, // void * buffer,
sizeof(char), // size
4, // count,
fp ); // FILE * stream
dprintf("First 4 bytes: '%c', '%c', '%c', '%c'\n\n",
buf[0],
buf[1],
buf[2],
buf[3]);
dprintf("Done - Closing File\n\n");
fclose(fp);
}// End main()
/* output.txt
Opening binary directx .x binary file (racer.x)
First 4 bytes: 'x', 'o', 'f', ' '
Done - Closing File
*/
|
Taking it to the next level we can start bye creating a structure to define
our .x binary header, which should be the same for the text and binary
version....so we can read this in, and it will show us what type of directx .x
file we are dealing with.
Again its not to complicated, and well if you've looked at any file format
you'll know that once you've seen one header you've seen them all....in this
case its a signature, 'xof ' couple of version numbers, followed by its type.
Below shows the sweet code that will dump the info to our text file.
Code Snippet |
/********************************************************************************/
/* */
/* File: main.cpp */
/* Auth: Ben Kenwright */
/* Email: bkenwright@xbdev.net */
/* Url: www.xbdev.net */
/* Date: 19/12/06 (xmas) */
/* */
/********************************************************************************/
/*
About?
Workings of the .x binary 3d directx file format
*/
/********************************************************************************/
// DirectX Header Defines
#pragma pack(1)
struct stXHeader
{ // Size Desc
char m_sig[4]; // 4 Magic Number (required) "xof "
char m_minor[2]; // 2 Minor Version 03
char m_major[2]; // 2 Major Version 02
char m_type[4]; // 4 Format Type (required)
// "txt " Text File
// "bin " Binary File
// "tzip" MSZip Compressed Text File
// "bzip" MSZip Compressed Binary File
char m_accuracy[4]; // 4 Float Accuracy "0032" 32 bit or "0064" 64 bit
};// End stXHeader
#pragma pack()
/********************************************************************************/
#include <stdio.h> // fopen(..), sprintf(..)
#include <stdarg.h> // va_start, va_end
// Debug output to a text file 'output.txt', so we can dump our information out
// to a debug text file, so we can look at it
void dprintf(const char *fmt, ...)
{
va_list parms;
char buf[256] = {0};
// Try to print in the allocated space.
va_start(parms, fmt);
vsprintf (buf, fmt, parms);
va_end(parms);
// Write the information out to a txt file
FILE *fp = fopen("output.txt", "a+");
fprintf(fp, "%s", buf);
fclose(fp);
}// End dprintf(..)
/********************************************************************************/
// Program Entry Point
void main()
{
dprintf("Opening binary directx .x binary file (racer.x)\n\n");
FILE* fp = fopen("racer.x", "rb");
// Large temp buffer
stXHeader h;
// Lets read in the first 4 characters of the file are, and see what
// they look like
int r = // The total number of items readed is returned.
(int)fread (&h, // void * buffer,
sizeof(h), // size
1, // count,
fp ); // FILE * stream
dprintf("stXHeader: \n");
// Signature
dprintf("\tSig: %c%c%c%c\n", h.m_sig[0], h.m_sig[1], h.m_sig[2], h.m_sig[3]);
// Minor Version
dprintf("\tMinor: %c%c\n", h.m_minor[0], h.m_minor[1]);
// Majour Version
dprintf("\tMajour: %c%c\n", h.m_major[0], h.m_major[1]);
// Type
dprintf("\tType: %c%c%c%c\n", h.m_type[0], h.m_type[1], h.m_type[2], h.m_type[3]);
// Accuracy
dprintf("\tAccuracy: %c%c%c%c\n", h.m_accuracy[0],
h.m_accuracy[1],
h.m_accuracy[2],
h.m_accuracy[3]);
dprintf("\nDone - Closing File\n\n");
fclose(fp);
}// End main()
/* output.txt
Opening binary directx .x binary file (racer.x)
stXHeader:
Sig: xof
Minor: 03
Majour: 02
Type: bin
Accuracy: 0032
Done - Closing File
*/
|
What next?
It sort of goes a bit crazy from here... I mean the file format isn't one
where we can easily skip over data that we don't want....for example, if you hit
a template tag id, then you can't just check how many bytes it is and skip to
the next tag....oh no, we have to parse the whole template layout, with lots of
nasty children and things...oh well...just one of those things we have to live
with.
Now the .x binary format works by dividing the data up into tokens!
First a token is a two byte id, which describes which type of token it is.
The list of token id's have been added to the code below, theres about 50
different types. So for example if you read in a token, and its value was
'0001', then you'd know you had a string name token....they should have made all
the tokens with an id followed by a size! Damm you microsoft!...always
making things difficult. Anyhow, the name token, is simple, the 2 bytes
which represent its token id, followed by another 4 bytes which represent the
number of characters in the string (not including the null character)....and
then you have a list of characters. Let me say this again...doesn't
include the null character in the length!...its length value is the number of
characters to follow...thats it.
As you look at the code, you'll see that each token has its own little
quirkyness that you'll just have to live with when you read in each one....The
below code will just read in most of the common tokens and dump the information
to a text file...once it hits a token we've not implemented yet, it will just
exit....so for example when it comes to the templates and the misc data ones it
will just bale out...we'll add in that later on.
Code Snippet |
/********************************************************************************/
/* */
/* File: main.cpp */
/* Auth: Ben Kenwright */
/* Email: bkenwright@xbdev.net */
/* Url: www.xbdev.net */
/* Date: 19/12/06 (xmas) */
/* */
/********************************************************************************/
/*
About?
Workings of the .x binary 3d directx file format
*/
/********************************************************************************/
#include <stdio.h> // fopen(..), sprintf(..)
#include <stdarg.h> // va_start, va_end
#include <string> // memset(..)
// Debug output to a text file 'output.txt', so we can dump our information out
// to a debug text file, so we can look at it
void dprintf(const char *fmt, ...)
{
va_list parms;
char buf[256] = {0};
// Try to print in the allocated space.
va_start(parms, fmt);
vsprintf (buf, fmt, parms);
va_end(parms);
// Write the information out to a txt file
FILE *fp = fopen("output.txt", "a+");
fprintf(fp, "%s", buf);
fclose(fp);
}// End dprintf(..)
/********************************************************************************/
// DirectX Header Defines
#pragma pack(1)
struct stXHeader
{ // size desc
char m_sig[4]; // 4 Magic Number (required) "xof "
char m_minor[2]; // 2 Minor Version 03
char m_major[2]; // 2 Major Version 02
char m_type[4]; // 4 Format Type (required)
// "txt " Text File
// "bin " Binary File
// "tzip" MSZip Compressed Text File
// "bzip" MSZip Compressed Binary File
char m_accuracy[4]; // 4 Float Accuracy "0032" 32 bit or "0064" 64 bit
};// End stXHeader
#pragma pack()
// Record-bearing
#define TOKEN_NAME 1
#define TOKEN_STRING 2
#define TOKEN_INTEGER 3
#define TOKEN_GUID 5
#define TOKEN_INTEGER_LIST 6
#define TOKEN_FLOAT_LIST 7
// Stand-alone
#define TOKEN_OBRACE 10
#define TOKEN_CBRACE 11
#define TOKEN_OPAREN 12
#define TOKEN_CPAREN 13
#define TOKEN_OBRACKET 14
#define TOKEN_CBRACKET 15
#define TOKEN_OANGLE 16
#define TOKEN_CANGLE 17
#define TOKEN_DOT 18
#define TOKEN_COMMA 19
#define TOKEN_SEMICOLON 20
#define TOKEN_TEMPLATE 31
#define TOKEN_WORD 40
#define TOKEN_DWORD 41
#define TOKEN_FLOAT 42
#define TOKEN_DOUBLE 43
#define TOKEN_CHAR 44
#define TOKEN_UCHAR 45
#define TOKEN_SWORD 46
#define TOKEN_SDWORD 47
#define TOKEN_VOID 48
#define TOKEN_LPSTR 49
#define TOKEN_UNICODE 50
#define TOKEN_CSTRING 51
#define TOKEN_ARRAY 52
#define MAX_STRING 128
#define MAX_INT_LIST 128
#define MAX_FLOAT_LIST 128
#pragma pack(1)
struct stTokenName
{
unsigned short m_token; // token_name
unsigned int m_count; // Length of name field, in bytes
char m_name[MAX_STRING]; // array count ASCII name
};
struct stTokenString
{
unsigned short m_token; // WORD 2 token_string
unsigned int m_count; // DWORD 4 Length of string field in bytes
char m_string[MAX_STRING];// BYTE array count ASCII string
unsigned int m_terminator; // DWORD 4 tOKEN_SEMICOLON or TOKEN_COMMA
};
struct stTokenInt
{
unsigned short m_token; // WORD 2 tOKEN_INTEGER
unsigned int m_value; // DWORD 4 Single integer
};
struct stTokenGUID
{
unsigned short m_token; // WORD 2 tOKEN_GUID
unsigned short m_Data1; // DWORD 4 UUID data field 1
unsigned short m_Data2; // WORD 2 UUID data field 2
unsigned short m_Data3; // WORD 2 UUID data field 3
unsigned char m_Data4[4]; // BYTE array 8 UUID data field 4
};
struct stTokenIntList
{
unsigned short m_token; // WORD 2 tOKEN_INTEGER_LISt
unsigned int m_count; // DWORD 4 Number of integers in list field
unsigned int m_list[MAX_INT_LIST];// DWORD 4 x count Integer list
};
struct stTokenFloatList
{
unsigned short m_token; // WORD 2 tOKEN_FLOAT_LISt
unsigned int m_count; // DWORD 4 Number of floats or doubles in list field
float m_list[MAX_FLOAT_LIST]; // float/double array 4 or 8 x count Float or double list
// determined from the header accuracy
};
#pragma pack()
//--------------------------------------------------------------------------------
void ReadTokenName(FILE* fp, stTokenName* tok)
{
tok->m_token = TOKEN_NAME;
fread(&tok->m_count, sizeof(unsigned int), 1, fp);
dprintf("size: %d\n", tok->m_count);
// Size doesn't include the null terminator!
memset(&tok->m_name, '\0', sizeof(tok->m_name));
for (unsigned int i=0; i<tok->m_count; i++)
{
fread(&tok->m_name[i], sizeof(char), 1, fp);
}
dprintf("name: %s\n", tok->m_name);
}
//--------------------------------------------------------------------------------
void ReadTokenString(FILE* fp, stTokenString* tok)
{
tok->m_token = TOKEN_STRING;
fread(&tok->m_count, sizeof(unsigned int), 1, fp);
dprintf("size: %d\n", tok->m_count);
// Size doesn't include the null terminator!
memset(&tok->m_string, '\0', sizeof(tok->m_string));
for (unsigned int i=0; i<tok->m_count; i++)
{
fread(&tok->m_string[i], sizeof(char), 1, fp);
}
dprintf("string: %s\n", tok->m_string);
fread(&tok->m_terminator, sizeof(unsigned int), 1, fp);
dprintf("terminator: %d\n", tok->m_terminator);
}
//--------------------------------------------------------------------------------
void ReadTokenInt(FILE *fp, stTokenInt* tok)
{
tok->m_token = TOKEN_INTEGER;
fread(&tok->m_value, sizeof(unsigned int), 1, fp);
dprintf("value: %d\n", tok->m_value);
}
//--------------------------------------------------------------------------------
void ReadTokenGUID(FILE* fp, stTokenGUID* tok)
{
tok->m_token = TOKEN_GUID;
fread(&tok->m_Data1, sizeof(unsigned short), 1, fp);
fread(&tok->m_Data2, sizeof(unsigned short), 1, fp);
fread(&tok->m_Data3, sizeof(unsigned short), 1, fp);
fread(&tok->m_Data4[0], sizeof(unsigned char), 1, fp);
fread(&tok->m_Data4[1], sizeof(unsigned char), 1, fp);
fread(&tok->m_Data4[2], sizeof(unsigned char), 1, fp);
fread(&tok->m_Data4[3], sizeof(unsigned char), 1, fp);
dprintf("Data1: 0x%x\n", tok->m_Data1);
dprintf("Data2: 0x%x\n", tok->m_Data2);
dprintf("Data3: 0x%x\n", tok->m_Data3);
dprintf("Data4: 0x%x %x %x %x\n", tok->m_Data4[0],
tok->m_Data4[1],
tok->m_Data4[2],
tok->m_Data4[3]);
}
//--------------------------------------------------------------------------------
void ReadTokenIntList(FILE* fp, stTokenIntList* tok)
{
tok->m_token = TOKEN_INTEGER_LIST;
fread(&tok->m_count, sizeof(unsigned int), 1, fp);
dprintf("count: %d\n", tok->m_count);
memset(&tok->m_list[0], 0, sizeof(tok->m_list));
for (unsigned int i=0; i<tok->m_count; i++)
{
unsigned int tmp;
fread(&tmp, sizeof(unsigned int), 1, fp);
if (i < 128)
{
tok->m_list[i] = tmp;
}
dprintf("%d,", tmp);
}
dprintf("\n\n");
}
//--------------------------------------------------------------------------------
void ReadTokenFloatList(FILE* fp, stTokenFloatList* tok)
{
tok->m_token = TOKEN_FLOAT_LIST;
fread(&tok->m_count, sizeof(unsigned int), 1, fp);
dprintf("count: %d\n", tok->m_count);
memset(&tok->m_list[0], 0, sizeof(tok->m_list));
for (unsigned int i=0; i<tok->m_count; i++)
{
float tmp;
fread(&tmp, sizeof(float), 1, fp);
if (i < 128)
{
tok->m_list[i] = tmp;
}
dprintf("%.1f ,", tmp);
}
dprintf("\n\n");
}
//--------------------------------------------------------------------------------
bool ReadTokens(FILE * fp)
{
unsigned short tokenType;
int r =
(int)fread(&tokenType, sizeof(tokenType), 1, fp);
if (r==0)
{
return false;
}
dprintf("TokenType: %d\n", tokenType);
switch (tokenType)
{
case TOKEN_NAME:
{
stTokenName tok;
ReadTokenName(fp, &tok);
}
break;
case TOKEN_STRING:
{
stTokenString tok;
ReadTokenString(fp, &tok);
}
break;
case TOKEN_INTEGER:
{
stTokenInt tok;
ReadTokenInt(fp, &tok);
}
break;
case TOKEN_GUID:
{
stTokenGUID tok;
ReadTokenGUID(fp, &tok);
}
break;
case TOKEN_INTEGER_LIST:
{
stTokenIntList tok;
ReadTokenIntList(fp, &tok);
}
break;
case TOKEN_FLOAT_LIST:
{
stTokenFloatList tok;
ReadTokenFloatList(fp, &tok);
}
break;
case TOKEN_OBRACE:
{
dprintf("{\n");
}
break;
case TOKEN_CBRACE:
{
dprintf("}\n");
}
break;
default:
{
dprintf("Uknown Token Type\n");
// Once we reach an unknown type we'll just bail out here, usually
// the templates or something
return false;
}
}
return true;
}// End ReadTokens(..)
/********************************************************************************/
// Program Entry Point
void main()
{
dprintf("Opening binary directx .x binary file (racer.x)\n\n");
FILE* fp = fopen("racer.x", "rb");
// Large temp buffer
stXHeader h;
// Lets read in the first 4 characters of the file are, and see what
// they look like
int r = // The total number of items readed is returned.
(int)fread (&h, // void * buffer,
sizeof(h), // size
1, // count,
fp ); // FILE * stream
dprintf("stXHeader: \n");
// Signature
dprintf("\tSig: %c%c%c%c\n", h.m_sig[0], h.m_sig[1], h.m_sig[2], h.m_sig[3]);
// Minor Version
dprintf("\tMinor: %c%c\n", h.m_minor[0], h.m_minor[1]);
// Majour Version
dprintf("\tMajour: %c%c\n", h.m_major[0], h.m_major[1]);
// Type
dprintf("\tType: %c%c%c%c\n", h.m_type[0], h.m_type[1], h.m_type[2], h.m_type[3]);
// Accuracy
dprintf("\tAccuracy: %c%c%c%c\n", h.m_accuracy[0],
h.m_accuracy[1],
h.m_accuracy[2],
h.m_accuracy[3]);
bool doRead = true;
while (doRead)
{
doRead = ReadTokens(fp);
}
dprintf("\nDone - Closing File\n\n");
fclose(fp);
}// End main()
/* output.txt
Opening binary directx .x binary file (racer.x)
stXHeader:
Sig: xof
Minor: 03
Majour: 02
Type: bin
Accuracy: 0032
TokenType: 1
size: 6
name: Header
TokenType: 10
{
TokenType: 6
count: 3
1,0,0,
TokenType: 11
}
TokenType: 1
size: 5
name: Frame
TokenType: 1
size: 4
name: Root
TokenType: 10
{
TokenType: 1
size: 20
name: FrameTransformMatrix
TokenType: 10
{
TokenType: 7
count: 16
1.0 ,0.0 ,0.0 ,0.0 ,0.0 ,1.0 ,0.0 ,0.0 ,0.0 ,0.0 ,1.0 ,0.0 ,0.0 ,0.0 ,0.0 ,1.0 ,
TokenType: 11
}
TokenType: 1
size: 5
name: Frame
TokenType: 1
size: 17
name: frm-lfront_tires1
TokenType: 10
{
TokenType: 1
size: 20
name: FrameTransformMatrix
TokenType: 10
{
TokenType: 7
count: 16
1.0 ,0.0 ,0.0 ,0.0 ,0.0 ,1.0 ,0.0 ,0.0 ,0.0 ,0.0 ,1.0 ,0.0 ,0.0 ,0.0 ,0.0 ,1.0 ,
TokenType: 11
.... etc etc etc...
Done - Closing File
*/
|
Templates! There nasty things...well there not nasty, there a good
idea....but there a pain to parse and skip over. I've implemented part of
the template parsing code so you can read in the rest of our demo racer.x test
file. I found that for some reason its packed the template information
onto the end of the .x file....which is nothing more than some copyright
information about the .x file :)
Code: Download Code (13kb) |
/********************************************************************************/
/* */
/* File: main.cpp */
/* Auth: Ben Kenwright */
/* Email: bkenwright@xbdev.net */
/* Url: www.xbdev.net */
/* Date: 19/12/06 (xmas) */
/* */
/********************************************************************************/
/*
About?
Workings of the .x binary 3d directx file format
*/
/********************************************************************************/
#include <stdio.h> // fopen(..), sprintf(..)
#include <stdarg.h> // va_start, va_end
#include <string> // memset(..)
// Debug output to a text file 'output.txt', so we can dump our information out
// to a debug text file, so we can look at it
void dprintf(const char *fmt, ...)
{
va_list parms;
char buf[256] = {0};
// Try to print in the allocated space.
va_start(parms, fmt);
vsprintf (buf, fmt, parms);
va_end(parms);
// Write the information out to a txt file
FILE *fp = fopen("output.txt", "a+");
fprintf(fp, "%s", buf);
fclose(fp);
}// End dprintf(..)
/********************************************************************************/
// DirectX Header Defines
#pragma pack(1)
struct stXHeader
{ // size desc
char m_sig[4]; // 4 Magic Number (required) "xof "
char m_minor[2]; // 2 Minor Version 03
char m_major[2]; // 2 Major Version 02
char m_type[4]; // 4 Format Type (required)
// "txt " Text File
// "bin " Binary File
// "tzip" MSZip Compressed Text File
// "bzip" MSZip Compressed Binary File
char m_accuracy[4]; // 4 Float Accuracy "0032" 32 bit or "0064" 64 bit
};// End stXHeader
#pragma pack()
// Record-bearing
#define TOKEN_NAME 1
#define TOKEN_STRING 2
#define TOKEN_INTEGER 3
#define TOKEN_GUID 5
#define TOKEN_INTEGER_LIST 6
#define TOKEN_FLOAT_LIST 7
// Stand-alone
#define TOKEN_OBRACE 10
#define TOKEN_CBRACE 11
#define TOKEN_OPAREN 12
#define TOKEN_CPAREN 13
#define TOKEN_OBRACKET 14
#define TOKEN_CBRACKET 15
#define TOKEN_OANGLE 16
#define TOKEN_CANGLE 17
#define TOKEN_DOT 18
#define TOKEN_COMMA 19
#define TOKEN_SEMICOLON 20
#define TOKEN_TEMPLATE 31
#define TOKEN_WORD 40
#define TOKEN_DWORD 41
#define TOKEN_FLOAT 42
#define TOKEN_DOUBLE 43
#define TOKEN_CHAR 44
#define TOKEN_UCHAR 45
#define TOKEN_SWORD 46
#define TOKEN_SDWORD 47
#define TOKEN_VOID 48
#define TOKEN_LPSTR 49
#define TOKEN_UNICODE 50
#define TOKEN_CSTRING 51
#define TOKEN_ARRAY 52
#define MAX_STRING 128
#define MAX_INT_LIST 128
#define MAX_FLOAT_LIST 128
#pragma pack(1)
struct stTokenName
{
unsigned short m_token; // token_name
unsigned int m_count; // Length of name field, in bytes
char m_name[MAX_STRING]; // array count ASCII name
};
struct stTokenString
{
unsigned short m_token; // WORD 2 token_string
unsigned int m_count; // DWORD 4 Length of string field in bytes
char m_string[MAX_STRING];// BYTE array count ASCII string
unsigned int m_terminator; // DWORD 4 tOKEN_SEMICOLON or TOKEN_COMMA
};
struct stTokenInt
{
unsigned short m_token; // WORD 2 tOKEN_INTEGER
unsigned int m_value; // DWORD 4 Single integer
};
struct stTokenGUID
{
unsigned short m_token; // WORD 2 tOKEN_GUID
unsigned int m_Data1; // DWORD 4 UUID data field 1
unsigned short m_Data2; // WORD 2 UUID data field 2
unsigned short m_Data3; // WORD 2 UUID data field 3
unsigned char m_Data4[8]; // BYTE array 8 UUID data field 4
};
struct stTokenIntList
{
unsigned short m_token; // WORD 2 tOKEN_INTEGER_LISt
unsigned int m_count; // DWORD 4 Number of integers in list field
unsigned int m_list[MAX_INT_LIST];// DWORD 4 x count Integer list
};
struct stTokenFloatList
{
unsigned short m_token; // WORD 2 tOKEN_FLOAT_LISt
unsigned int m_count; // DWORD 4 Number of floats or doubles in list field
float m_list[MAX_FLOAT_LIST]; // float/double array 4 or 8 x count Float or double list
// determined from the header accuracy
};
#pragma pack()
struct stTokenTemplate
{
int m_token; // TOKEN_TEMPLATE
stTokenName m_tokName; // TOKEN_NAME
stTokenGUID m_tokGUID; // TOKEN_GUID
};
//--------------------------------------------------------------------------------
void ReadTokenName(FILE* fp, stTokenName* tok)
{
tok->m_token = TOKEN_NAME;
fread(&tok->m_count, sizeof(unsigned int), 1, fp);
dprintf("size: %d\n", tok->m_count);
// Size doesn't include the null terminator!
memset(&tok->m_name, '\0', sizeof(tok->m_name));
for (unsigned int i=0; i<tok->m_count; i++)
{
char tmp;
fread(&tmp, sizeof(char), 1, fp);
if (i+1 < MAX_STRING)
{
tok->m_name[i] = tmp;
}
else
{
dprintf("*");
}
}
dprintf("name: %s\n", tok->m_name);
}// End ReadTokenName(..)
//--------------------------------------------------------------------------------
void ReadTokenString(FILE* fp, stTokenString* tok)
{
tok->m_token = TOKEN_STRING;
fread(&tok->m_count, sizeof(unsigned int), 1, fp);
dprintf("size: %d\n", tok->m_count);
// Size doesn't include the null terminator!
memset(&tok->m_string, '\0', sizeof(tok->m_string));
for (unsigned int i=0; i<tok->m_count; i++)
{
char tmp;
fread(&tmp, sizeof(char), 1, fp);
if (i+1 < MAX_STRING)
{
tok->m_string[i] = tmp;
}
else
{
dprintf("*");
}
}
dprintf("string: %s\n", tok->m_string);
fread(&tok->m_terminator, sizeof(unsigned int), 1, fp);
dprintf("terminator: %d\n", tok->m_terminator);
}// End ReadTokenString(..)
//--------------------------------------------------------------------------------
void ReadTokenInt(FILE *fp, stTokenInt* tok)
{
tok->m_token = TOKEN_INTEGER;
fread(&tok->m_value, sizeof(unsigned int), 1, fp);
dprintf("value: %d\n", tok->m_value);
}// End ReadTokenInt(..)
//--------------------------------------------------------------------------------
void ReadTokenGUID(FILE* fp, stTokenGUID* tok)
{
tok->m_token = TOKEN_GUID;
fread(&tok->m_Data1, sizeof(unsigned int), 1, fp);
fread(&tok->m_Data2, sizeof(unsigned short), 1, fp);
fread(&tok->m_Data3, sizeof(unsigned short), 1, fp);
for (int i=0; i<8; i++)
{
fread(&tok->m_Data4[i], sizeof(unsigned char), 1, fp);
}
dprintf("Data1: 0x%.8x, ", tok->m_Data1);
dprintf("Data2: 0x%.4x, ", tok->m_Data2);
dprintf("Data3: 0x%.4x, ", tok->m_Data3);
dprintf("Data4: ");
for (int i=0; i<8; i++)
{
dprintf("0x%.2x, ", tok->m_Data4[i]);
}
dprintf("\n");
}// End ReadTokenGUID(..)
//--------------------------------------------------------------------------------
void ReadTokenIntList(FILE* fp, stTokenIntList* tok)
{
tok->m_token = TOKEN_INTEGER_LIST;
fread(&tok->m_count, sizeof(unsigned int), 1, fp);
dprintf("count: %d\n", tok->m_count);
memset(&tok->m_list[0], 0, sizeof(tok->m_list));
for (unsigned int i=0; i<tok->m_count; i++)
{
unsigned int tmp;
fread(&tmp, sizeof(unsigned int), 1, fp);
if (i < 128)
{
tok->m_list[i] = tmp;
}
dprintf("%d,", tmp);
}
dprintf("\n\n");
}// End ReadTokenIntList(..)
//--------------------------------------------------------------------------------
void ReadTokenFloatList(FILE* fp, stTokenFloatList* tok)
{
tok->m_token = TOKEN_FLOAT_LIST;
fread(&tok->m_count, sizeof(unsigned int), 1, fp);
dprintf("count: %d\n", tok->m_count);
memset(&tok->m_list[0], 0, sizeof(tok->m_list));
for (unsigned int i=0; i<tok->m_count; i++)
{
float tmp;
fread(&tmp, sizeof(float), 1, fp);
if (i < 128)
{
tok->m_list[i] = tmp;
}
dprintf("%.1f ,", tmp);
}
dprintf("\n\n");
}// End ReadTokenFloatList(..)
//--------------------------------------------------------------------------------
// THIS IS THE SPECIFICATION OF THE TEMPLATE .X BINARY LAYOUT, BUT WE'LL
// ONLY PARSE WHAT WE NEED!
//template : TOKEN_TEMPLATE name TOKEN_OBRACE
// class_id
// template_parts
// TOKEN_CBRACE
//
//template_parts : template_members_part TOKEN_OBRACKET
// template_option_info
// TOKEN_CBRACKET
// | template_members_list
//
//template_members_part : /* Empty */
// | template_members_list
//
//template_option_info : ellipsis
// | template_option_list
//
//template_members_list : template_members
// | template_members_list template_members
//
//template_members : primitive
// | array
// | template_reference
//
//primitive : primitive_type optional_name TOKEN_SEMICOLON
//
//array : TOKEN_ARRAY array_data_type name dimension_list
// TOKEN_SEMICOLON
//
//template_reference : name optional_name YT_SEMICOLON
//
//primitive_type : TOKEN_WORD
// | TOKEN_DWORD
// | TOKEN_FLOAT
// | TOKEN_DOUBLE
// | TOKEN_CHAR
// | TOKEN_UCHAR
// | TOKEN_SWORD
// | TOKEN_SDWORD
// | TOKEN_LPSTR
// | TOKEN_UNICODE
// | TOKEN_CSTRING
//
//array_data_type : primitive_type
// | name
//
//dimension_list : dimension
// | dimension_list dimension
//
//dimension : TOKEN_OBRACKET dimension_size TOKEN_CBRACKET
//
//dimension_size : TOKEN_INTEGER
// | name
//
//template_option_list : template_option_part
// | template_option_list template_option_part
//
//template_option_part : name optional_class_id
//
//name : TOKEN_NAME
//
//optional_name : /* Empty */
// | name
//
//class_id : TOKEN_GUID
//
//optional_class_id : /* Empty */
// | class_id
//
//ellipsis : TOKEN_DOT TOKEN_DOT TOKEN_DOT
void ReadTokenTemplateParts(FILE* fp, stTokenTemplate* tok)
{
unsigned short tokenType = 0;
while (tokenType != TOKEN_CBRACE)
{
int r =
(int)fread(&tokenType, sizeof(tokenType), 1, fp);
dprintf("Token Templates Part: %d\n", tokenType);
int pos = ftell(fp);
dprintf("file offset: %d\n", pos);
switch (tokenType)
{
case TOKEN_NAME:
{
stTokenName tt;
ReadTokenName(fp, &tt);
dprintf("%s\n", tt.m_name);
}
case TOKEN_LPSTR:
{
// Not sure this is right, but the example binary file I'm using does
// this, so I'll go with it for now.
// I'd have thought there would be a null terminated string here,
// but it seems to have a TOKEN_NAME following it.
r =
(int)fread(&tokenType, sizeof(tokenType), 1, fp);
if (tokenType != TOKEN_NAME)
{
dprintf("ERROR expecting TOKEN_NAME after TOKEN_LPSTR in template\n");
}
stTokenName tt;
ReadTokenName(fp, &tt);
dprintf("lpstr: %s\n", tt.m_name);
}
break;
default:
{
dprintf("Unknown token template type {%d}\n", tokenType);
break;
}
}
}
dprintf("TOKEN_CBRACE\n");
}// End ReadTokenTemplateParts(..)
//--------------------------------------------------------------------------------
void ReadTokenTemplate(FILE* fp, stTokenTemplate* tok)
{
tok->m_token = TOKEN_TEMPLATE;
unsigned short tokenType;
int r =
(int)fread(&tokenType, sizeof(tokenType), 1, fp);
if (tokenType != TOKEN_NAME)
{
dprintf("ERROR - Token Template Unknown syntax (expected TOKEN_NAME)\n");
}
ReadTokenName(fp, &tok->m_tokName);
dprintf("template name: %s\n", tok->m_tokName.m_name);
r =
(int)fread(&tokenType, sizeof(tokenType), 1, fp);
if (tokenType != TOKEN_OBRACE)
{
dprintf("ERROR - Token Template Unknown syntax (expected TOKEN_OBRACE\n");
}
dprintf("TOKEN_OBRACE\n");
r =
(int)fread(&tokenType, sizeof(tokenType), 1, fp);
if (tokenType != TOKEN_GUID)
{
dprintf("ERROR - Token Template Unknown syntax (expected TOKEN_GUID\n");
}
ReadTokenGUID(fp, &tok->m_tokGUID);
ReadTokenTemplateParts(fp, tok);
}// End ReadTokenTemplate(..)
//--------------------------------------------------------------------------------
bool ReadTokens(FILE * fp)
{
unsigned short tokenType;
int r =
(int)fread(&tokenType, sizeof(tokenType), 1, fp);
if (r==0)
{
return false;
}
dprintf("TokenType: %d\n", tokenType);
switch (tokenType)
{
case TOKEN_NAME:
{
stTokenName tok;
ReadTokenName(fp, &tok);
}
break;
case TOKEN_STRING:
{
stTokenString tok;
ReadTokenString(fp, &tok);
}
break;
case TOKEN_INTEGER:
{
stTokenInt tok;
ReadTokenInt(fp, &tok);
}
break;
case TOKEN_GUID:
{
stTokenGUID tok;
ReadTokenGUID(fp, &tok);
}
break;
case TOKEN_INTEGER_LIST:
{
stTokenIntList tok;
ReadTokenIntList(fp, &tok);
}
break;
case TOKEN_FLOAT_LIST:
{
stTokenFloatList tok;
ReadTokenFloatList(fp, &tok);
}
break;
case TOKEN_OBRACE:
{
dprintf("{\n");
}
break;
case TOKEN_CBRACE:
{
dprintf("}\n");
}
break;
case TOKEN_TEMPLATE:
{
stTokenTemplate tok;
ReadTokenTemplate(fp, &tok);
}
break;
default:
{
dprintf("Uknown Token Type\n");
// Once we reach an unknown type we'll just bail out here, usually
// the templates or something
return false;
}
}
return true;
}// End ReadTokens(..)
/********************************************************************************/
// Program Entry Point
void main()
{
dprintf("Opening binary directx .x binary file (racer.x)\n\n");
FILE* fp = fopen("racer.x", "rb");
// Large temp buffer
stXHeader h;
// Lets read in the first 4 characters of the file are, and see what
// they look like
int r = // The total number of items readed is returned.
(int)fread (&h, // void * buffer,
sizeof(h), // size
1, // count,
fp ); // FILE * stream
dprintf("stXHeader: \n");
// Signature
dprintf("\tSig: %c%c%c%c\n", h.m_sig[0], h.m_sig[1], h.m_sig[2], h.m_sig[3]);
// Minor Version
dprintf("\tMinor: %c%c\n", h.m_minor[0], h.m_minor[1]);
// Majour Version
dprintf("\tMajour: %c%c\n", h.m_major[0], h.m_major[1]);
// Type
dprintf("\tType: %c%c%c%c\n", h.m_type[0], h.m_type[1], h.m_type[2], h.m_type[3]);
// Accuracy
dprintf("\tAccuracy: %c%c%c%c\n", h.m_accuracy[0],
h.m_accuracy[1],
h.m_accuracy[2],
h.m_accuracy[3]);
bool doRead = true;
while (doRead)
{
doRead = ReadTokens(fp);
}
dprintf("\nDone - Closing File\n\n");
fclose(fp);
}// End main()
/* output.txt
Opening binary directx .x binary file (racer.x)
stXHeader:
Sig: xof
Minor: 03
Majour: 02
Type: bin
Accuracy: 0032
TokenType: 1
size: 6
name: Header
TokenType: 10
{
TokenType: 6
count: 3
1,0,0,
TokenType: 11
}
TokenType: 1
size: 5
... etc etc
name: ViewpointCopyright
template name: ViewpointCopyright
TOKEN_OBRACE
Data1: 0xcd16f302, Data2: 0x9bba, Data3: 0x11d0, Data4: 0xb1, 0xc0, 0x00, 0xc0, 0x4f, 0xc3, 0x6d, 0x46,
Token Templates Part: 49
file offset: 33904
size: 11
... etc etc
Done - Closing File
*/
|
|