1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
|
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
/*
* This code is based on Broken Sword 2.5 engine
*
* Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
*
* Licensed under GNU GPL v2
*
*/
#include "sword25/kernel/resmanager.h"
#include "sword25/kernel/resource.h"
#include "sword25/kernel/resservice.h"
#include "sword25/kernel/string.h"
#include "sword25/package/packagemanager.h"
namespace Sword25 {
#define BS_LOG_PREFIX "RESOURCEMANAGER"
ResourceManager::~ResourceManager() {
// Clear all unlocked resources
EmptyCache();
// All remaining resources are not released, so print warnings and release
Common::List<Resource *>::iterator Iter = m_Resources.begin();
for (; Iter != m_Resources.end(); ++Iter) {
BS_LOG_WARNINGLN("Resource \"%s\" was not released.", (*Iter)->getFileName().c_str());
// Set the lock count to zero
while ((*Iter)->GetLockCount() > 0) {
(*Iter)->release();
};
// Delete the resource
delete(*Iter);
}
}
/**
* Returns a resource by it's ordinal index. Returns NULL if any error occurs
* Note: This method is not optimised for speed and should be used only for debugging purposes
* @param Ord Ordinal number of the resource. Must be between 0 and GetResourceCount() - 1.
*/
Resource *ResourceManager::GetResourceByOrdinal(int Ord) const {
// �berpr�fen ob der Index Ord innerhald der Listengrenzen liegt.
if (Ord < 0 || Ord >= GetResourceCount()) {
BS_LOG_ERRORLN("Resource ordinal (%d) out of bounds (0 - %d).", Ord, GetResourceCount() - 1);
return NULL;
}
// Liste durchlaufen und die Resource mit dem gew�nschten Index zur�ckgeben.
int CurOrd = 0;
Common::List<Resource *>::const_iterator Iter = m_Resources.begin();
for (; Iter != m_Resources.end(); ++Iter, ++CurOrd) {
if (CurOrd == Ord)
return (*Iter);
}
// Die Ausf�hrung sollte nie an diesem Punkt ankommen.
BS_LOG_EXTERRORLN("Execution reached unexpected point.");
return NULL;
}
/**
* Registers a RegisterResourceService. This method is the constructor of
* BS_ResourceService, and thus helps all resource services in the ResourceManager list
* @param pService Which service
*/
bool ResourceManager::RegisterResourceService(ResourceService *pService) {
if (!pService) {
BS_LOG_ERRORLN("Can't register NULL resource service.");
return false;
}
m_ResourceServices.push_back(pService);
return true;
}
/**
* Deletes resources as necessary until the specified memory limit is not being exceeded.
*/
void ResourceManager::DeleteResourcesIfNecessary() {
// If enough memory is available, or no resources are loaded, then the function can immediately end
if (m_KernelPtr->GetUsedMemory() < m_MaxMemoryUsage || m_Resources.empty()) return;
// Keep deleting resources until the memory usage of the process falls below the set maximum limit.
// The list is processed backwards in order to first release those resources who have been
// not been accessed for the longest
Common::List<Resource *>::iterator Iter = m_Resources.end();
do {
--Iter;
// The resource may be released only if it isn't locked
if ((*Iter)->GetLockCount() == 0) Iter = DeleteResource(*Iter);
} while (Iter != m_Resources.begin() && m_KernelPtr->GetUsedMemory() > m_MaxMemoryUsage);
}
/**
* Releases all resources that are not locked.
**/
void ResourceManager::EmptyCache() {
// Scan through the resource list
Common::List<Resource *>::iterator Iter = m_Resources.begin();
while (Iter != m_Resources.end()) {
if ((*Iter)->GetLockCount() == 0) {
// Delete the resource
Iter = DeleteResource(*Iter);
} else
++Iter;
}
}
/**
* Returns a requested resource. If any error occurs, returns NULL
* @param FileName Filename of resource
*/
Resource *ResourceManager::RequestResource(const Common::String &FileName) {
// Get the absolute path to the file
Common::String UniqueFileName = GetUniqueFileName(FileName);
if (UniqueFileName == "")
return NULL;
// Determine whether the resource is already loaded
// If the resource is found, it will be placed at the head of the resource list and returned
{
Resource *pResource = GetResource(UniqueFileName);
if (pResource) {
MoveToFront(pResource);
(pResource)->AddReference();
return pResource;
}
}
// The resource was not found, therefore, must not be loaded yet
if (m_LogCacheMiss) BS_LOG_WARNINGLN("\"%s\" was not precached.", UniqueFileName.c_str());
Resource *pResource;
if ((pResource = loadResource(UniqueFileName))) {
pResource->AddReference();
return pResource;
}
return NULL;
}
/**
* Loads a resource into the cache
* @param FileName The filename of the resource to be cached
* @param ForceReload Indicates whether the file should be reloaded if it's already in the cache.
* This is useful for files that may have changed in the interim
*/
bool ResourceManager::PrecacheResource(const Common::String &FileName, bool ForceReload) {
// Get the absolute path to the file
Common::String UniqueFileName = GetUniqueFileName(FileName);
if (UniqueFileName == "")
return false;
Resource *ResourcePtr = GetResource(UniqueFileName);
if (ForceReload && ResourcePtr) {
if (ResourcePtr->GetLockCount()) {
BS_LOG_ERRORLN("Could not force precaching of \"%s\". The resource is locked.", FileName.c_str());
return false;
} else {
DeleteResource(ResourcePtr);
ResourcePtr = 0;
}
}
if (!ResourcePtr && loadResource(UniqueFileName) == NULL) {
BS_LOG_ERRORLN("Could not precache \"%s\",", FileName.c_str());
return false;
}
return true;
}
/**
* Moves a resource to the top of the resource list
* @param pResource The resource
*/
void ResourceManager::MoveToFront(Resource *pResource) {
// Erase the resource from it's current position
m_Resources.erase(pResource->_iterator);
// Re-add the resource at the front of the list
m_Resources.push_front(pResource);
// Reset the resource iterator to the repositioned item
pResource->_iterator = m_Resources.begin();
}
/**
* Loads a resource and updates the m_UsedMemory total
*
* The resource must not already be loaded
* @param FileName The unique filename of the resource to be loaded
*/
Resource *ResourceManager::loadResource(const Common::String &fileName) {
// ResourceService finden, der die Resource laden kann.
for (uint i = 0; i < m_ResourceServices.size(); ++i) {
if (m_ResourceServices[i]->canLoadResource(fileName)) {
// If more memory is desired, memory must be released
DeleteResourcesIfNecessary();
// Load the resource
Resource *pResource;
if (!(pResource = m_ResourceServices[i]->loadResource(fileName))) {
BS_LOG_ERRORLN("Responsible service could not load resource \"%s\".", fileName.c_str());
return NULL;
}
// Add the resource to the front of the list
m_Resources.push_front(pResource);
pResource->_iterator = m_Resources.begin();
// Also store the resource in the hash table for quick lookup
m_ResourceHashTable[pResource->GetFileNameHash() % HASH_TABLE_BUCKETS].push_front(pResource);
return pResource;
}
}
BS_LOG_ERRORLN("Could not find a service that can load \"%s\".", fileName.c_str());
return NULL;
}
/**
* Returns the full path of a given resource filename.
* It will return an empty string if a path could not be created.
*/
Common::String ResourceManager::GetUniqueFileName(const Common::String &FileName) const {
// Get a pointer to the package manager
PackageManager *pPackage = (PackageManager *)m_KernelPtr->GetService("package");
if (!pPackage) {
BS_LOG_ERRORLN("Could not get package manager.");
return Common::String("");
}
// Absoluten Pfad der Datei bekommen und somit die Eindeutigkeit des Dateinamens sicherstellen
Common::String UniqueFileName = pPackage->getAbsolutePath(FileName);
if (UniqueFileName == "")
BS_LOG_ERRORLN("Could not create absolute file name for \"%s\".", FileName.c_str());
return UniqueFileName;
}
/**
* Deletes a resource, removes it from the lists, and updates m_UsedMemory
*/
Common::List<Resource *>::iterator ResourceManager::DeleteResource(Resource *pResource) {
// Remove the resource from the hash table
m_ResourceHashTable[pResource->GetFileNameHash() % HASH_TABLE_BUCKETS].remove(pResource);
Resource *pDummy = pResource;
// Delete the resource from the resource list
Common::List<Resource *>::iterator Result = m_Resources.erase(pResource->_iterator);
// Delete the resource
delete(pDummy);
// Return the iterator
return Result;
}
/**
* Returns a pointer to a loaded resource. If any error occurs, NULL will be returned.
* @param UniqueFileName The absolute path and filename
* Gibt einen Pointer auf die angeforderte Resource zur�ck, oder NULL, wenn die Resourcen nicht geladen ist.
*/
Resource *ResourceManager::GetResource(const Common::String &UniqueFileName) const {
// Determine whether the resource is already loaded
const Common::List<Resource *>& HashBucket = m_ResourceHashTable[
BS_String::GetHash(UniqueFileName) % HASH_TABLE_BUCKETS];
{
Common::List<Resource *>::const_iterator Iter = HashBucket.begin();
for (; Iter != HashBucket.end(); ++Iter) {
// Wenn die Resource gefunden wurde wird sie zur�ckgegeben.
if ((*Iter)->getFileName() == UniqueFileName)
return *Iter;
}
}
// Resource wurde nicht gefunden, ist also nicht geladen
return NULL;
}
/**
* Writes the names of all currently locked resources to the log file
*/
void ResourceManager::DumpLockedResources() {
for (Common::List<Resource *>::iterator Iter = m_Resources.begin(); Iter != m_Resources.end(); ++Iter) {
if ((*Iter)->GetLockCount() > 0) {
BS_LOGLN("%s", (*Iter)->getFileName().c_str());
}
}
}
/**
* Specifies the maximum amount of memory the engine is allowed to use.
* If this value is exceeded, resources will be unloaded to make room. This value is meant
* as a guideline, and not as a fixed boundary. It is not guaranteed not to be exceeded;
* the whole game engine may still use more memory than any amount specified.
*/
void ResourceManager::SetMaxMemoryUsage(uint MaxMemoryUsage) {
m_MaxMemoryUsage = MaxMemoryUsage;
DeleteResourcesIfNecessary();
}
} // End of namespace Sword25
|