diff options
Diffstat (limited to 'engines/sword25/kernel/resmanager.cpp')
-rw-r--r-- | engines/sword25/kernel/resmanager.cpp | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/engines/sword25/kernel/resmanager.cpp b/engines/sword25/kernel/resmanager.cpp new file mode 100644 index 0000000000..9e80f32f8d --- /dev/null +++ b/engines/sword25/kernel/resmanager.cpp @@ -0,0 +1,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 |