Main Page   Modules   Alphabetical List   Data Structures   Data Fields  

TOC entry
[Tutorial 6 - RWS files and Tables of Contents]

An RtTOCEntry obtained from the TOC describes it's associated chunk in the stream.

A RenderWare Graphics's chunk ID is incorporated into the RtTOCEntry to identify the chunk that the entry refers to.

An offset, in bytes, from the beginning of the stream to the entry's chunk header is also provided in the RtTOCEntry. This enables the application to read the stream non-sequentially to find particular chunks. Since this offset is from the beginning of the stream, the stream must be closed and re-opened in order to use the offset with RwStreamSkip. Note that after the skip has been applied, the chunk should be read as if it had been found sequentually.

Since there may be multiple instances of the same chunk ID in the TOC a method of distinguishing one instance from another is required. The RtTOCEntry also carries a GUID and a gid (game ID) that can be used to this end. The RenderWare Graphics exporters assign GUIDs to assets when they are exported and their value displayed in the output window.

In this tutorial we define a callback function that takes an RtTOCEntry and either accepts or declines it. If the entry is accepted, the chunk can be read. If it is declined, the next RtTOCEntry is processed.

In the utils.h file, define this callback function type

    typedef RwBool (* AcceptTOCEntryCB)( RtTOCEntry *tocEntry );
The callback takes an RtTOCEntry, queries it's contents and returns TRUE to accept it, or FALSE to decline it.

Now implement the function RwsLoadWorldFromTOC in utils.c:

    RpWorld *
    RwsLoadWorldFromTOC( RwChar *filename,
                         RtTOC *toc,
                         AcceptTOCEntryCB acceptTOCEntry )
    {
        RpWorld     *world;
        RwInt32     idx;
        world = (RpWorld *)NULL;
        for ( idx = 0; idx < RtTOCGetNumEntries( toc ); idx += 1 )
        {
            RtTOCEntry  *tocEntry;
            tocEntry = RtTOCGetEntry( toc, idx );
            /* we're only interested in worlds */
            if ( rwID_WORLD != tocEntry->id )
            {
                continue;
            }
            /* is this the world we want? */
            if ( FALSE != acceptTOCEntry( tocEntry ) )
            {
                RwChar              *pathName;
                RwStream            *stream;
                RwChunkHeaderInfo   chunkHdrInfo;
                pathName = RsPathnameCreate( filename );
                if ( NULL == pathName )
                {
                    return (RpWorld *)NULL;
                }
                stream = RwStreamOpen( rwSTREAMFILENAME,
                                       rwSTREAMREAD,
                                       pathName );
                /* skip through the stream using the TOC entry's offset */
                RwStreamSkip( stream, tocEntry->offset );
                /* read the chunk as if we'd found it sequentially */
                if ( NULL != RwStreamReadChunkHeaderInfo( stream,
                                                          &chunkHdrInfo ) )
                {
                    world = RpWorldStreamRead( stream );
                }
                RwStreamClose( stream, NULL );
                RsPathnameDestroy( pathName );
                break;
            }
        }
        return world;
    }
adding the appropriate function prototype in utils.h.

RwsLoadWorldFromTOC loops over the entries in the TOC, ignores non-world chunks, and for each world chunk, passes the appropriate TOC entry to the application specified callback. If the TOC entry is accepted, the world is subsequently read by opening the stream and skipping to the appropriate position.

The chunk's header can be read either by RwStreamFindChunk with the rwID_WORLD argument or, as shown above, by RwStreamReadChunkHeaderInfo that assumes the stream is positioned correctly to find a chunk header.

Now in main.c we need to write the TOC entry accept callback function, and use the TOC related functions to read the world from the stream. The callback function for this tutorial is very simple as the .rws file contains just a single world, hence the first one encountered is accepted.

    static RwBool
    acceptFirstWorld( RtTOCEntry *tocEntry )
    {
        return TRUE;
    }
Now replace the use of RwsLoadWorld in Initialize3D with the RwsLoadWorldFromTOC function.

    Initialize3D(void *param)
    {
        RtTOC   *toc;
        if( !RsRwInitialize(param) )
        {
            RsErrorMessage(RWSTRING("Error initializing RenderWare."));
            return FALSE;
        }
        Charset = RtCharsetCreate(&ForegroundColor, &BackgroundColor);
        if( Charset == NULL )
        {
            RsErrorMessage(RWSTRING("Cannot create raster charset."));
            return FALSE;
        }
        /* Get the TOC from the RWS file */
        toc = RwsLoadTOC( "models/tutorial5p.rws" );
        if ( NULL == toc )
        {
            /* no TOC, so just load the first world found */
            World = RwsLoadWorld( "models/tutorial5p.rws" );
        }
        else
        {
            World = RwsLoadWorldFromTOC( "models/tutorial5p.rws",
                                         toc,
                                         acceptFirstWorld );
            RtTOCDestroy( toc );
        }
        if( World == NULL )
        {
            RsErrorMessage(RWSTRING("Cannot create world."));
            return FALSE;
        }
        /* Create the lights */
        CreateLights();
Note that the RwsLoadWorld function is still used if the TOC, for any reason, is not present in the .rws file.

Compile the code. Before you can link this project, you will need to add the rttoc.lib library to the project.

Put a breakpoint on the call to RwsLoadWorldFromTOC. Debug the application to test that the TOC loading code is working.

Next...


Criterion Software © 1993-2004 Criterion Software Limited. All rights reserved. Built Thu Feb 12 13:47:00 2004. Send Feedback