/* 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 "md2Model.h" #include "texture.h" #include "text_engine.h" #include "sound_engine.h" #include "list.h" #include "sdlincl.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(const 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) \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) \param param3: an additional option to parse (see the constuctors for more help) \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; if (!strncmp(fileName+(strlen(fileName)-4), ".md2", 4)) tmpType = MD2; 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) \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) \param param3: an additional option to parse (see the constuctors for more help) \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->pointer = NULL; tmpResource->name = new char[strlen(fileName)+1]; strcpy(tmpResource->name, fileName); // creating the full name. (directoryName + FileName) char* fullName = ResourceManager::getFullName(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(PRIM_CUBE, tmpResource->modelSize); else if (!strcmp(tmpResource->name, "sphere")) tmpResource->pointer = new PrimitiveModel(PRIM_SPHERE, tmpResource->modelSize); else if (!strcmp(tmpResource->name, "plane")) tmpResource->pointer = new PrimitiveModel(PRIM_PLANE, tmpResource->modelSize); else if (!strcmp(tmpResource->name, "cylinder")) tmpResource->pointer = new PrimitiveModel(PRIM_CYLINDER, tmpResource->modelSize); else if (!strcmp(tmpResource->name, "cone")) tmpResource->pointer = new PrimitiveModel(PRIM_CONE, tmpResource->modelSize); break; case MD2: if(ResourceManager::isFile(fullName)) { if (param1) { tmpResource->skinFileName = new char[strlen((const char*)param1)+1]; strcpy(tmpResource->skinFileName, (const char*) param1); } else tmpResource->skinFileName = NULL; tmpResource->pointer = new MD2Data(fullName, tmpResource->skinFileName); } 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 WAV: if(isFile(fullName)) tmpResource->pointer = new SoundBuffer(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; } if (tmpResource->pointer) 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 \param prio: the PriorityLevel to unload this resource \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); } /** \brief unloads a Resource \param resource: The resource to unloade \param prio the PriorityLevel to unload this resource */ 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 MD2: delete (MD2Data*)resource->pointer; break; case IMAGE: delete (Texture*)resource->pointer; break; case WAV: delete (SoundBuffer*)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 \param type the Type of resource to locate. \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) \param param3: 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 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 MD2: if (!param1) { if (enumRes->skinFileName == NULL) match = true; } else if (!strcmp(enumRes->skinFileName, (const char*) 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) { if (directoryName == NULL) return false; 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) { if (fileName == NULL) return false; 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); if (tmpName == NULL) return false; 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) { if (fileName == NULL) return false; char* tmpName = ResourceManager::homeDirCheck(fileName); unlink(tmpName); delete tmpName; } /** \param name 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) { if (name == NULL) return NULL; 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) { if (fileName == NULL) return NULL; char* retName = new char[strlen(ResourceManager::getInstance()->getDataDir()) + strlen(fileName) + 1]; sprintf(retName, "%s%s", ResourceManager::getInstance()->getDataDir(), fileName); if (ResourceManager::isFile(retName) || ResourceManager::isDir(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"); }