The Marmalade resource manager

Most bitmap art packages are capable of saving images in a number of different file formats, but we really need access to the actual bitmap data itself, which may well be stored in a compressed format with any particular file format.

Marmalade makes the task of loading images simple by way of the IwResManager API. This API relies upon the ITX file format we have just discussed, and is not just limited to loading images. It can also be used to load in data such as 3D models and animations, and we can also use it to keep track of our own custom classes.

Note

Earlier we had to create our own instance of CIwTextParserITX in order to parse an ITX file. IwResManager creates its own instance of CIwTextParserITX when it needs it, so we don't need to worry about creating our own instance.

Adding IwResManager to a project

To make the IwResManager API available to a project, all that needs to be done is to add iwresmanager to the subprojects list in the MKB file.

To initialize the API just add a call to IwResManagerInit, which will create a singleton instance of the Marmalade resource manager class CIwResManager. This class is used to load, free, and of course access our projects resources, whatever they may be. The singleton can be accessed using the function IwGetResManager.

When our project terminates we should call IwResManagerTerminate, which will destroy the resource manager singleton and any resources it may still have loaded in memory.

Specifying resources with a GROUP file

Marmalade allows us to collect different types of resources together into a resource group. We are free to mix images, sounds, 3D models, and any other data types we might need to use.

Why would we want to group resources together? Well, let's say you are writing a game with a number of different levels. Each level will have some common resources (for example, the player graphics) but might have unique elements specific to that level, so it would make sense to only have these resources in memory when the level is being played. You could therefore create one resource group for the player graphics, and individual ones for each level.

In order to load a resource group into our program, we first need to create a GROUP file. A GROUP file is actually an ITX file with the extension .group that allows us to list all the resources we want to gather together.

Let's start by taking a look at a sample GROUP file:

CIwResGroup
{
  name  "game_resources"

  "./images/titlescreen.png"
  "./sounds/sounds.group"
  "./levels/levels.itx"
}

The first line of this file is defining a new CIwResGroup class instance, which is the class used to implement a resource group, and the first thing we do inside the curly braces of the definition is to give the resource group a name. This name will be used later to allow us to access the resource group.

Note

A GROUP file should only contain a single CIwResGroup definition. The Marmalade SDK documentation states that behavior is undefined should you specify more than one. In practice this is not a problem since the GROUP file is the lowest level block of resources that can be loaded at a time, so there would be no real benefit in specifying more than one CIwResGroup anyway.

The remaining lines of the example definition specify the resources we want to include in this group, and most often these will just be filenames for the resources in question. As we progress through this book we will see some extra functionality that the group file provides us with, but for now we'll just concentrate on the main task of loading resources.

In the example, we are specifying three files that we want to be part of this resource group. The first is a bitmapped image saved in the PNG file format. The next resource is a reference to another GROUP file. When this GROUP file is loaded, the sounds.group file will be loaded into memory as well.

The final file we are including is levels.itx, which is a standard ITX file and would be used to create instances of our own classes.

Loading groups and accessing resources

To load a GROUP file in our program, we do the following:

CIwResGroup* pResGroup;
pResGroup = IwGetResManager()->LoadGroup("groupfile.group");

This will look in the project's data directory for the specified GROUP file, and then load it into memory. The LoadGroup method returns a pointer to the CIwResGroup instance that was created, which we can store away somewhere so we can release the resource group and all its resources later.

With the resource group in memory, we can access the individual resources in one of two ways. The first way is to ask the CIwResGroup instance itself to locate a particular resource for us. Here's how we do this:

CIwResource* pResource;
pResource = pResGroup->GetResNamed(name, type, flags);

In the call to GetResNamed, the name parameter is a null terminated string containing the name of the resource we want to access. This is the value that is specified using the name attribute in an ITX file. If no name value is explicitly specified, the name of the first resource encountered in the GROUP file (minus any extension) will be used for the name. In the example GROUP file in the previous section this name would become titlescreen, since the first resource in the file is the titlescreen.png file.

The type parameter indicates the class of the resource that we are trying to locate. This parameter is also a string and is simply the class name of the resource type.

Finally there is the flags parameter that we can normally leave out entirely as it defaults to a value of zero. There are various flags we can use that alter the way the search for our resource is performed. For example, IW_RES_PERMIT_NULL_F will prevent an assert from being fired if the required resource could not be found. Check the Marmalade documentation for more information on these flags, though in most cases the default value of zero is what we need to use.

If the resource can't be found, the GetResNamed call will return NULL, otherwise it returns our resource as a pointer to a CIwResource instance, which we can then cast to the required class type.

