/* 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" #include "debug.h" // 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_engine.h" #include "ogg_player.h" #endif /* NO_AUDIO */ #ifndef NO_SHADERS #include "shader.h" #endif /* NO_SHADERS */ #include "list.h" #include "sdlincl.h" // File Handling Includes #include #include #include using namespace std; /** * standard constructor */ ResourceManager::ResourceManager () { this->setClassID(CL_RESOURCE_MANAGER, "ResourceManager"); this->setName("ResourceManager"); this->dataDir = NULL; this->setDataDir("./"); this->imageDirs = new tList; this->resourceList = new tList; } //! Singleton Reference to the ResourceManager ResourceManager* ResourceManager::singletonRef = NULL; /** * standard destructor */ ResourceManager::~ResourceManager () { // deleting the Resources-List this->unloadAllByPriority(RP_GAME); if (this->resourceList->getSize() > 0) PRINTF(1)("Not removed all Resources, since there are still %d resources registered\n", this->resourceList->getSize()); delete this->resourceList; // deleting the Directorie Lists tIterator* tmpIt = imageDirs->getIterator(); char* tmpDir = tmpIt->firstElement(); while(tmpDir) { delete[] tmpDir; tmpDir = tmpIt->nextElement(); } delete tmpIt; delete this->imageDirs; delete[] this->dataDir; ResourceManager::singletonRef = NULL; } /** * 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; if (dataDir[strlen(dataDir)-1] == '/' || dataDir[strlen(dataDir)-1] == '\\') { this->dataDir = new char[strlen(realDir)+1]; strcpy(this->dataDir, realDir); } else { this->dataDir = new char[strlen(realDir)+2]; strcpy(this->dataDir, realDir); this->dataDir[strlen(realDir)] = '/'; this->dataDir[strlen(realDir)+1] = '\0'; } delete[] realDir; return true; } else { PRINTF(1)("%s is not a Directory, and can not be the Data Directory, leaving as %s \n", realDir, this->dataDir); delete[] realDir; return false; } } /** * 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; } #ifndef NO_TEXTURES /** * 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) { if (imageDir == NULL) return false; char* newDir; if (imageDir[strlen(imageDir)-1] == '/' || imageDir[strlen(imageDir)-1] == '\\') { newDir = new char[strlen(imageDir)+1]; strcpy(newDir, imageDir); } else { newDir = new char[strlen(imageDir)+2]; strcpy(newDir, imageDir); newDir[strlen(imageDir)] = '/'; newDir[strlen(imageDir)+1] = '\0'; } // check if the param is a Directory if (isDir(newDir)) { // check if the Directory has been added before tIterator* tmpImageDirs = imageDirs->getIterator(); char* tmpDir = tmpImageDirs->firstElement(); while(tmpDir) { if (!strcmp(tmpDir, newDir)) { PRINTF(3)("Path %s already loaded\n", newDir); delete[] newDir; delete tmpImageDirs; return true; } tmpDir = tmpImageDirs->nextElement(); } delete tmpImageDirs; // adding the directory to the List this->imageDirs->add(newDir); return true; } else { PRINTF(1)("%s is not a Directory, and can not be added to the Paths of Images\n", newDir); delete[] newDir; return false; } } #endif /* NO_TEXTURES */ /** * 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. */ BaseObject* ResourceManager::load(const char* fileName, ResourcePriority prio, void* param1, void* param2, void* param3) { if (fileName == NULL) return NULL; ResourceType tmpType; #ifndef NO_MODEL #define __IF_OK if (!strncasecmp(fileName+(strlen(fileName)-4), ".obj", 4)) tmpType = OBJ; else if (!strncmp(fileName+(strlen(fileName)-4), ".md2", 4)) tmpType = MD2; else if (!strcasecmp(fileName, "cube") || !strcasecmp(fileName, "sphere") || !strcasecmp(fileName, "plane") || !strcasecmp(fileName, "cylinder") || !strcasecmp(fileName, "cone")) tmpType = PRIM; #endif /* NO_MODEL */ #ifndef NO_AUDIO #ifdef __IF_OK else #endif #define __IF_OK if (!strncasecmp(fileName+(strlen(fileName)-4), ".wav", 4)) tmpType = WAV; else if (!strncasecmp(fileName+(strlen(fileName)-4), ".mp3", 4)) tmpType = MP3; else if (!strncasecmp(fileName+(strlen(fileName)-4), ".ogg", 4)) tmpType = OGG; #endif /* NO_AUDIO */ #ifndef NO_TEXT #ifdef __IF_OK else #endif #define __IF_OK if (!strncasecmp(fileName+(strlen(fileName)-4), ".ttf", 4)) tmpType = TTF; #endif /* NO_TEXT */ #ifndef NO_SHADERS #ifdef __IF_OK else #endif #define __IF_OK if (!strncasecmp(fileName+(strlen(fileName)-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, param1, param2, param3); } /** * 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 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. */ BaseObject* ResourceManager::load(const char* fileName, ResourceType type, ResourcePriority prio, void* param1, void* param2, void* param3) { if (fileName == NULL) return NULL; // searching if the resource was loaded before. Resource* tmpResource = this->locateResourceByInfo(fileName, type, param1, param2, param3); if (tmpResource != NULL) // 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 { // 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) { #ifndef NO_MODEL 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)("File %s in %s does not exist. Loading a cube-Model instead\n", fileName, dataDir); 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 != NULL) { tmpResource->secFileName = new char[strlen((const char*)param1)+1]; strcpy(tmpResource->secFileName, (const char*) param1); } else tmpResource->secFileName = NULL; tmpResource->pointer = new MD2Data(fullName, tmpResource->secFileName); } break; #endif /* NO_MODEL */ #ifndef NO_TEXT case TTF: if (param1 != NULL) tmpResource->ttfSize = *(unsigned int*)param1; else tmpResource->ttfSize = FONT_DEFAULT_RENDER_SIZE; if(isFile(fullName)) tmpResource->pointer = new Font(fullName, tmpResource->ttfSize); else PRINTF(2)("%s does not exist in %s. Not loading Font\n", fileName, this->dataDir); break; #endif /* NO_TEXT */ #ifndef NO_AUDIO case WAV: if(isFile(fullName)) tmpResource->pointer = new SoundBuffer(fullName); break; case OGG: if (isFile(fullName)) tmpResource->pointer = new OggPlayer(fullName); break; #endif /* NO_AUDIO */ #ifndef NO_TEXTURES case IMAGE: if(isFile(fullName)) { PRINTF(4)("Image %s resides to %s\n", fileName, fullName); tmpResource->pointer = new Texture(fullName); } else { char* tmpDir; tIterator* iterator = imageDirs->getIterator(); tmpDir = iterator->firstElement(); 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; #endif /* NO_TEXTURES */ #ifndef NO_SHADERS case SHADER: if(ResourceManager::isFile(fullName)) { if (param1 != NULL) { char* secFullName = ResourceManager::getFullName((const char*)param1); if (ResourceManager::isFile(secFullName)) { tmpResource->secFileName = new char[strlen((const char*)param1)+1]; strcpy(tmpResource->secFileName, (const char*) param1); tmpResource->pointer = new Shader(fullName, secFullName); } delete[] secFullName; } else { tmpResource->secFileName = NULL; tmpResource->pointer = new Shader(fullName, NULL); } } 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); break; } if (tmpResource->pointer != NULL) this->resourceList->add(tmpResource); delete[] fullName; } if (tmpResource->pointer != NULL) return tmpResource->pointer; else { PRINTF(2)("Resource %s could not be loaded\n", fileName); delete[] tmpResource->name; delete tmpResource; return NULL; } } /** * 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 == 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; } } /** * 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) { // deleting the Resource switch(resource->type) { #ifndef NO_MODEL case OBJ: case PRIM: delete (Model*)resource->pointer; break; case MD2: delete (MD2Data*)resource->pointer; break; #endif /* NO_MODEL */ #ifndef NO_AUDIO case WAV: delete (SoundBuffer*)resource->pointer; break; case OGG: delete (OggPlayer*)resource->pointer; break; #endif /* NO_AUDIO */ #ifndef NO_TEXT case TTF: delete (Font*)resource->pointer; break; #endif /* NO_TEXT */ #ifndef NO_TEXTURES case IMAGE: delete (Texture*)resource->pointer; break; #endif /* NO_TEXTURES */ #ifndef NO_SHADERS case SHADER: delete (Shader*)resource->pointer; break; #endif /* NO_SHADERS */ default: PRINTF(2)("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); delete 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; } /** * 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->lastElement(); 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->prevElement(); } delete iterator; } /** * 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->firstElement(); while (enumRes) { if (enumRes->type == type && !strcmp(fileName, enumRes->name)) { bool match = false; switch (type) { #ifndef NO_MODEL 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->secFileName == NULL) match = true; } else if (!strcmp(enumRes->secFileName, (const char*)param1)) match = true; break; #endif /* NO_MODEL */ #ifndef NO_TEXT case TTF: if (param1 == NULL) { if (enumRes->ttfSize == FONT_DEFAULT_RENDER_SIZE) match = true; } else if (enumRes->ttfSize == *(unsigned int*)param1) match = true; break; #endif /* NO_TEXT */ #ifndef NO_SHADERS case SHADER: if (!param1) { if (enumRes->secFileName == NULL) match = true; } else if (!strcmp(enumRes->secFileName, (const char*)param1)) match = true; #endif /* NO_SHADERS */ default: match = true; break; } if (match) { delete iterator; return enumRes; } } enumRes = iterator->nextElement(); } delete iterator; return NULL; } /** * 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->firstElement(); while (enumRes) { if (pointer == enumRes->pointer) { delete iterator; return enumRes; } enumRes = iterator->nextElement(); } delete iterator; return NULL; } /** * 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] == '/' || directoryName[strlen(directoryName)-1] == '\\') { tmpDirName = new char[strlen(directoryName)]; 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 { delete[] tmpDirName; return false; } } /** * 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; } } /** * 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); delete[] tmpName; return false; } fclose(stream); delete[] tmpName; } /** * 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 || ResourceManager::getInstance()->getDataDir() == 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; } } /** * 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 char* fileName) { if (fileName == NULL || ResourceManager::getInstance()->getDataDir() == NULL) return false; bool retVal = false; char* checkFile = new char[strlen(ResourceManager::getInstance()->getDataDir()) + strlen(fileName) + 1]; sprintf(checkFile, "%s%s", ResourceManager::getInstance()->getDataDir(), fileName); if (ResourceManager::isFile(checkFile) || ResourceManager::isDir(checkFile)) retVal = true; else retVal = false; delete[] checkFile; return retVal; } /** * 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); PRINT(0)(" List of Image-Directories: "); tIterator* tmpIt = imageDirs->getIterator(); char* tmpDir = tmpIt->firstElement(); 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->firstElement(); while (enumRes) { PRINT(0)("-----------------------------------------\n"); PRINT(0)("Name: %s; References: %d; Type: %s ", enumRes->name, enumRes->count, ResourceManager::ResourceTypeToChar(enumRes->type)); 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"); } /** * converts a ResourceType into the corresponding String * @param type the ResourceType to translate * @returns the converted String. */ const char* ResourceManager::ResourceTypeToChar(ResourceType type) { switch (type) { #ifndef NO_MODEL case OBJ: return "ObjectModel"; break; case PRIM: return "PrimitiveModel"; break; case MD2: return "MD2-Data"; break; #endif #ifndef NO_TEXTURES case IMAGE: return "ImageFile (Texture)"; break; #endif #ifndef NO_AUDIO case WAV: return "SoundFile"; break; case OGG: return "MusicFile"; break; #endif #ifndef NO_TEXT case TTF: return "Font (TTF)"; break; #endif #ifndef NO_SHADERS case SHADER: return "Shader"; break; #endif default: return "unknown Format"; break; } }