/* 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 "resource_manager.h" // different resource Types #include "objModel.h" #include "primitive_model.h" #include "texture.h" #include "text_engine.h" #include "list.h" // File Handling Includes #include #include #include using namespace std; /** \brief standard constructor */ ResourceManager::ResourceManager () { this->setClassID(CL_RESOURCE_MANAGER, "ResourceManager"); this->dataDir = NULL; this->setDataDir("./"); this->imageDirs = new tList(); this->resourceList = new tList(); } /** \returns the Instance to this ResourceManager */ ResourceManager* ResourceManager::getInstance(void) { if (!ResourceManager::singletonRef) ResourceManager::singletonRef = new ResourceManager(); return ResourceManager::singletonRef; } //! Singleton Reference to the ResourceManager ResourceManager* ResourceManager::singletonRef = NULL; /** \brief standard destructor */ ResourceManager::~ResourceManager (void) { // deleting the Resources-List this->unloadAllByPriority(RP_GAME); delete this->resourceList; // deleting the Directorie Lists tIterator* tmpIt = imageDirs->getIterator(); char* tmpDir = tmpIt->nextElement(); while(tmpDir) { delete []tmpDir; tmpDir = tmpIt->nextElement(); } delete tmpIt; delete this->imageDirs; ResourceManager::singletonRef = NULL; } /** \brief sets the data main directory \param dataDir the DataDirectory. */ bool ResourceManager::setDataDir(const char* dataDir) { char* realDir = ResourceManager::homeDirCheck(dataDir); if (isDir(realDir)) { delete this->dataDir; this->dataDir = new char[strlen(realDir)+1]; strcpy(this->dataDir, realDir); delete realDir; return true; } else { PRINTF(1)("%s is not a Directory, and can not be the Data Directory, leaving as %s \n", dataDir, this->dataDir); delete realDir; return false; } } /** \brief checks for the DataDirectory, by looking if \param fileInside is inisde?? */ bool ResourceManager::checkDataDir(const char* fileInside) { bool retVal; if (!isDir(this->dataDir)) { PRINTF(1)("%s is not a directory\n", this->dataDir); return false; } char* testFile = new char[strlen(this->dataDir)+strlen(fileInside)+1]; sprintf(testFile, "%s%s", this->dataDir, fileInside); retVal = isFile(testFile); delete testFile; return retVal; } /** \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(char* imageDir) { // check if the param is a Directory if (isDir(imageDir)) { // check if the Directory has been added before tIterator* tmpImageDirs = imageDirs->getIterator(); char* tmpDir = tmpImageDirs->nextElement(); while(tmpDir) { if (!strcmp(tmpDir, imageDir)) { PRINTF(4)("Path %s already loaded\n", imageDir); delete tmpImageDirs; return true; } tmpDir = tmpImageDirs->nextElement(); } delete tmpImageDirs; // adding the directory to the List tmpDir = new char[strlen(imageDir)+1]; strcpy(tmpDir, imageDir); this->imageDirs->add(tmpDir); return true; } else { PRINTF(1)("%s is not a Directory, and can not be added to the Paths of Images\n", dataDir); return false; } } /** \brief loads resources \param fileName The fileName of the resource to load \param prio The ResourcePriority of this resource (will only be increased) \returns a pointer to a desired Resource. */ void* ResourceManager::load(const char* fileName, ResourcePriority prio, void* param1, void* param2, void* param3) { ResourceType tmpType; if (!strncmp(fileName+(strlen(fileName)-4), ".obj", 4)) tmpType = OBJ; else if (!strncmp(fileName+(strlen(fileName)-4), ".wav", 4)) tmpType = WAV; else if (!strncmp(fileName+(strlen(fileName)-4), ".mp3", 4)) tmpType = MP3; else if (!strncmp(fileName+(strlen(fileName)-4), ".ogg", 4)) tmpType = OGG; else if (!strcmp(fileName, "cube") || !strcmp(fileName, "sphere") || !strcmp(fileName, "plane") || !strcmp(fileName, "cylinder") || !strcmp(fileName, "cone")) tmpType = PRIM; else if (!strncmp(fileName+(strlen(fileName)-4), ".ttf", 4)) tmpType = TTF; else tmpType = IMAGE; return this->load(fileName, tmpType, prio, param1, param2, param3); } /** \brief loads resources \param fileName The fileName of the resource to load \param type The Type of Resource to load (\see ResourceType) \param prio The ResourcePriority of this resource (will only be increased) \returns a pointer to a desired Resource. */ void* ResourceManager::load(const char* fileName, ResourceType type, ResourcePriority prio, void* param1, void* param2, void* param3) { // searching if the resource was loaded before. Resource* tmpResource = this->locateResourceByInfo(fileName, type, param1, param2,param3); if (tmpResource) // if the resource was loaded before. { PRINTF(4)("not loading cached resource %s\n", tmpResource->name); tmpResource->count++; if(tmpResource->prio < prio) tmpResource->prio = prio; } else { char* tmpDir; // Setting up the new Resource tmpResource = new Resource; tmpResource->count = 1; tmpResource->type = type; tmpResource->prio = prio; tmpResource->name = new char[strlen(fileName)+1]; strcpy(tmpResource->name, fileName); // creating the full name. (directoryName + FileName) char* fullName = new char[strlen(this->getDataDir())+strlen(fileName)+1]; sprintf(fullName, "%s%s", this->getDataDir(), fileName); // Checking for the type of resource \see ResourceType switch(type) { case OBJ: if (param1) tmpResource->modelSize = *(float*)param1; else tmpResource->modelSize = 1.0; if(ResourceManager::isFile(fullName)) tmpResource->pointer = new OBJModel(fullName, tmpResource->modelSize); else { PRINTF(2)("Sorry, %s does not exist. Loading a cube-Model instead\n", fullName); tmpResource->pointer = ResourceManager::load("cube", PRIM, prio, &tmpResource->modelSize); } break; case PRIM: if (param1) tmpResource->modelSize = *(float*)param1; else tmpResource->modelSize = 1.0; if (!strcmp(tmpResource->name, "cube")) tmpResource->pointer = new PrimitiveModel(CUBE, tmpResource->modelSize); else if (!strcmp(tmpResource->name, "sphere")) tmpResource->pointer = new PrimitiveModel(SPHERE, tmpResource->modelSize); else if (!strcmp(tmpResource->name, "plane")) tmpResource->pointer = new PrimitiveModel(PLANE, tmpResource->modelSize); else if (!strcmp(tmpResource->name, "cylinder")) tmpResource->pointer = new PrimitiveModel(CYLINDER, tmpResource->modelSize); else if (!strcmp(tmpResource->name, "cone")) tmpResource->pointer = new PrimitiveModel(CONE, tmpResource->modelSize); break; case TTF: if (param1) tmpResource->ttfSize = *(int*)param1; else tmpResource->ttfSize = FONT_DEFAULT_SIZE; if (param2) { Vector* tmpVec = (Vector*)param2; tmpResource->ttfColorR = (int)tmpVec->x; tmpResource->ttfColorG = (int)tmpVec->y; tmpResource->ttfColorB = (int)tmpVec->z; } else { tmpResource->ttfColorR = FONT_DEFAULT_COLOR_R; tmpResource->ttfColorG = FONT_DEFAULT_COLOR_G; tmpResource->ttfColorB = FONT_DEFAULT_COLOR_B; } if(isFile(fullName)) tmpResource->pointer = new Font(fullName, tmpResource->ttfSize, tmpResource->ttfColorR, tmpResource->ttfColorG, tmpResource->ttfColorB); else PRINTF(2)("Sorry, %s does not exist. Not loading Font\n", fullName); break; case IMAGE: if(isFile(fullName)) { PRINTF(4)("Image %s resides to %s\n", fileName, fullName); tmpResource->pointer = new Texture(fullName); } else { tIterator* iterator = imageDirs->getIterator(); tmpDir = iterator->nextElement(); //tmpDir = imageDirs->enumerate(); while(tmpDir) { char* imgName = new char[strlen(tmpDir)+strlen(fileName)+1]; sprintf(imgName, "%s%s", tmpDir, fileName); if(isFile(imgName)) { PRINTF(4)("Image %s resides to %s\n", fileName, imgName); tmpResource->pointer = new Texture(imgName); delete []imgName; break; } delete []imgName; tmpDir = iterator->nextElement(); } delete iterator; } if(!tmpResource) PRINTF(2)("!!Image %s not Found!!\n", fileName); break; default: tmpResource->pointer = NULL; PRINTF(1)("No type found for %s.\n !!This should not happen unless the Type is not supported yet.!!\n", tmpResource->name); break; } this->resourceList->add(tmpResource); delete []fullName; } if (tmpResource->pointer) return tmpResource->pointer; else { PRINTF(2)("Resource %s could not be loaded\n", fileName); delete tmpResource; return NULL; } } /** \brief unloads a Resource \param pointer The pointer to free \returns true if successful (pointer found, and deleted), false otherwise */ bool ResourceManager::unload(void* pointer, ResourcePriority prio) { // if pointer is existent. and only one resource of this type exists. Resource* tmpResource = this->locateResourceByPointer(pointer); if (!tmpResource) { PRINTF(2)("Resource not Found %p\n", pointer); return false; } else unload(tmpResource, prio); } bool ResourceManager::unload(Resource* resource, ResourcePriority prio) { if (resource->count > 0) resource->count--; if (resource->prio <= prio) { if (resource->count <= 0) { // deleting the Resource switch(resource->type) { case OBJ: case PRIM: delete (Model*)resource->pointer; break; case IMAGE: delete (Texture*)resource->pointer; break; case TTF: delete (Font*)resource->pointer; break; default: PRINTF(1)("NOT YET IMPLEMENTED !!FIX FIX!!\n"); return false; break; } // deleting the List Entry: PRINTF(4)("Resource %s safely removed.\n", resource->name); delete []resource->name; this->resourceList->remove(resource); } else PRINTF(4)("Resource %s not removed, because there are still %d References to it.\n", resource->name, resource->count); } else PRINTF(4)("not deleting resource %s because DeleteLevel to high\n", resource->name); 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) { tIterator* iterator = resourceList->getIterator(); Resource* enumRes = iterator->nextElement(); while (enumRes) { if (enumRes->prio <= prio) if (enumRes->count == 0) unload(enumRes, prio); else PRINTF(2)("unable to unload %s because there are still %d references to it\n", enumRes->name, enumRes->count); //enumRes = resourceList->nextElement(); enumRes = iterator->nextElement(); } delete iterator; } /** \brief Searches for a Resource by some information \param fileName The name to look for \returns a Pointer to the Resource if found, NULL otherwise. */ Resource* ResourceManager::locateResourceByInfo(const char* fileName, ResourceType type, void* param1, void* param2, void* param3) { // Resource* enumRes = resourceList->enumerate(); tIterator* iterator = resourceList->getIterator(); Resource* enumRes = iterator->nextElement(); while (enumRes) { if (enumRes->type == type && !strcmp(fileName, enumRes->name)) { bool match = false; bool subMatch = false; switch (type) { case PRIM: case OBJ: if (!param1) { if (enumRes->modelSize == 1.0) match = true; } else if (enumRes->modelSize == *(float*)param1) match = true; break; case TTF: if (!param1) { if (enumRes->ttfSize == FONT_DEFAULT_SIZE) subMatch = true; } else if (enumRes->modelSize =- *(int*)param1) subMatch = true; if(subMatch) { Vector* tmpVec = (Vector*)param2; if (!param2) { if(enumRes->ttfColorR == FONT_DEFAULT_COLOR_R && enumRes->ttfColorG == FONT_DEFAULT_COLOR_G && enumRes->ttfColorB == FONT_DEFAULT_COLOR_B ) match = true; } else if (enumRes->ttfColorR == (int)tmpVec->x && enumRes->ttfColorG == (int)tmpVec->y && enumRes->ttfColorB == (int)tmpVec->z ) match = true; } break; default: match = true; break; } if (match) { delete iterator; return enumRes; } } enumRes = iterator->nextElement(); } delete iterator; 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) { // Resource* enumRes = resourceList->enumerate(); tIterator* iterator = resourceList->getIterator(); Resource* enumRes = iterator->nextElement(); while (enumRes) { if (pointer == enumRes->pointer) { delete iterator; return enumRes; } enumRes = iterator->nextElement(); } delete iterator; return NULL; } /** \brief Checks if it is a Directory \param directoryName the Directory to check for \returns true if it is a directory/symlink false otherwise */ bool ResourceManager::isDir(const char* directoryName) { char* tmpDirName = NULL; struct stat status; // checking for the termination of the string given. If there is a "/" at the end cut it away if (directoryName[strlen(directoryName)-1] == '/') { tmpDirName = new char[strlen(directoryName)+1]; strncpy(tmpDirName, directoryName, strlen(directoryName)-1); tmpDirName[strlen(directoryName)-1] = '\0'; } else { tmpDirName = new char[strlen(directoryName)+1]; strcpy(tmpDirName, directoryName); } if(!stat(tmpDirName, &status)) { if (status.st_mode & (S_IFDIR #ifndef __WIN32__ | S_IFLNK #endif )) { delete tmpDirName; return true; } else { delete tmpDirName; return false; } } else return false; } /** \brief Checks if the file is either a Regular file or a Symlink \param fileName the File to check for \returns true if it is a regular file/symlink, false otherwise */ bool ResourceManager::isFile(const char* fileName) { char* tmpFileName = ResourceManager::homeDirCheck(fileName); // actually checks the File struct stat status; if (!stat(tmpFileName, &status)) { if (status.st_mode & (S_IFREG #ifndef __WIN32__ | S_IFLNK #endif )) { delete tmpFileName; return true; } else { delete tmpFileName; return false; } } else { delete tmpFileName; return false; } } /** \brief touches a File on the disk (thereby creating it) \param fileName The file to touch */ bool ResourceManager::touchFile(const char* fileName) { char* tmpName = ResourceManager::homeDirCheck(fileName); FILE* stream; if( (stream = fopen (tmpName, "w")) == NULL) { PRINTF(1)("could not open %s fro writing\n", fileName); return false; } fclose(stream); delete tmpName; } /** \brief deletes a File from disk \param fileName the File to delete */ bool ResourceManager::deleteFile(const char* fileName) { char* tmpName = ResourceManager::homeDirCheck(fileName); unlink(tmpName); delete tmpName; } /** \param fileName the Name of the file to check \returns The name of the file, including the HomeDir IMPORTANT: this has to be deleted from the outside */ char* ResourceManager::homeDirCheck(const char* name) { char* retName; if (!strncmp(name, "~/", 2)) { char tmpFileName[500]; #ifdef __WIN32__ strcpy(tmpFileName, getenv("USERPROFILE")); #else strcpy(tmpFileName, getenv("HOME")); #endif retName = new char[strlen(tmpFileName)+strlen(name)]; sprintf(retName, "%s%s", tmpFileName, name+1); } else { retName = new char[strlen(name)+1]; strcpy(retName, name); } return retName; } /** \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 */ char* ResourceManager::getFullName(const char* fileName) { char* retName = new char[strlen(ResourceManager::getInstance()->getDataDir()) + strlen(fileName) + 1]; sprintf(retName, "%s%s", ResourceManager::getInstance()->getDataDir(), fileName); if (ResourceManager::isFile(retName)) return retName; else { delete retName; return NULL; } } /** \brief outputs debug information about the ResourceManager */ void ResourceManager::debug(void) { 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); PRINT(0)(" List of Image-Directories: "); tIterator* tmpIt = imageDirs->getIterator(); char* tmpDir = tmpIt->nextElement(); while(tmpDir) { PRINT(0)("%s ",tmpDir); tmpDir = tmpIt->nextElement(); } delete tmpIt; PRINT(0)("\n"); PRINT(0)("List of all stored Resources:\n"); tIterator* iterator = resourceList->getIterator(); Resource* enumRes = iterator->nextElement(); while (enumRes) { PRINT(0)("-----------------------------------------\n"); PRINT(0)("Name: %s; References: %d; Type:", enumRes->name, enumRes->count); switch (enumRes->type) { case OBJ: PRINT(0)("ObjectModel\n"); break; case PRIM: PRINT(0)("PrimitiveModel\n"); break; case IMAGE: PRINT(0)("ImageFile (Texture)\n"); break; default: PRINT(0)("SoundFile\n"); break; } PRINT(0)("gets deleted at "); switch(enumRes->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; } enumRes = iterator->nextElement(); } delete iterator; PRINT(0)("==================================RM==\n"); }