Versioning and Storing Additional User Data

Versioning

As discussed in the previous section, one way for you to check if the file contains custom HSF objects that you know/care about is to always use custom opcode handlers, which then check during the reading process to see if there is user data. However, there is one deliberate flaw to the example approach and its corresponding sample code. If the code sees a custom chunk of data following the default HTK_Shell object (by noticing a #TKE_Start_User_Data opcode), it simply goes ahead and reads the data, assuming that it was data written out by our custom shell handler. However, what if the file was written out by a custom handler that was not ours?! In this case, we wouldn’t understand the information and don’t care about it. However, the sample code does not properly check if the data is something that we know or care about. Because it is assuming a specific amount of user data, and this is an unsafe assumption, the code is flawed.

One potential solution is to add another stage during the writing process: after writing out the #TKE_Start_User_Data opcode and the number of bytes of custom data, we could also write out some special value which ‘marks’ the custom data as ‘our’ custom data. Then, during reading, we would check that special value to confirm if it was our data. However, this solution is a bit cumbersome since it means that our custom logic would always need to be executed, and to properly handle the case, we’d also have to either A) peek at the data up through the special value and then return from the function (so that the default toolkit will skip the custom data) or B) manually skip through the custom data ourselves by utilizing the ‘number of bytes’ information.

A better solution would be to store some type of additional versioning information in the beginning of the file which could be checked once, and then we would create and register our custom HSF object handlers only if the file was verified to be a custom version that we created with our custom toolkit. Recalling that the first opcode in an HSF file is always a #TKE_Comment opcode (with contents that are specifically formatted to denote file version information), you could export another #TKE_Comment opcode immediately after the first one with contents that contain additional version information. For example:

<TKE_Comment> standard version information; contents:  HSF V11.00
<TKE_Comment> custom version information; contents:  SuperCAD V2.10
<data opcode>
          .
          .
          .
<data opcode>
<TKE_Termination>

The following section details how additional information could be added at the beginning of the file (prior to default HSF objects) as well as at the end of the file.

Storing Additional User Data

In addition to providing support for attaching/retrieving user data to/from default HSF objects (by enabling overloading of the Write and Read methods of opcode handlers), the HOOPS/Stream Toolkit also provides general support for exporting user data via the TK_XML, TK_User_Data and TK_URL opcode handlers, which export the #TKE_XML, #TKE_Start_User_Data, and #TKE_URL opcodes, respectively. This gives developers the ability to store discrete chunks of user data that may (or may not) be associated with the HSF objects. The TK_XML opcode handler would be used to store or retreive XML data, and the TK_User_Data opcode handler would be used to store or retrieve custom binary data. The TK_URL opcode handler provides informational links corresponding to data (as opposed to #TKE_External_Reference which provides additional content).

When writing out user data within the Write method of your custom TK_User_Data object, be sure to review the process of formatting user data.

To handle import/export of user data, you will need to register a custom opcode handler for the #TKE_Start_User_Data opcode. This is because the toolkit’s default handler (TK_User_Data) simply skips over the user data that is read in. (Remember that custom opcode handlers such as My_HTK_Shell described in the previous section typically only handle user data that is appended to a default HSF object. If you are adding discrete chunks of user data to the file, then you must read or write that data with an entirely new TK_User_Data handler) The following steps are involved:

**1. Define a new class derived from ``TK_User_Data``(which we'll call ``TK_My_User_Data``) that overloads the ``Write`` and ``Read`` methods to process the extra user data.** Example:
#include "object.h"

class TK_My_User_Data : public TK_User_Data
{
        protected:

                int    my_stage; // denotes the current processing stage


        public:

                TK_My_User_Data(unsigned char opcode) : TK_User_Data(opcode) {}

                // Within Read(), we may need to verify that the user data is 'our'
                // user data.  As previously noted, one approach is to write out
                // versioning information at the beginning of the file.
                // If it is not our custom version of the file, we would NOT
                // even register this custom user data opcode handler; instead
                // we would allow the default TK_User_Data handler to take care of
                // the TKE_Start_User_Data opcode by simply skipping over any user data
                virtual TK_Status   Read (HStreamFileToolkit & tk) alter;

                virtual TK_Status   Write (HStreamFileToolkit & tk) alter;
};

2. Instruct the toolkit to use our custom user data opcode handler in place of the default handler by calling ``SetOpcodeHandler``. We specify the type of opcode that we want to replace, and pass in a pointer to the new opcode handler object.

tk->SetOpcodeHandler (TKE_Start_User_Data, new TK_My_User_Data(TKE_Start_User_Data));

This will also cause the toolkit to delete it’s default handler object for the #TKE_Start_User_Data opcode. Note: As the HOOPS/Stream Reference Manual points out, all opcode handler objects stored in the HStreamFileToolkit object will be deleted when the HStreamFileTookit object is deleted. Therefore, we would not delete the TK_My_User_Data object created in the above example.

Custom handling of the #TKE_XML opcode would be similar to the above, but you would instead register a custom opcode handler for the XML opcode that is derived from TK_XML.

Using Prewalk and Postwalk Handers

The 3dGS-specific classes allow the developer to setup special opcode handlers, called prewalk and postwalk handlers. A prewalk handler will be called prior to the traversal of the HOOPS/3dGS scene-graph, while a postwalk handler will be called after the traversal of the scene-graph is complete. This allows the developer to add chunks of custom data at the beginning or end of the file. These handlers are set by calling BStreamFileToolkit::SetPrewalkHandler and BStreamFileToolkit::SetPostwalkHandler. Multiple pre/post-walk handlers can be set by calling these functions multiple times. (The order that the handlers will be called is depending on the order in which the functions were called, or FIFE for ‘first-invoked, first executed’

It may be desirable to export the #TKE_View opcode prior to the scene-graph information, so that reading applications know the extents of the scene in advance. This can therefore be achieved by setting the prewalk handler to be a custom opcode-handler that is registered to handle the #TKE_View opcode:

tk->SetPrewalkHandler(new TK_My_Initial_View);

If we want our application to handle the #TKE_View opcode during reading, then we’d also register our custom opcode handler to handle the #TKE_View opcode:

tk->SetOpcodeHandler(TKE_View, new TK_My_Initial_View);

The sample definition of TK_My_Inital_View is contained in HTK_My_View.h, and the implementation is located in HTK_My_View.cpp. Note that it is actually a custom HTK_Camera class that has been registered to handle the #TKE_View opcode. (Per the opcode registration list that was previously discussed in the previous page, the HTK_Camera class is the class designated to handle the #TKE_View opcode.)