Mana
Loading...
Searching...
No Matches
resourcemanager.cpp
Go to the documentation of this file.
1/*
2 * The Mana Client
3 * Copyright (C) 2004-2009 The Mana World Development Team
4 * Copyright (C) 2009-2012 The Mana Developers
5 *
6 * This file is part of The Mana Client.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
23
24#include "client.h"
25#include "log.h"
26
27#include "resources/dye.h"
28#include "resources/image.h"
29#include "resources/imageset.h"
30#include "resources/music.h"
32#include "resources/spritedef.h"
33
34#include "utils/filesystem.h"
35
36#include <SDL_image.h>
37
38#include <cassert>
39#include <sstream>
40#include <memory>
41
42#include <sys/time.h>
43
45
47{
48 Log::info("Initializing resource manager...");
49}
50
52{
53 auto cleanupResources = [&](auto match)
54 {
55 // Include any orphaned resources into the main list for cleanup
57 mOrphanedResources.clear();
58
59 for (auto iter = mResources.begin(); iter != mResources.end(); )
60 {
61 if (match(iter->second))
62 {
63 cleanUp(iter->second);
64 iter = mResources.erase(iter);
65 }
66 else
67 {
68 ++iter;
69 }
70 }
71 };
72
73 // SpriteDef references ImageSet
74 cleanupResources([](Resource *res) { return dynamic_cast<SpriteDef *>(res); });
75
76 // ImageSet references Image
77 cleanupResources([](Resource *res) { return dynamic_cast<ImageSet *>(res); });
78
79 // Release remaining resources
80 cleanupResources([](Resource *res) { return true; });
81
82 assert(mOrphanedResources.empty());
83}
84
86{
87 if (res->mRefCount > 0)
88 {
89 Log::info("ResourceManager::~ResourceManager() cleaning up %d "
90 "reference%s to %s",
91 res->mRefCount,
92 (res->mRefCount == 1) ? "" : "s",
93 res->mIdPath.c_str());
94 }
95
96 delete res;
97}
98
100{
101 // Delete orphaned resources after 30 seconds.
102 time_t oldest = time(nullptr);
103 const time_t threshold = oldest - 30;
104
105 if (mOrphanedResources.empty() || mOldestOrphan >= threshold)
106 return;
107
108 auto iter = mOrphanedResources.begin();
109 while (iter != mOrphanedResources.end())
110 {
111 Resource *res = iter->second;
112 const time_t t = res->mTimeStamp;
113 if (t >= threshold)
114 {
115 if (t < oldest)
116 oldest = t;
117 ++iter;
118 }
119 else
120 {
121 Log::info("ResourceManager::release(%s)", res->mIdPath.c_str());
122 iter = mOrphanedResources.erase(iter);
123 delete res; // delete only after removal from list, to avoid issues in recursion
124 }
125 }
126
127 mOldestOrphan = oldest;
128}
129
130bool ResourceManager::addToSearchPath(const std::string &path, bool append)
131{
132 if (!FS::addToSearchPath(path, append))
133 {
134 Log::error("Couldn't add search path: %s (%s)", path.c_str(), FS::getLastError());
135 return false;
136 }
137 Log::info("Added search path: %s", path.c_str());
138 return true;
139}
140
141void ResourceManager::searchAndAddArchives(const std::string &path,
142 const std::string &ext,
143 bool append)
144{
145 const char *dirSep = FS::getDirSeparator();
146
147 for (auto fileName : FS::enumerateFiles(path))
148 {
149 const size_t len = strlen(fileName);
150
151 if (len > ext.length() && ext != (fileName + (len - ext.length())))
152 {
153 std::string file = path + fileName;
154 if (auto realDir = FS::getRealDir(file))
155 {
156 std::string archive = std::string(*realDir) + dirSep + file;
157 addToSearchPath(archive, append);
158 }
159 }
160 }
161}
162
163std::string ResourceManager::getPath(const std::string &file)
164{
165 // Get the real directory of the file
166 auto realDir = FS::getRealDir(file);
167 std::string path;
168
169 if (realDir)
170 {
171 path = std::string(*realDir) + "/" + file;
172 }
173 else
174 {
175 // if not found in search path return the default path
176 path = Client::getPackageDirectory() + "/" + file;
177 }
178
179 return path;
180}
181
182Resource *ResourceManager::get(const std::string &idPath,
183 const std::function<Resource *()> &generator)
184{
185 // Check if the id exists, and return the value if it does.
186 auto resIter = mResources.find(idPath);
187 if (resIter != mResources.end())
188 {
189 return resIter->second;
190 }
191
192 resIter = mOrphanedResources.find(idPath);
193 if (resIter != mOrphanedResources.end())
194 {
195 Resource *res = resIter->second;
196 mResources.insert(*resIter);
197 mOrphanedResources.erase(resIter);
198 return res;
199 }
200
201 Resource *resource = generator();
202 if (resource)
203 {
204 resource->mIdPath = idPath;
205 mResources[idPath] = resource;
206 cleanOrphans();
207 }
208
209 return resource;
210}
211
213{
214 return static_cast<Music*>(get(path, [&] () -> Resource * {
215 if (SDL_RWops *rw = FS::openBufferedRWops(path))
216 return Music::load(rw);
217
218 return nullptr;
219 }));
220}
221
223{
224 return static_cast<SoundEffect*>(get(path, [&] () -> Resource * {
225 if (SDL_RWops *rw = FS::openBufferedRWops(path))
226 return SoundEffect::load(rw);
227
228 return nullptr;
229 }));
230}
231
233{
234 return static_cast<Image*>(get(idPath, [&] () -> Resource * {
235 std::string path = idPath;
236 std::string::size_type p = path.find('|');
237 std::unique_ptr<Dye> d;
238 if (p != std::string::npos)
239 {
240 d = std::make_unique<Dye>(path.substr(p + 1));
241 path = path.substr(0, p);
242 }
243 SDL_RWops *rw = FS::openRWops(path);
244 if (!rw)
245 return nullptr;
246
247 Resource *res = d ? Image::load(rw, *d)
248 : Image::load(rw);
249 return res;
250 }));
251}
252
254 int w, int h)
255{
256 std::stringstream ss;
257 ss << imagePath << "[" << w << "x" << h << "]";
258
259 return static_cast<ImageSet*>(get(ss.str(), [&] () -> Resource * {
260 auto img = getImage(imagePath);
261 if (!img)
262 return nullptr;
263
264 return new ImageSet(img, w, h);
265 }));
266}
267
268ResourceRef<SpriteDef> ResourceManager::getSprite(const std::string &path, int variant)
269{
270 std::stringstream ss;
271 ss << path << "[" << variant << "]";
272
273 return static_cast<SpriteDef*>(get(ss.str(), [&] () -> Resource * {
274 return SpriteDef::load(path, variant);
275 }));
276}
277
279{
280 auto resIter = mResources.find(res->mIdPath);
281
282 // The resource has to exist
283 assert(resIter != mResources.end() && resIter->second == res);
284
285 const time_t timestamp = time(nullptr);
286
287 res->mTimeStamp = timestamp;
288 if (mOrphanedResources.empty())
289 mOldestOrphan = timestamp;
290
291 mOrphanedResources.insert(*resIter);
292 mResources.erase(resIter);
293}
294
296{
297 mResources.erase(res->mIdPath);
298}
299
301{
302 // Create a new instance if necessary.
303 if (!instance)
305 return instance;
306}
307
309{
310 delete instance;
311 instance = nullptr;
312}
static const std::string & getPackageDirectory()
Definition client.h:175
Stores a set of subimages originating from a single image.
Definition imageset.h:34
Defines a class for loading and storing images.
Definition image.h:45
static Resource * load(SDL_RWops *rw)
Loads an image from an SDL_RWops structure.
Definition image.cpp:98
Defines a class for loading and storing music.
Definition music.h:32
static Music * load(SDL_RWops *rw)
Loads a music from a buffer in memory.
Definition music.cpp:36
A class for loading and managing resources.
ResourceRef< SpriteDef > getSprite(const std::string &path, int variant=0)
Loads a SpriteDef based on a given path and the supplied variant.
void remove(Resource *)
Removes a resource from the list of resources managed by the resource manager.
static ResourceManager * getInstance()
Returns an instance of the class, creating one if it does not already exist.
Resource * get(const std::string &idPath, const std::function< Resource *()> &generator)
Looks up a resource, creating it with the generator function if it does not exist.
ResourceRef< Music > getMusic(const std::string &path)
Loads the Music resource found at the given path.
std::map< std::string, Resource * > mResources
static ResourceManager * instance
~ResourceManager()
Destructor.
ResourceRef< SoundEffect > getSoundEffect(const std::string &path)
Loads the SoundEffect resource found at the given path.
static std::string getPath(const std::string &file)
Returns the real path to a file.
ResourceRef< Image > getImage(const std::string &idPath)
Loads the Image resource found at the given identifier path.
static void deleteInstance()
Deletes the class instance if it exists.
static void cleanUp(Resource *resource)
Deletes the resource after logging a cleanup message.
static void searchAndAddArchives(const std::string &path, const std::string &ext, bool append)
Searches for zip files and adds them to the search path.
void release(Resource *)
Releases a resource, placing it in the set of orphaned resources.
static bool addToSearchPath(const std::string &path, bool append)
Adds a directory or archive to the search path.
std::map< std::string, Resource * > mOrphanedResources
ResourceRef< ImageSet > getImageSet(const std::string &imagePath, int w, int h)
Loads a image set based on the image referenced by the given path and the supplied sprite sizes.
Automatically counting Resource reference.
Definition resource.h:74
A generic reference counted resource object.
Definition resource.h:31
std::string mIdPath
Path identifying this resource.
Definition resource.h:64
time_t mTimeStamp
Time at which the resource was orphaned.
Definition resource.h:65
unsigned mRefCount
Reference count.
Definition resource.h:66
Defines a class for loading and storing sound effects.
Definition soundeffect.h:32
static SoundEffect * load(SDL_RWops *rw)
Loads a sample from a buffer in memory.
Defines a class to load an animation.
Definition spritedef.h:87
const char * getDirSeparator()
Definition filesystem.h:47
SDL_RWops * openBufferedRWops(const std::string &path, PHYSFS_uint64 bufferSize=2048)
Creates a buffered SDL_RWops.
Definition filesystem.h:274
bool addToSearchPath(const std::string &path, bool append)
Adds a directory or archive to the search path.
Definition filesystem.h:85
const char * getLastError()
Definition filesystem.h:252
SDL_RWops * openRWops(const std::string &path)
Definition filesystem.h:261
std::optional< const char * > getRealDir(const std::string &path)
Definition filesystem.h:98
Files enumerateFiles(const std::string &dir)
Returns a list of files in the given directory.
Definition filesystem.h:153
void info(const char *log_text,...) LOG_PRINTF_ATTR
void error(const char *log_text,...) LOG_PRINTF_ATTR