/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Benjamin Grauer co-programmer: Patrick Boenzli */ #define DEBUG_SPECIAL_MODULE DEBUG_MODULE_LOAD #include "util/loading/resource_manager.h" #include "file.h" #include "substring.h" #include "debug.h" #include #include // different resource Types #ifndef NO_MODEL #include "objModel.h" #include "primitive_model.h" #include "md2Model.h" #endif /* NO_MODEL */ #ifndef NO_TEXTURES #include "texture.h" #endif /* NO_TEXTURES */ #ifndef NO_TEXT #include "font.h" #endif /* NO_TEXT */ #ifndef NO_AUDIO #include "sound_buffer.h" #include "ogg_player.h" #endif /* NO_AUDIO */ #ifndef NO_SHADERS #include "shader.h" #endif /* NO_SHADERS */ // File Handling Includes #include #include #include using namespace std; /** * @brief standard constructor */ ResourceManager::ResourceManager () { this->setClassID(CL_RESOURCE_MANAGER, "ResourceManager"); this->setName("ResourceManager"); this->dataDir = "./"; this->tryDataDir("./data"); } //! Singleton Reference to the ResourceManager ResourceManager* ResourceManager::singletonRef = NULL; /** * @brief standard destructor */ ResourceManager::~ResourceManager () { // deleting the Resources-List this->unloadAllByPriority(RP_GAME); if (!this->resourceList.empty()) PRINTF(1)("Not removed all Resources, since there are still %d resources registered\n", this->resourceList.size()); ResourceManager::singletonRef = NULL; } /** * @brief sets the data main directory * @param dataDir the DataDirectory. */ bool ResourceManager::setDataDir(const std::string& dataDir) { File dataDirectory(dataDir); if (dataDirectory.isDirectory()) { this->dataDir = dataDirectory.name(); if (dataDir[dataDir.size()-1] != '/' && dataDir[dataDir.size()-1] != '\\') { this->dataDir += '/'; } return true; } else { PRINTF(1)("%s is not a Directory, and can not be the Data Directory, leaving as %s \n", dataDir.c_str(), this->dataDir.c_str()); return false; } } /** * @brief sets the data main directory * @param dataDir the DataDirectory. * * this is essentially the same as setDataDir, but it ommits the error-message */ bool ResourceManager::tryDataDir(const std::string& dataDir) { File dataDirectory(dataDir); if (dataDirectory.isDirectory()) { this->dataDir = dataDirectory.name(); if (dataDir[dataDir.size()-1] != '/' && dataDir[dataDir.size()-1] != '\\') { this->dataDir += '/'; } return true; } return false; } /** * @brief checks for the DataDirectory, by looking if * @param fileInside is iniside of the given directory. */ bool ResourceManager::verifyDataDir(const std::string& fileInside) { File dataDirectory(this->dataDir); if (!dataDirectory.isDirectory()) { PRINTF(1)("'%s' is not a directory\n", this->dataDir.c_str()); return false; } File testFile(this->dataDir + fileInside); return testFile.isFile(); } #ifndef NO_TEXTURES /** * @brief adds a new Path for Images * @param imageDir The path to insert * @returns true, if the Path was well and injected (or already existent within the list) false otherwise */ bool ResourceManager::addImageDir(const std::string& imageDir) { std::string newDir = imageDir; if (imageDir[imageDir.size()-1] != '/' && imageDir[imageDir.size()-1] != '\\') { newDir += '/'; } // check if the param is a Directory if (File(newDir).isDirectory()) { // check if the Directory has been added before std::vector::const_iterator imageDir; for (imageDir = this->imageDirs.begin(); imageDir != this->imageDirs.end(); imageDir++) { if (*imageDir == newDir) { PRINTF(3)("Path %s already loaded\n", newDir.c_str()); return true; } } // adding the directory to the List this->imageDirs.push_back(newDir); return true; } else { PRINTF(1)("%s is not a Directory, and can not be added to the Paths of Images\n", newDir.c_str()); return false; } } #endif /* NO_TEXTURES */ /** * @brief loads resources * @param fileName: The fileName of the resource to load * @param prio: The ResourcePriority of this resource (will only be increased) * @param param0: an additional option to parse (see the constuctors for more help) * @param param1: an additional option to parse (see the constuctors for more help) * @param param2: an additional option to parse (see the constuctors for more help) * @returns a pointer to a desired Resource. */ BaseObject* ResourceManager::load(const std::string& fileName, ResourcePriority prio, const MultiType& param0, const MultiType& param1, const MultiType& param2) { ResourceType tmpType; #ifndef NO_MODEL #define __IF_OK if (!strncasecmp(fileName.c_str()+(fileName.size()-4), ".obj", 4)) tmpType = OBJ; else if (!strncmp(fileName.c_str()+(fileName.size()-4), ".md2", 4)) tmpType = MD2; else if (!strcasecmp(fileName.c_str(), "cube") || !strcasecmp(fileName.c_str(), "sphere") || !strcasecmp(fileName.c_str(), "plane") || !strcasecmp(fileName.c_str(), "cylinder") || !strcasecmp(fileName.c_str(), "cone")) tmpType = PRIM; #endif /* NO_MODEL */ #ifndef NO_AUDIO #ifdef __IF_OK else #endif #define __IF_OK if (!strncasecmp(fileName.c_str()+(fileName.size()-4), ".wav", 4)) tmpType = WAV; else if (!strncasecmp(fileName.c_str()+(fileName.size()-4), ".mp3", 4)) tmpType = MP3; else if (!strncasecmp(fileName.c_str()+(fileName.size()-4), ".ogg", 4)) tmpType = OGG; #endif /* NO_AUDIO */ #ifndef NO_TEXT #ifdef __IF_OK else #endif #define __IF_OK if (!strncasecmp(fileName.c_str()+(fileName.size()-4), ".ttf", 4)) tmpType = TTF; #endif /* NO_TEXT */ #ifndef NO_SHADERS #ifdef __IF_OK else #endif #define __IF_OK if (!strncasecmp(fileName.c_str()+(fileName.size()-5), ".vert", 5)) tmpType = SHADER; #endif /* NO_SHADERS */ #ifndef NO_TEXTURES #ifdef __IF_OK else #else if #endif tmpType = IMAGE; #endif /* NO_TEXTURES */ #undef __IF_OK return this->load(fileName, tmpType, prio, param0, param1, param2); } /** * @brief caches a Resource * * @see load; * * @brief returns true if ok, false otherwise. * This function loads a Resource without applying it to an Object. * This is for loading purposes, e.g, when the user is loading a Resource * during the initialisation instead of at Runtime. */ bool ResourceManager::cache(const std::string& fileName, ResourceType type, ResourcePriority prio, const MultiType& param0, const MultiType& param1, const MultiType& param2) { // searching if the resource was loaded before. Resource* tmpResource; // check if we already loaded this Resource tmpResource = this->locateResourceByInfo(fileName, type, param0, param1, param2); // otherwise load it if (tmpResource == NULL) tmpResource = this->loadResource(fileName, type, prio, param0, param1, param2); // return cached pointer. if (tmpResource != NULL) // if the resource was loaded before. { if(tmpResource->prio < prio) tmpResource->prio = prio; return true; } else return false; } /** * tells the ResourceManager to generate a Copy of the Resource. * @brief resourcePointer: The Pointer to the resource to copy * @returns the Resource pointed to resourcePointer. */ BaseObject* ResourceManager::copy(BaseObject* resourcePointer) { Resource* tmp = locateResourceByPointer(resourcePointer); if (tmp!=NULL) { tmp->count++; return tmp->pointer; } else return NULL; } /** * @brief loads resources * @param fileName: The fileName of the resource to load * @param type: The Type of Resource to load. * @param prio: The ResourcePriority of this resource (will only be increased) * @param param0: an additional option to parse (see the constuctors for more help) * @param param1: an additional option to parse (see the constuctors for more help) * @param param2: an additional option to parse (see the constuctors for more help) * @returns a pointer to a desired Resource. */ BaseObject* ResourceManager::load(const std::string& fileName, ResourceType type, ResourcePriority prio, const MultiType& param0, const MultiType& param1, const MultiType& param2) { // searching if the resource was loaded before. Resource* tmpResource; // check if we already loaded this Resource tmpResource = this->locateResourceByInfo(fileName, type, param0, param1, param2); // otherwise load it if (tmpResource == NULL) { tmpResource = this->loadResource(fileName, type, prio, param0, param1, param2); } // return cached pointer. if (tmpResource != NULL) // if the resource was loaded before. { tmpResource->count++; if(tmpResource->prio < prio) tmpResource->prio = prio; return tmpResource->pointer; } else return NULL; } /** * @brief loads resources for internal purposes * @param fileName: The fileName of the resource to load * @param type: The Type of Resource to load. * @param prio: The ResourcePriority of this resource (will only be increased) * @param param0: an additional option to parse (see the constuctors for more help) * @param param1: an additional option to parse (see the constuctors for more help) * @param param2: an additional option to parse (see the constuctors for more help) * @returns a pointer to a desired Resource. */ Resource* ResourceManager::loadResource(const std::string& fileName, ResourceType type, ResourcePriority prio, const MultiType& param0, const MultiType& param1, const MultiType& param2) { // Setting up the new Resource Resource* tmpResource = new Resource; tmpResource->count = 0; tmpResource->type = type; tmpResource->prio = prio; tmpResource->pointer = NULL; tmpResource->name = fileName; // creating the full name. (directoryName + FileName) std::string fullName = ResourceManager::getFullName(fileName); // Checking for the type of resource \see ResourceType switch(type) { #ifndef NO_MODEL case OBJ: if (param0.getType() != MT_NULL) tmpResource->param[0] = param0; else tmpResource->param[0] = 1.0f; if(File(fullName).isFile()) tmpResource->pointer = new OBJModel(fullName, tmpResource->param[0].getFloat()); else { PRINTF(2)("File %s in %s does not exist. Loading a cube-Model instead\n", fileName.c_str(), dataDir.c_str()); tmpResource->pointer = ResourceManager::load("cube", PRIM, prio, tmpResource->param[0].getFloat()); } break; case PRIM: if (param0 != MT_NULL) tmpResource->param[0] = param0; else tmpResource->param[0] = 1.0f; if (tmpResource->name == "cube") tmpResource->pointer = new PrimitiveModel(PRIM_CUBE, tmpResource->param[0].getFloat()); else if (tmpResource->name == "sphere") tmpResource->pointer = new PrimitiveModel(PRIM_SPHERE, tmpResource->param[0].getFloat()); else if (tmpResource->name == "plane") tmpResource->pointer = new PrimitiveModel(PRIM_PLANE, tmpResource->param[0].getFloat()); else if (tmpResource->name == "cylinder") tmpResource->pointer = new PrimitiveModel(PRIM_CYLINDER, tmpResource->param[0].getFloat()); else if (tmpResource->name == "cone") tmpResource->pointer = new PrimitiveModel(PRIM_CONE, tmpResource->param[0].getFloat()); break; case MD2: if(File(fullName).isFile()) { tmpResource->param[0] = param0; tmpResource->param[1] = param1; tmpResource->pointer = new MD2Data(fullName, tmpResource->param[0].getCString(), tmpResource->param[1].getFloat()); } break; #endif /* NO_MODEL */ #ifndef NO_TEXT case TTF: if (param0 != MT_NULL) { assert(param0.getInt() >= 0); tmpResource->param[0] = param0; } else tmpResource->param[0] = FONT_DEFAULT_RENDER_SIZE; if(File(fullName).isFile()) tmpResource->pointer = new Font(fullName, (unsigned int) tmpResource->param[0].getInt()); else PRINTF(2)("%s does not exist in %s. Not loading Font\n", fileName.c_str(), this->dataDir.c_str()); break; #endif /* NO_TEXT */ #ifndef NO_AUDIO case WAV: if(File(fullName).isFile()) tmpResource->pointer = new OrxSound::SoundBuffer(fullName); break; case OGG: if (File(fullName).isFile()) tmpResource->pointer = new OrxSound::OggPlayer(fullName); break; #endif /* NO_AUDIO */ #ifndef NO_TEXTURES case IMAGE: if (param0 != MT_NULL) tmpResource->param[0] = param0; else tmpResource->param[0] = GL_TEXTURE_2D; if(File(fullName).isFile()) { PRINTF(4)("Image %s resides to %s\n", fileName.c_str(), fullName.c_str()); tmpResource->pointer = new Texture(fullName, tmpResource->param[0].getInt()); } else { std::vector::iterator imageDir; for (imageDir = this->imageDirs.begin(); imageDir != this->imageDirs.end(); imageDir++) { std::string imgName = *imageDir + fileName; if(File(imgName).isFile()) { PRINTF(4)("Image %s resides to %s\n", fileName.c_str(), imgName.c_str()); tmpResource->pointer = new Texture(imgName, tmpResource->param[0].getInt()); break; } } } if(!tmpResource) PRINTF(2)("!!Image %s not Found!!\n", fileName.c_str()); break; #endif /* NO_TEXTURES */ #ifndef NO_SHADERS case SHADER: if(File(fullName).isFile()) { if (param0 != MT_NULL) { MultiType param = param0; /// HACK std::string secFullName = ResourceManager::getFullName(param.getCString()); if (File(secFullName).isFile()) { tmpResource->param[0] = secFullName; tmpResource->pointer = new Shader(fullName, secFullName); } } else { tmpResource->param[0] = param0; tmpResource->pointer = new Shader(fullName, ""); } } break; #endif /* NO_SHADERS */ default: tmpResource->pointer = NULL; PRINTF(1)("No type found for %s.\n !!This should not happen unless the Type is not supported yet. JUST DO IT!!\n", tmpResource->name.c_str()); break; } if (tmpResource->pointer != NULL) this->resourceList.push_back(tmpResource); if (tmpResource->pointer != NULL) return tmpResource; else { PRINTF(2)("Resource %s could not be loaded\n", fileName.c_str()); delete tmpResource; return NULL; } } /** * @brief unloads a Resource * @param pointer: The pointer to free * @param prio: the PriorityLevel to unload this resource * @returns true if successful (pointer found, and deleted), false otherwise */ bool ResourceManager::unload(BaseObject* pointer, ResourcePriority prio) { if (pointer == NULL) return false; // if pointer is existent. and only one resource of this type exists. Resource* tmpResource = this->locateResourceByPointer(pointer); if (tmpResource != NULL) return unload(tmpResource, prio); else { PRINTF(2)("Resource not Found %p\n", pointer); return false; } } /** * @brief unloads a Resource * @param resource: The resource to unloade * @param prio the PriorityLevel to unload this resource * @returns true on success, false otherwise. */ bool ResourceManager::unload(Resource* resource, ResourcePriority prio) { if (resource == NULL) return false; if (resource->count > 0) resource->count--; if (resource->prio <= prio) { if (resource->count == 0) { delete resource->pointer; // deleting the List Entry: PRINTF(4)("Resource %s safely removed.\n", resource->name.c_str()); std::vector::iterator resourceIT = std::find(this->resourceList.begin(), this->resourceList.end(), resource); this->resourceList.erase(resourceIT); delete resource; } else PRINTF(4)("Resource %s not removed, because there are still %d References to it.\n", resource->name.c_str(), resource->count); } else PRINTF(4)("not deleting resource %s because DeleteLevel to high\n", resource->name.c_str()); return true; } /** * @brief unloads all alocated Memory of Resources with a pririty lower than prio * @param prio The priority to delete */ bool ResourceManager::unloadAllByPriority(ResourcePriority prio) { unsigned int removeCount; for (unsigned int round = 0; round < 3; round++) { int index = this->resourceList.size() - 1; removeCount = 0; while (index >= 0) { if (this->resourceList[index]->prio <= prio) { if (this->resourceList[index]->count == 0) unload(this->resourceList[index], prio); else { if (round == 3) PRINTF(2)("unable to unload %s because there are still %d references to it\n", this->resourceList[index]->name.c_str(), this->resourceList[index]->count); removeCount++; } } index--; } if (removeCount == 0) break; } } /** * @brief Searches for a Resource by some information * @param fileName: The name to look for * @param type the Type of resource to locate. * @param param0: an additional option to parse (see the constuctors for more help) * @param param1: an additional option to parse (see the constuctors for more help) * @param param2: an additional option to parse (see the constuctors for more help) * @returns a Pointer to the Resource if found, NULL otherwise. */ Resource* ResourceManager::locateResourceByInfo(const std::string& fileName, ResourceType type, const MultiType& param0, const MultiType& param1, const MultiType& param2) const { std::vector::const_iterator resource; for (resource = this->resourceList.begin(); resource != this->resourceList.end(); resource++) { if ((*resource)->type == type && fileName == (*resource)->name) { bool match = false; switch (type) { #ifndef NO_MODEL case PRIM: case OBJ: if (param0 == MT_NULL) { if ((*resource)->param[0] == 1.0f) match = true; } else if ((*resource)->param[0] == param0.getFloat()) match = true; break; case MD2: if (param0 == MT_NULL && ((*resource)->param[0] == "") && param1 == MT_NULL && ((*resource)->param[0] == 1.0f)) match = true; else if ((*resource)->param[0] == ((MultiType)param0).getString() && (*resource)->param[1] == ((MultiType)param1).getFloat()) match = true; break; #endif /* NO_MODEL */ #ifndef NO_TEXT case TTF: if (param0 == MT_NULL) { if ((*resource)->param[0] == FONT_DEFAULT_RENDER_SIZE) match = true; } else if ((*resource)->param[0] == param0.getInt()) match = true; break; #endif /* NO_TEXT */ #ifndef NO_SHADERS case SHADER: if (param0 == MT_NULL) { if ((*resource)->param[0] == "") match = true; } else if ((*resource)->param[0] == ((MultiType)param0).getString()) match = true; break; #endif /* NO_SHADERS */ #ifndef NO_TEXTURES case IMAGE: if (param0 == MT_NULL) { if ((*resource)->param[0] == GL_TEXTURE_2D) match = true; } else if ((*resource)->param[0] == param0.getInt()) match = true; break; #endif /* NO_TEXTURES */ default: match = true; break; } if (match) { return (*resource); } } } return NULL; } /** * @brief Searches for a Resource by Pointer * @param pointer the Pointer to search for * @returns a Pointer to the Resource if found, NULL otherwise. */ Resource* ResourceManager::locateResourceByPointer(const void* pointer) const { // Resource* enumRes = resourceList->enumerate(); std::vector::const_iterator resource; for (resource = this->resourceList.begin(); resource != this->resourceList.end(); resource++) if (pointer == (*resource)->pointer) return (*resource); return NULL; } std::string ResourceManager::toResourcableString(unsigned int i) { /* int len = strlen(ResourceManager::ResourceTypeToChar(this->resourceList[i]->type)); len += this->resourceList[i]->name.size(); if (this->resourceList[i]->param[0].getCString()) len += strlen(this->resourceList[i]->param[0].getCString()) +1; if (this->resourceList[i]->param[1].getCString()) len += strlen(this->resourceList[i]->param[1].getCString()) +1; if (this->resourceList[i]->param[2].getCString()) len += strlen(this->resourceList[i]->param[2].getCString()) +1; len += 10; std::string tmp = new char[len]; tmp[0] = '\0'; strcat(tmp, ResourceManager::ResourceTypeToChar(this->resourceList[i]->type)); strcat(tmp,","); strcat (tmp, this->resourceList[i]->name); if (this->resourceList[i]->param[0].getCString() && this->resourceList[i]->param[0].getCString() != '\0') { strcat(tmp,","); strcat( tmp, this->resourceList[i]->param[0].getCString()); } if (this->resourceList[i]->param[1].getCString() && this->resourceList[i]->param[1].getCString() != '\0') { strcat(tmp,","); strcat( tmp, this->resourceList[i]->param[1].getCString()); } if (this->resourceList[i]->param[2].getCString() && this->resourceList[i]->param[2].getCString() != '\0') { strcat(tmp,","); strcat( tmp, this->resourceList[i]->param[2].getCString()); } return tmp;*/ } /** * @brief caches a Resource from a ResourceableString created with the toResourcableString-function * @param resourceableString the String to cache the resource from. */ bool ResourceManager::fromResourceableString(const std::string& resourceableString) { /* SubString splits(resourceableString, ','); splits.debug(); if (splits.getCount() == 2) this->cache(splits[1], ResourceManager::stringToResourceType(splits[0]), RP_LEVEL); else if (splits.getCount() == 3) return this->cache(splits[1], ResourceManager::stringToResourceType(splits[0]), RP_LEVEL, splits[2]); else if (splits.getCount() == 4) return this->cache(splits[1], ResourceManager::stringToResourceType(splits[0]), RP_LEVEL, splits[2], splits[3]); else if (splits.getCount() == 5) return this->cache(splits[1], ResourceManager::stringToResourceType(splits[0]), RP_LEVEL, splits[2], splits[3], splits[4]);*/ } /** * @param fileName the Name of the File to check * @returns The full name of the file, including the DataDir, and NULL if the file does not exist * !!IMPORTANT: this has to be deleted from the outside!! */ std::string ResourceManager::getFullName(const std::string& fileName) { if (fileName.empty() || ResourceManager::getInstance()->getDataDir().empty()) return ""; std::string retName = ResourceManager::getInstance()->getDataDir() +fileName; if (File(retName).isFile() || File(retName).isDirectory()) return retName; else return ""; } /** * @brief checks wether a file is in the DataDir. * @param fileName the File to check if it is in the Data-Dir structure. * @returns true if the file exists, false otherwise */ bool ResourceManager::isInDataDir(const std::string& fileName) { if (fileName.empty() || ResourceManager::getInstance()->getDataDir().empty()) return false; bool retVal = false; std::string checkFile = ResourceManager::getInstance()->getDataDir() + fileName; if (File(checkFile).exists()) retVal = true; else retVal = false; return retVal; } /** * @brief outputs debug information about the ResourceManager */ void ResourceManager::debug() const { PRINT(0)("=RM===================================\n"); PRINT(0)("= RESOURCE-MANAGER DEBUG INFORMATION =\n"); PRINT(0)("======================================\n"); // if it is not initialized PRINT(0)(" Reference is: %p\n", ResourceManager::singletonRef); PRINT(0)(" Data-Directory is: %s\n", this->dataDir.c_str()); PRINT(0)(" List of Image-Directories: "); std::vector::const_iterator imageDir; for (imageDir = this->imageDirs.begin(); imageDir != this->imageDirs.end(); imageDir++) PRINT(0)("%s ", (*imageDir).c_str()); PRINT(0)("\n"); PRINT(0)("List of all stored Resources:\n"); std::vector::const_iterator resource; for (resource = this->resourceList.begin(); resource != this->resourceList.end(); resource++) { PRINT(0)("-----------------------------------------\n"); PRINT(0)("Name: %s; References: %d; Type: %s ", (*resource)->name.c_str(), (*resource)->count, ResourceManager::ResourceTypeToChar((*resource)->type)); PRINT(0)("gets deleted at "); switch((*resource)->prio) { default: case RP_NO: PRINT(0)("first posibility (0)\n"); break; case RP_LEVEL: PRINT(0)("the end of the Level (1)\n"); break; case RP_CAMPAIGN: PRINT(0)("the end of the campaign (2)\n"); break; case RP_GAME: PRINT(0)("when leaving the game (3)\n"); break; } } PRINT(0)("==================================RM==\n"); } /** * @brief converts a ResourceType into the corresponding String * @param type the ResourceType to translate * @returns the converted String. */ const char* ResourceManager::ResourceTypeToChar(ResourceType type) { return ResourceManager::resourceNames[type]; } /** * @brief converts a String into a ResourceType (good for loading) * @param resourceType the name of the Type * @returns the Number of the Type, or 0 (defautl) if not found. */ ResourceType ResourceManager::stringToResourceType(const std::string& resourceType) { for (unsigned int i = 0; i < RESOURCE_TYPE_SIZE; i++) if (resourceType == ResourceManager::resourceNames[i]) return (ResourceType)i; return (ResourceType)0; } /** * The Names of the ResourceTypes */ const char* ResourceManager::resourceNames[] = { #ifndef NO_MODEL "ObjectModel", "PrimitiveModel", "MD2-Data", #endif #ifndef NO_TEXT "Font", #endif #ifndef NO_AUDIO "Wav", "mp3", "ogg", #endif #ifndef NO_TEXTURES "Texture", #endif #ifndef NO_SHADERS "Shader", #endif };