00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "resources/resourcemanager.h"
00023
00024 #include "resources/dye.h"
00025 #include "resources/image.h"
00026 #include "resources/imageset.h"
00027 #include "resources/music.h"
00028 #include "resources/soundeffect.h"
00029 #include "resources/spritedef.h"
00030
00031 #include "log.h"
00032
00033 #include <cassert>
00034 #include <physfs.h>
00035 #include <SDL_image.h>
00036 #include <sstream>
00037
00038 #include <sys/time.h>
00039
00040 ResourceManager *ResourceManager::instance = NULL;
00041
00042 ResourceManager::ResourceManager()
00043 : mOldestOrphan(0)
00044 {
00045 logger->log("Initializing resource manager...");
00046 }
00047
00048 ResourceManager::~ResourceManager()
00049 {
00050 mResources.insert(mOrphanedResources.begin(), mOrphanedResources.end());
00051
00052
00053 ResourceIterator iter = mResources.begin();
00054 while (iter != mResources.end())
00055 {
00056 if (dynamic_cast<SpriteDef*>(iter->second) != 0)
00057 {
00058 cleanUp(iter->second);
00059 ResourceIterator toErase = iter;
00060 ++iter;
00061 mResources.erase(toErase);
00062 }
00063 else
00064 {
00065 ++iter;
00066 }
00067 }
00068
00069
00070 iter = mResources.begin();
00071 while (iter != mResources.end())
00072 {
00073 if (dynamic_cast<ImageSet*>(iter->second) != 0)
00074 {
00075 cleanUp(iter->second);
00076 ResourceIterator toErase = iter;
00077 ++iter;
00078 mResources.erase(toErase);
00079 }
00080 else
00081 {
00082 ++iter;
00083 }
00084 }
00085
00086
00087 iter = mResources.begin();
00088 while (iter != mResources.end())
00089 {
00090 cleanUp(iter->second);
00091 ++iter;
00092 }
00093 }
00094
00095 void ResourceManager::cleanUp(Resource *res)
00096 {
00097 if (res->mRefCount > 0)
00098 {
00099 logger->log("ResourceManager::~ResourceManager() cleaning up %d "
00100 "reference%s to %s",
00101 res->mRefCount,
00102 (res->mRefCount == 1) ? "" : "s",
00103 res->mIdPath.c_str());
00104 }
00105
00106 delete res;
00107 }
00108
00109 void ResourceManager::cleanOrphans()
00110 {
00111 timeval tv;
00112 gettimeofday(&tv, NULL);
00113
00114 time_t oldest = tv.tv_sec, threshold = oldest - 30;
00115
00116 if (mOrphanedResources.empty() || mOldestOrphan >= threshold) return;
00117
00118 ResourceIterator iter = mOrphanedResources.begin();
00119 while (iter != mOrphanedResources.end())
00120 {
00121 Resource *res = iter->second;
00122 time_t t = res->mTimeStamp;
00123 if (t >= threshold)
00124 {
00125 if (t < oldest) oldest = t;
00126 ++iter;
00127 }
00128 else
00129 {
00130 logger->log("ResourceManager::release(%s)", res->mIdPath.c_str());
00131 ResourceIterator toErase = iter;
00132 ++iter;
00133 mOrphanedResources.erase(toErase);
00134 delete res;
00135 }
00136 }
00137
00138 mOldestOrphan = oldest;
00139 }
00140
00141 bool ResourceManager::setWriteDir(const std::string &path)
00142 {
00143 return (bool) PHYSFS_setWriteDir(path.c_str());
00144 }
00145
00146 bool ResourceManager::addToSearchPath(const std::string &path, bool append)
00147 {
00148 logger->log("Adding to PhysicsFS: %s", path.c_str());
00149 if (!PHYSFS_addToSearchPath(path.c_str(), append ? 1 : 0)) {
00150 logger->log("Error: %s", PHYSFS_getLastError());
00151 return false;
00152 }
00153 return true;
00154 }
00155
00156 void ResourceManager::searchAndAddArchives(const std::string &path,
00157 const std::string &ext,
00158 bool append)
00159 {
00160 const char *dirSep = PHYSFS_getDirSeparator();
00161 char **list = PHYSFS_enumerateFiles(path.c_str());
00162
00163 for (char **i = list; *i; i++)
00164 {
00165 size_t len = strlen(*i);
00166
00167 if (len > ext.length() && !ext.compare((*i)+(len - ext.length())))
00168 {
00169 std::string file, realPath, archive;
00170
00171 file = path + (*i);
00172 realPath = std::string(PHYSFS_getRealDir(file.c_str()));
00173 archive = realPath + dirSep + file;
00174
00175 addToSearchPath(archive, append);
00176 }
00177 }
00178
00179 PHYSFS_freeList(list);
00180 }
00181
00182 bool ResourceManager::mkdir(const std::string &path)
00183 {
00184 return (bool) PHYSFS_mkdir(path.c_str());
00185 }
00186
00187 bool ResourceManager::exists(const std::string &path)
00188 {
00189 return PHYSFS_exists(path.c_str());
00190 }
00191
00192 bool ResourceManager::isDirectory(const std::string &path)
00193 {
00194 return PHYSFS_isDirectory(path.c_str());
00195 }
00196
00197 std::string ResourceManager::getPath(const std::string &file)
00198 {
00199
00200 const char* tmp = PHYSFS_getRealDir(file.c_str());
00201 std::string path;
00202
00203
00204 if (tmp)
00205 {
00206 path = std::string(tmp) + "/" + file;
00207 }
00208 else
00209 {
00210
00211 path = std::string(PKG_DATADIR) + std::string("data") + "/" + file;
00212 }
00213
00214 return path;
00215 }
00216
00217 Resource *ResourceManager::get(const std::string &idPath, generator fun,
00218 void *data)
00219 {
00220
00221 ResourceIterator resIter = mResources.find(idPath);
00222 if (resIter != mResources.end())
00223 {
00224 resIter->second->incRef();
00225 return resIter->second;
00226 }
00227
00228 resIter = mOrphanedResources.find(idPath);
00229 if (resIter != mOrphanedResources.end())
00230 {
00231 Resource *res = resIter->second;
00232 mResources.insert(*resIter);
00233 mOrphanedResources.erase(resIter);
00234 res->incRef();
00235 return res;
00236 }
00237
00238 Resource *resource = fun(data);
00239
00240 if (resource)
00241 {
00242 resource->incRef();
00243 resource->mIdPath = idPath;
00244 mResources[idPath] = resource;
00245 cleanOrphans();
00246 }
00247
00248
00249 return resource;
00250 }
00251
00252 struct ResourceLoader
00253 {
00254 ResourceManager *manager;
00255 std::string path;
00256 ResourceManager::loader fun;
00257 static Resource *load(void *v)
00258 {
00259 ResourceLoader *l = static_cast< ResourceLoader * >(v);
00260 int fileSize;
00261 void *buffer = l->manager->loadFile(l->path, fileSize);
00262 if (!buffer) return NULL;
00263 Resource *res = l->fun(buffer, fileSize);
00264 free(buffer);
00265 return res;
00266 }
00267 };
00268
00269 Resource *ResourceManager::load(const std::string &path, loader fun)
00270 {
00271 ResourceLoader l = { this, path, fun };
00272 return get(path, ResourceLoader::load, &l);
00273 }
00274
00275 Music *ResourceManager::getMusic(const std::string &idPath)
00276 {
00277 return static_cast<Music*>(load(idPath, Music::load));
00278 }
00279
00280 SoundEffect *ResourceManager::getSoundEffect(const std::string &idPath)
00281 {
00282 return static_cast<SoundEffect*>(load(idPath, SoundEffect::load));
00283 }
00284
00285 struct DyedImageLoader
00286 {
00287 ResourceManager *manager;
00288 std::string path;
00289 static Resource *load(void *v)
00290 {
00291 DyedImageLoader *l = static_cast< DyedImageLoader * >(v);
00292 std::string path = l->path;
00293 std::string::size_type p = path.find('|');
00294 Dye *d = NULL;
00295 if (p != std::string::npos)
00296 {
00297 d = new Dye(path.substr(p + 1));
00298 path = path.substr(0, p);
00299 }
00300 int fileSize;
00301 void *buffer = l->manager->loadFile(path, fileSize);
00302 if (!buffer) return NULL;
00303 Resource *res = d ? Image::load(buffer, fileSize, *d)
00304 : Image::load(buffer, fileSize);
00305 free(buffer);
00306 delete d;
00307 return res;
00308 }
00309 };
00310
00311 Image *ResourceManager::getImage(const std::string &idPath)
00312 {
00313 DyedImageLoader l = { this, idPath };
00314 return static_cast<Image*>(get(idPath, DyedImageLoader::load, &l));
00315 }
00316
00317 struct ImageSetLoader
00318 {
00319 ResourceManager *manager;
00320 std::string path;
00321 int w, h;
00322 static Resource *load(void *v)
00323 {
00324 ImageSetLoader *l = static_cast< ImageSetLoader * >(v);
00325 Image *img = l->manager->getImage(l->path);
00326 if (!img) return NULL;
00327 ImageSet *res = new ImageSet(img, l->w, l->h);
00328 img->decRef();
00329 return res;
00330 }
00331 };
00332
00333 ImageSet *ResourceManager::getImageSet(const std::string &imagePath,
00334 int w, int h)
00335 {
00336 ImageSetLoader l = { this, imagePath, w, h };
00337 std::stringstream ss;
00338 ss << imagePath << "[" << w << "x" << h << "]";
00339 return static_cast<ImageSet*>(get(ss.str(), ImageSetLoader::load, &l));
00340 }
00341
00342 struct SpriteDefLoader
00343 {
00344 std::string path;
00345 int variant;
00346 static Resource *load(void *v)
00347 {
00348 SpriteDefLoader *l = static_cast< SpriteDefLoader * >(v);
00349 return SpriteDef::load(l->path, l->variant);
00350 }
00351 };
00352
00353 SpriteDef *ResourceManager::getSprite(const std::string &path, int variant)
00354 {
00355 SpriteDefLoader l = { path, variant };
00356 std::stringstream ss;
00357 ss << path << "[" << variant << "]";
00358 return static_cast<SpriteDef*>(get(ss.str(), SpriteDefLoader::load, &l));
00359 }
00360
00361 void ResourceManager::release(Resource *res)
00362 {
00363 ResourceIterator resIter = mResources.find(res->mIdPath);
00364
00365
00366 assert(resIter != mResources.end() && resIter->second == res);
00367
00368 timeval tv;
00369 gettimeofday(&tv, NULL);
00370 time_t timestamp = tv.tv_sec;
00371
00372 res->mTimeStamp = timestamp;
00373 if (mOrphanedResources.empty()) mOldestOrphan = timestamp;
00374
00375 mOrphanedResources.insert(*resIter);
00376 mResources.erase(resIter);
00377 }
00378
00379 ResourceManager *ResourceManager::getInstance()
00380 {
00381
00382 if (!instance)
00383 instance = new ResourceManager;
00384 return instance;
00385 }
00386
00387 void ResourceManager::deleteInstance()
00388 {
00389 delete instance;
00390 instance = NULL;
00391 }
00392
00393 void *ResourceManager::loadFile(const std::string &fileName, int &fileSize)
00394 {
00395
00396 PHYSFS_file *file = PHYSFS_openRead(fileName.c_str());
00397
00398
00399 if (file == NULL) {
00400 logger->log("Warning: Failed to load %s: %s",
00401 fileName.c_str(), PHYSFS_getLastError());
00402 return NULL;
00403 }
00404
00405
00406 logger->log("Loaded %s/%s", PHYSFS_getRealDir(fileName.c_str()),
00407 fileName.c_str());
00408
00409
00410 fileSize = PHYSFS_fileLength(file);
00411
00412
00413 void *buffer = malloc(fileSize);
00414 PHYSFS_read(file, buffer, 1, fileSize);
00415
00416
00417 PHYSFS_close(file);
00418
00419 return buffer;
00420 }
00421
00422 bool ResourceManager::copyFile(const std::string &src, const std::string &dst)
00423 {
00424 PHYSFS_file *srcFile = PHYSFS_openRead(src.c_str());
00425 if (!srcFile)
00426 {
00427 logger->log("Read error: %s", PHYSFS_getLastError());
00428 return false;
00429 }
00430 PHYSFS_file *dstFile = PHYSFS_openWrite(dst.c_str());
00431 if (!dstFile)
00432 {
00433 logger->log("Write error: %s", PHYSFS_getLastError());
00434 PHYSFS_close(srcFile);
00435 return false;
00436 }
00437
00438 int fileSize = PHYSFS_fileLength(srcFile);
00439 void *buf = malloc(fileSize);
00440 PHYSFS_read(srcFile, buf, 1, fileSize);
00441 PHYSFS_write(dstFile, buf, 1, fileSize);
00442
00443 PHYSFS_close(srcFile);
00444 PHYSFS_close(dstFile);
00445 free(buf);
00446 return true;
00447 }
00448
00449 std::vector<std::string> ResourceManager::loadTextFile(
00450 const std::string &fileName)
00451 {
00452 int contentsLength;
00453 char *fileContents = (char*)loadFile(fileName, contentsLength);
00454 std::vector<std::string> lines;
00455
00456 if (!fileContents)
00457 {
00458 logger->log("Couldn't load text file: %s", fileName.c_str());
00459 return lines;
00460 }
00461
00462 std::istringstream iss(std::string(fileContents, contentsLength));
00463 std::string line;
00464
00465 while (getline(iss, line))
00466 lines.push_back(line);
00467
00468 free(fileContents);
00469 return lines;
00470 }
00471
00472 SDL_Surface *ResourceManager::loadSDLSurface(const std::string &filename)
00473 {
00474 int fileSize;
00475 void *buffer = loadFile(filename, fileSize);
00476 SDL_Surface *tmp = NULL;
00477
00478 if (buffer) {
00479 SDL_RWops *rw = SDL_RWFromMem(buffer, fileSize);
00480 tmp = IMG_Load_RW(rw, 1);
00481 ::free(buffer);
00482 }
00483
00484 return tmp;
00485 }