Loading .red Files

You gain some nice benefits when using the .red file format to load your data. RED files can be read both from disk or from memory depending on the stream you pass to the loading method. This is quite useful as it authorizes, for examples, inter-process in-memory data exchange or accessing to .red files from other archive formats. The .red file loader is multi-threaded meaning that you’ll get quite good performances at reloading huge data files. Data are loaded using the RED::IREDFile API and later accessed using the RED::IDataManager API. Both will be extensively detailed in the upcoming sections.

Loading a .red File

The .red file API is exposed through the RED::IREDFile interface. To create an instance of a .red file, you need to use the RED::Factory:

RED::Object* red_file = RED::Factory::CreateInstance( CID_REDFile );

and get back its interface with:

RED::IREDFile* ired_file = red_file->As< RED::IREDFile >();

Now we can load a .red file using our object:

Case of File on Disk

// iresmgr is a pointer to the RED::IResourceManager interface.
RED::StreamingPolicy policy;
RED::FileHeader fheader;
RED::FileInfo finfo;
RED::Vector< unsigned int > context;

RC_TEST( ired_file->Load( "./my_file.red", iresmgr->GetState(), policy, fheader, finfo, context ) );

More Generic Case of a RED::IStream

// iresmgr is a pointer to the RED::IResourceManager interface.
// stream is a pointer to a RED stream.
RC_TEST( ired_file->Load( *stream, iresmgr->GetState(), policy, fheader, finfo, context ) );

Every .red file starts with a header (RED::FileHeader) which is returned by the RED::IREDFile::Load method. This header says us more about the version of HOOPS Luminate used to produce the file and the source application. Along with that header, an optional RED::FileInfo structure may also be provided. This contains information about how to process/render the loaded data in a way similar to how it was processed/rendered in the source application. You should considered this more as hints than strict recommendations (after all, it’s optional).

Loaded data are stored into contexts in the RED data manager which can be retrieved from the RED resource manager using RED::IResourceManager::GetDataManager. This manager implements the RED::IDataManager interface to get access to stored data. A context corresponds to a whole file or to a single animation track in a file. Hence, each animation track contained in the input .red file will be loaded into a separate context (see Saving .red Files under the section Recording Animation Tracks for details about multiple animation tracks). So, when the loading is successful, the context array is returned with at least one new entry. Contexts are logical views of the content of a .red file. They let you access to parts of .red file even after it may have been merged with other .red files into the RED::IDataManager.

Accessing to Loaded Data

With the list of contexts as returned by the RED::IREDFile::Load method and the access to the RED::IDataManager interface, you can retrieve any loaded data.

Accessing to Loaded Data

Here is an example of access to all the viewpoints loaded from a file:

// iresmgr is a pointer to the RED::IResourceManager interface.
// context is the vector of loaded contexts.

RED::IDataManager* idatamgr = iresmgr->GetDataManager()->As< RED::IDataManager >();

for( unsigned int c = 0; c < context.size(); ++c )
{
    // Get the number of viewpoints in the current context.
    unsigned int vcount;
    RC_TEST( idatamgr->GetViewpointsCount( vcount, context[c] ) );

    for( unsigned int v = 0; v < vcount; ++v )
    {
        RED::Object* viewpoint;
        RC_TEST( idatamgr->GetViewpoint( viewpoint, context[c], v, iresmgr->GetState() ) );
    }
}

Data stored in the RED data manager are not managed by the RED resource manager. Therefore, to destroy loaded data, you need to call a dedicated method in RED::IDataManager. The example below illustrates the destruction of an image, a full context or the whole stored data:

// image is a pointer to a loaded image.
// image_context is the context of the loaded image.

// Delete the image.
RC_TEST( idatamgr->ReleaseData( image_context, image, iresmgr->GetState() ) );

// Delete all the other image context data.
RC_TEST( idatamgr->ReleaseContext( image_context, false, iresmgr->GetState() ) );

// Delete all the contexts.
RC_TEST( idatamgr->Release( iresmgr->GetState() ) );

RED files can be saved encrypted for better security in file exchanges.

How to Load Encrypted Files?

You must first determine if a given file is encrypted or not. To do so, just call the RED::IREDFile::Load method with no encryption key and check for the returned code. If it’s RED_OK, then everything went fine and the data were successfully loaded (and were not encrypted). If RED_ENCRYPTED_FILE is returned, then the file is encrypted and the user must be asked for the right encryption key. Once he supplied it, RED::IREDFile::Load can be called again with the key and should return RED_OK. Otherwise, another RED_RC code is returned describing the issue.

Load an Encrypted .red File

Here is how to know if a file is encrypted or not:

// iresmgr is a pointer to the RED::IResourceManager interface.
RED::Object* red_file = RED::Factory::CreateInstance( CID_REDFile );
RED::IREDFile* ifile = red_file->As< RED::IREDFile >();

RED::StreamingPolicy policy;
RED::FileHeader fheader;
RED::FileInfo finfo;
RED::Vector< unsigned int > context;

RED_RC rc = ifile->Load( "./my_file.red", iresmgr->GetState(), policy, fheader, finfo, context );
if( rc == RED_OK )
{
    // File is not encrypted.
}
else if( rc == RED_ENCRYPTED_FILE )
{
    // File is encrypte: prompt the user for the right encryption key.
}
else
{
    // An error occured: look at rc to identify the issue.
}

The key is not stored into the encrypted file and people wanting to read the file must have previously received the key through another mean.

// iresmgr is a pointer to the RED::IResourceManager interface.
RED::Object* red_file = RED::Factory::CreateInstance( CID_REDFile );
RED::IREDFile* ifile = red_file->As< RED::IREDFile >();

RED::StreamingPolicy policy;
RED::FileHeader fheader;
RED::FileInfo finfo;
RED::Vector< unsigned int > context;

RED::String encryption_key = "encryption key used to encrypt the source file";

// Loading and decrypting are done simultaneously by the next call.
RC_TEST( ifile->Load( "./my_file.red", iresmgr->GetState(), policy, fheader, finfo, context, 0, encryption_key ) );

If a wrong encryption key is supplied, you’ll most of the time end with a RED_RC error returned by the loading call. As the input key can’t be verified, the decrypting process will produce an error at any random time during the loading and must be considered as not meaningful.

Handling Unknown Data

The .red file format can be extended by the user to integrate custom application data or complementary information. It’s also a living standard and new chunks of data can be added to new versions of the file format. Hence, HOOPS Luminate offers a mechanism to handle cases where unknown data are encountered while loading a .red file (file is newer than the loader or custom chunks are found). This is known as the unknown chunk policy and can be set to the RED::StreamingPolicy by calling RED::StreamingPolicy::SetUnknownChunkPolicy.

By default, the policy is set to RED::StreamingPolicy::UCP_ABORT which means that an unknown chunk will make the loading process to return with an error. But the user can choose to perform any other action depending on the chunk actually encountered. In that case, the RED::StreamingPolicy::UCP_ASK policy can be set and a user callback will be called instead of aborting.

The Saving and Reloading a Custom Container tutorial illustrates how to reload a .red file saved with a custom chunk.