The second way of accessing a resource is to ask the resource manager to find it by searching through all the currently loaded groups. This can be very useful since it means we don't have to know exactly which resource group to search in. Obviously a full search of all currently loaded resource groups will be slower, but it means we don't have to keep track of every resource group we load. After all, that's what the resource manager is for! The call required to search all loaded groups for a particular resource is as follows:

CIwResource* pResource;
pResource = IwGetResManager()->GetResNamed(name, type, flags);

The parameters are exactly the same as calling the CIwResGroup::GetResNamed method.

Finally, we can remove a resource group and everything it contains from memory by making the following call:

IwGetResManager()->DestroyGroup(pResGroup);

We should destroy a group whenever we no longer need those resources in memory (for example, a group containing resources for a particular level of a game only needs to be in memory when playing that level). It isn't strictly necessary to destroy all groups on shutdown however, as Marmalade will ensure everything that has been allocated will be freed whenever an application is terminated.

The CIwResource class

We've already seen how the CIwManaged class can be used to allow us to easily create instances of our own classes by loading them out of a file. This functionality is improved further by the CIwResource class, which allows us to include our own classes into a resource group.

In the GROUP file example shown in the previous section, we specified the levels.itx file that may contain definitions of our own classes. If our custom classes used CIwResource as their base class (or of course another class that was in turn derived from CIwResource) then all of our resources can be added to the resource group, saving us the bother of keeping track of them ourselves.

GROUP file serialization

It is great that we can load different types of resources so easily, but ultimately we probably do not want to deploy our application with a collection of easily recognized or editable files. There are several reasons for this:

  • Loading speed: Parsing a text file and converting it into a class is a slower operation than just loading in a ready parsed serialized version. It is also possible that we might need to do some sort of conversion on the original data to make it usable in the game, so if we can avoid doing this we will improve the loading time of our game.
  • To prevent hacking: If we ship a collection of text files and common file formats such as PNG files, we make it very easy for someone to hack and modify our game or make unauthorized use of the game's resources.
  • Smaller code size: If we are loading resource data that is already in a form that our game code can use directly, there is no need to include any code for converting the original data format into our own internal one. This makes the code size smaller and also helps guard against hackers a little more.
  • Deployment size: Text files are often much larger than their serialized binary equivalents, so shipping a binary version could reduce the size of our install package.

Marmalade tackles all these issues by automatically converting every GROUP file we load into its binary equivalent using the serialization functionality provided by the CIwManaged class.

After the resource group has been fully loaded, the resource manager will call the Serialise method on every instance of every resource contained within the group, creating a file with .bin added to the original GROUP's filename. For example, the resources in a file called images.group would be serialized to a file called images.group.bin.

Once the serialized version of the GROUP file has been created, the resource manager destroys the resource group and then recreates it from the newly serialized version. This step is present as it makes catching problems, such as forgetting to serialize a member variable of a class, easier to spot.

There is a useful ICF setting that controls the resource building process. Simply add the following to the ICF file (refer back to Chapter 1, Getting Started with Marmalade, for a discussion of what an ICF file is):

[RESMANAGER]
ResBuild=1

When set to 1, the ResBuild setting will ensure that the resource manager always loads the GROUP file and serializes it. By setting it to 0, the GROUP file parsing stage is skipped and instead any existing serialized version of the GROUP file will be loaded directly. This can be very useful during development, to both increase application startup time when no resources have been added or changed, and also to match more closely the loading process on the device.

Note

If you've made changes to the resources for your game but they aren't appearing when you run it, the ResBuild flag is always a good first port of call. It's amazing how easy it is to make a resource change and forget that you've disabled resource building!

Resource handlers

There is one final part of the IwResManager API that is worth mentioning, and this is the concept of resource handlers.

You may have wondered how the resource manager is able to load and process files of different types. It's great that we can just list a bunch of filenames in a GROUP file, but how exactly does a PNG format image file end up being loaded into a form that we can use for rendering? Resource handlers, of course!

A resource handler is a subclass of CIwResHandler that is used to load and process resources of a particular type, identified by one or more filename extensions.

When the text parser comes across a filename in the GROUP file, it looks at the file extension and then checks to see if a resource handler has been registered for that extension. If no suitable handler is found an error will be raised, otherwise the filename is passed to the relevant resource handler class that will then do whatever needs to be done to the file to make it usable in our code.

The entire resource manager system in Marmalade relies on resource handlers in order to work. GROUP files, ITX files, and bitmapped image files are all processed by classes derived from CIwResHandler, and we can create our own custom resource handlers should we want to make use of some other file type not supported by the core Marmalade SDK.

We will be coming back to the subject of resource handlers when we talk about implementing sound in Chapter 7, Adding Sound and Video of this book, since Marmalade does not have support for any sound file formats as part of the core SDK.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.9.169