It is possible to add your own resource types to clanlib, or even extend the functionality of already existing resource types.
The resource system uses an interface called CL_ResourceType to identify resource types in the resource files, and instantiate the appropiate data objects for a resource. When a resource is being loaded, the following happens:
This may sound a little advanced, but a small example will show how simple it can look. First we create our data object:
// resources.scr: our_map = some_location.map ( type=map, size=100x200 }; class ResourceData_Map : public CL_ResourceData { public: ResourceData_Map(CL_Resource &resource) : CL_ResourceData(resource), width(0), height(0), map(NULL) { // Connect to the file load signal: slot_load_file = resource.sig_load_file().connect(this, &ResourceData_Map::on_load_file); // Attach resource data object to the resource. resource.attach_data("map", this); } ~ResourceData_Map() { delete[] map; } void on_load_file() { // get the size option (100x200) CL_ResourceOption &size_opt = get_resource().get_options().get_option("size"); // get the first value of the size (100) width = CL_String(size_opt.get_value(0)).get_as_int(); // get the second value of the size (200) height = CL_String(size_opt.get_value(1)).get_as_int(); // allocate memory for the map map = new char[width*height]; // open the file "some_location.map" FILE *file = fopen(get_resource().get_location().c_str(), "rb"); fread(map, width*height, file); fclose(file); } char *map; int width, height; CL_Slot slot_load_file; };
The data object attaches itself to the resource using the name 'map'. We can obtain a pointer to the object by using the CL_Resource::get_data() function:
// open the resource file CL_ResourceManager resources("resources.scr", false); // get the map resource CL_Resource &map_resource = resources.get_resource("our_map"); // get the attached Map data from the resource ResourceData_Map *map = (ResourceData_Map*) map_resource.get_data("map");
Note that the above code will not produce a call to ResourceData_Map::on_load_file(). This will first happen when a call is done to CL_Resource::load(). The same thing applies with unloading; if you loaded an object by calling load(), you must also call unload(). The resource class reference counts the loading, so several objects can call load(), but only the first call will actually perform the load.
When the resource manager parses the resource file, it will need to know what resource types exist, and how to attach data objects. In order to do this, the resource manager use a linked list of CL_ResourceType objects. It walks through all the resource type objects, and then asks wether it can attach data to the resource. If no resource type recognizes the resource, the resource manager will throw and exception, telling the program that it has encountered an unknown resource type.
ClanLib contain a template class called CL_RegisterResourceType
void MyApp::main(int argc, char **argv) { CL_SetupCore setup_core; CL_RegisterResourceType<ResourceData_Map> resource_type_map("map"); // register resource type 'map' CL_ResourceManager manager("resources.scr", false); CL_Resource &map_resource = manager.get_resource("our_map"); map_resource.load(); // calls on_load_file() on our data object ResourceData_Map *map = (ResourceData_Map *) map_resource.get_data("map"); std::cout << "width of map: " << map->width << std::endl; map_resource.unload(); // calls on_unload() on our data object }
That's it! We have constructed our own custom resource.
It is possible to have several resource type objects for the same resource. This is practical if you want to extend an already existing resource type with more functionality. Each resource type object can attach its own data to the resource object, allowing you to eg. add "animation_data" to a surface type.
The following is a small example of how to extend the surface type with some additional data:
class AnimationData; class AnimationSurface : public CL_Surface { public: AnimationSurface(const std::string &res_id, CL_ResourceManager *resources) : CL_Surface(res_id, resources) { CL_Resource &resource = resources->get_resource(res_id); anim_data = (AnimationData *) resource.get_data("animation_data"); } AnimationData *get_anim_data() { return anim_data; } private: AnimationData *anim_data; }; class AnimationData : public CL_ResourceData { public: AnimationData(CL_Resource &resource) { some_data = resource.get_option("anim_data").get_value(); resource.attach_data(this); } std::string some_data; }; class MyApplication : public CL_ClanApplication { public: virtual int main(int argc, char **argv) { CL_SetupCore setup_core; CL_SetupGL setup_gl; CL_SetupDisplay setup_display; CL_RegisterResourceType<AnimationData> restype_anim("surface"); CL_DisplayWindow window("Window", 640, 480); CL_ResourceManager resources("resources.scr", false); AnimationSurface surf("my_surf", &resources); surf.draw(0, 0); CL_Display::flip(); std::cout << "some data: " << surf.get_anim_data()->some_data.c_str() << std::endl; return 0; } } app;