www.xbdev.net xbdev - software development
Tuesday September 17, 2019
home | about | contact | Donations

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
*/

 

 

 

 

 

 

 

 

Code Snippet
--to do

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 Visitor: 9534626  { 209.237.238.175 } Copyright (c) 2002-2017 xbdev.net - All rights reserved.
Designated tutorial and software are the property of their respective owners.