Enumerating Data Objects
At this point, you have opened your .X file and registered the templates
you'll be using (such as the DirectX standard templates). The enumeration object
has been created, and you are now ready to pull data from the .X file.
In its current state, the IDirectXFileEnumObject object you created points to
the first data object in the file, which is typically the Header object. All
top−level data objects are siblings of the Header object (or the first object in
the file). Each data object you read might contain embedded objects (child
objects) or references to other data objects; you can query for both of these.
The enumerator object itself doesn't handle a data object's data. Rather, you
need to obtain a data object interface, called IDirectXFileData, to access the
data.
Applications use the methods of the IDirectXFileData
interface to build or to access the immediate hierarchy of the data object.
Template restrictions determine the hierarchy. Data types allowed by the
template are called optional members. The optional members are not required, but
an object might miss important information without them. These optional members
are saved as children of the data object. The children can be another data
object, a reference to an earlier data object, or a binary object. Deprecated.
IDirectXFileData Members
Method |
Description |
IDirectXFileData::AddBinaryObject |
Creates a binary object and adds it as a child
object. Deprecated. |
IDirectXFileData::AddDataObject |
Adds a data object as a child object.
Deprecated. |
IDirectXFileData::AddDataReference |
Creates and adds a data reference object as a
child object. Deprecated. |
IDirectXFileData::GetData |
Retrieves the data for one of the object's
members or the data for all members. Deprecated. |
IDirectXFileData::GetNextObject |
Retrieves the next child data object, data
reference object, or binary object in the DirectX file. Deprecated. |
IDirectXFileData::GetType |
Retrieves the GUID of the object's template.
Deprecated. |
Remarks
The GUID for the IDirectXFileData interface is
IID_IDirectXFileData.
The LPDIRECTXFILEDATA type is defined as a pointer to
this interface.
typedef interface IDirectXFileData *LPDIRECTXFILEDATA;
To obtain an IDirectXFileData interface, you need to call the
IDirectXFileEnumObject::GetNextDataObject function.
Retrieves the next top-level object in the DirectX
file. Deprecated.
HRESULT GetNextDataObject(
LPDIRECTXFILEDATA * ppDataObj
);
Parameters
- ppDataObj
- [out] Address of a pointer to an IDirectXFileData
interface, representing the returned file data object.
Return Values
If the method succeeds, the return value is DXFILE_OK.
If the method fails, the return value can be one of the following values:
DXFILEERR_BADVALUE, DXFILEERR_NOMOREOBJECTS
Remarks
Top-level objects are always data objects. Data
reference objects and binary objects can only be children of data objects.
With only one parameter, the GetNextDataObject is a breeze to use. You just
need to instance an IDirectXFileData object and use it in your call to
GetNextDataObject.
IDirectXFileData *pData;
HRESULT hr = pEnum−>GetNextDataObject(&pData);
Notice how I'm saving the return value of the GetNextDataObject call? If the
return code is an error(which you can check by using the FAILED macro), it
signifies that the enumeration is complete. If the call to GetNextDataObject is
successful, then you have yourself a spiffy new interface for accessing the data
object's data!
Before you get into working with the object's data, let's finish the
discussion on enumeration. So far, you've been able to enumerate the first data
object in a file and retrieve its data interface. What do you do when you want
to go to the next data object in the .X file or query for embedded data objects?
Once you're finished with a data interface, you need to free it to go to the
next data object. Simply calling IDirectXFileData::Release will free the data
interface, and repeating the call to IDirectXFileEnumObject::GetNextDataObject
will get the next enumerated sibling (top−level) data object for you. You can
wrap the entire enumeration of siblings (grabbing their respective data
interfaces)
into a code bite such as this one:
while(SUCCEEDED(pEnum−>GetNextDataObject(&pData))) {
// Do something with pData data object
// Free the data interface in order to continue
pData−>Release();
}
All that's left is to add the ability to query for child (lower−level) data
objects, and to allow those child objects to be enumerated and accessed. To
query for a child data object, you use the IDirectXFileData::GetNextObject
function to first see whether a data object contains any embedded objects.
Retrieves the next child data object, data reference
object, or binary object in the DirectX file. Deprecated.
HRESULT GetNextObject(
LPDIRECTXFILEOBJECT * ppChildObj
);
Parameters
- ppChildObj
- [out, retval] Address of a pointer to an
IDirectXFileObject interface, representing the returned child object's file
object interface.
Return Values
If the method succeeds, the return value is DXFILE_OK.
If the method fails, the return value can be one of the following values:
DXFILEERR_BADVALUE, DXFILEERR_NOMOREOBJECTS.
Remarks
To determine the type of object retrieved, use
QueryInterface to query the retrieved object for support of IDirectXFileData,
IDirectXFileDataReference, or IDirectXFileBinary interfaces. The interface
supported indicates the type of object (data, data reference, or binary).
This is another simple function with only one parameter−the pointer to an
IDirectXFileObject interface. If the call to GetNextObject is successful, then
you need to process the child data object. Once you've done that, you can free
it (by calling Release) and continue calling GetNextObject until it returns an
error code, which signifies that no more child objects remain.
You can wrap the continuous calling of GetNextObject into a small loop, as I
have done here.
IDirectXFileObject *pObject;
while(SUCCEEDED(pData−>GetNextObject(&pObject))) {
// A child data object exists, need to query for it
// Free file object interface
pObject−>Release();
}
Once you have a valid IDirectFileObject interface (after the call to
GetNextObject), you can quickly determine which child data object it is
currently enumerating (using the techniques coming up in the next section).
There's a slight snag, however. A data object can either be referenced or
instanced, and the way you access the object varies a bit depending on which
type it is.
For instanced objects (those defined normally in an .X file), you can query
the IDirectXFileObject for an IDirectXFileData interface.
IDirectXFileData *pSubData;
// Check if child object is instanced (fails if not)
if(SUCCEEDED(pObject−>QueryInterface( IID_IDirectXFileData, (void**)&pSubData)))
{
// Child data object exists, do something with it.
// Free data object
pSubData−>Release();
}
Using what you've just learned, you can query a child data object's
IDirectXFileData object for its own embedded child objects.
As for referenced data objects, you need to first query for the
IDirectXFileDataReference object and resolve the reference into an
IDirectXFileData object.
Applications use the methods of the
IDirectXFileDataReference interface to support data reference objects. A data
reference object refers to a data object that is defined earlier in the file.
This enables you to use the same object multiple times without repeating it in
the file. Deprecated.
IDirectXFileDataReference Members
Method |
Description |
IDirectXFileDataReference::Resolve |
Resolves data references. Deprecated. |
Remarks
After you have determined that an object is a data
reference object, use the IDirectXFileDataReference::Resolve method to
retrieve the referenced object defined earlier in the file. For information
about how to identify a data reference object, see the IDirectXFileData
interface.
The GUID for the IDirectXFileDataReference interface is
IID_IDirectXFileDataReference.
The LPDIRECTXFILEDataReference type is defined as a
pointer to this interface.
typedef interface IDirectXFileDataReference *LPDIRECTXFILEDATAREFERENCE;
IDirectXFileDataReference::Resolve
Resolves data references. Deprecated.
HRESULT Resolve(
LPDIRECTXFILEDATA * ppDataObj
);
Parameters
- ppDataObj
- [out, retval] Address of a pointer to an
IDirectXFileData interface, representing the returned file data object.
Return Values
If the method succeeds, the return value is DXFILE_OK.
If the method fails, the return value can be one of the following values:
DXFILEERR_BADVALUE, DXFILEERR_NOTFOUND.
The following code will query and resolve the referenced data object for you.
Tip If an instanced data object does not
exist when you query for it, the call to QueryInterface will fail. This is a
quick way to tell the type of the data object. The same goes for referenced
objects−the query will fail, meaning the object is not referenced.
IDirectXFileDataReference *pRef;
IDirectXFileData *pSubData;
// Check if the data object is referenced (fails if not)
if(SUCCEEDED(pSubObj−>QueryInterface(IID_IDirectXFileDataReference,
(void**)&pRef))) {
// A data object reference exists. Resolve the reference
pRef−>Resolve(&pSubData);
// Do something with data object
// Release the interfaces used
pRef−>Release();
pSubData−>Release();
}
Would you believe me if I told you that the hardest part is over? Enumerating
the data objects and child objects is simple, and if that's as hard as it gets,
then you're in for an easy ride! To make your programming job much easier, I
suggest wrapping up the entire enumeration of data objects into two simple
functions.
The first function (called Parse) will open an .X file, create the
enumeration object, and enumerate all top−level data objects. The function will
then take each enumerated object and pass it to the second function
(ParseObject), which will process the data object data based on its template
type and scan for embedded child data objects. The ParseObject function will
call itself using any child objects it finds, thus processing a child's embedded
objects.
The code for the Parse function follows.
// Need to include rmxftmpl.h and rmxfguid.h
BOOL Parse(char *Filename)
{
IDirectXFile *pFile = NULL;
IDirectXFileEnumObject *pEnum = NULL;
IDirectXFileData *pData = NULL;
// Create the enumeration object, return on error
if(FAILED(DirectXFileCreate(&pFile)))
return FALSE;
// Register the standard templates, return on error
if(FAILED(pFile−>RegisterTemplates((LPVOID)D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES)))
return FALSE;
// Create the enumeration object, return on error
if(FAILED(pDXFile−>CreateEnumObject((LPVOID)Filename, DXFILELOAD_FROMFILE, &pEnum))) {
pFile−>Release();
return FALSE;
}
// Loop through all top−level data objects
while(SUCCEEDED(pEnum−>GetNextDataObject(&pData))) {
// Parse the data object by calling ParseObject
ParseObject(pData);
// Release the data object
pData−>Release();
}
// Release used COM objects
pEnum−>Release();
pFile−>Release();
return TRUE;
}
The Parse function doesn't hold back any punches, and it certainly isn't
overly complicated. I have already explained everything in the function, so
there's no need to recap here. Instead, move on to the ParseObject function,
which takes a data object and queries it for child objects.
void ParseObject(IDirectXFileData *pData)
{
IDirectXFileObject *pObject = NULL;
IDirectXFileData *pSubData = NULL;
IDirectXFileDataReference *pRef = NULL;
// Scan for embedded objects
while(SUCCEEDED(pData−>GetNextObject(&pObject))) {
// Look for referenced objects
if(SUCCEEDED(pObject−>QueryInterface(IID_IDirectXFileDataReference, (void**)&pRef))) {
// Resolve the data object
pRef−>Resolve(&pSubData);
// Parse the object by calling ParseObject
ParseObject(pSubData);
// Free interfaces
pSubData−>Release();
pRef−>Release();
}
// Look for instanced objects
if(SUCCEEDED(pObject−>QueryInterface(IID_IDirectXFileData, (void**)&pSubData))) {
// Parse the object by calling ParseObject
ParseObject(pSubData);
// Free the object interface
pSubData−>Release();
}
// Free the interface for next object to use
pObject−>Release();
}
}
Again, the ParseObject function doesn't contain anything new. The one thing
you'll notice about Parse and ParseObject is that they don't really do anything
except enumerate every data object in an .X file. When it comes time to work
with an object's data, what do you do?