From 06d828049ef72c87f1b03596249cd4a4d09a485d Mon Sep 17 00:00:00 2001 From: danij Date: Tue, 30 Oct 2012 05:42:55 +0000 Subject: [PATCH] Refactor|ResourceNamespace: Continued improving de::ResourceNamespace Revised SearchPath implementation, relocated various logics from sys_reslocator.cpp into ResourceNamespace and refactored away the now unnecessary callback mechanisms. ResourceNamespace now has ownership of the FileDirectory it uses. --- .../portable/include/resourcenamespace.h | 102 ++++--- .../engine/portable/include/sys_reslocator.h | 13 +- doomsday/engine/portable/src/dd_main.cpp | 8 +- doomsday/engine/portable/src/def_read.c | 4 +- .../engine/portable/src/resourcenamespace.cpp | 274 ++++++++++++++---- .../engine/portable/src/sys_reslocator.cpp | 267 ++++------------- 6 files changed, 350 insertions(+), 318 deletions(-) diff --git a/doomsday/engine/portable/include/resourcenamespace.h b/doomsday/engine/portable/include/resourcenamespace.h index 4ee46f2460..d658e90b7c 100644 --- a/doomsday/engine/portable/include/resourcenamespace.h +++ b/doomsday/engine/portable/include/resourcenamespace.h @@ -28,9 +28,6 @@ #include "pathtree.h" #include "uri.h" -// Number of entries in the hash table. -#define RESOURCENAMESPACE_HASHSIZE 512 - /** * @defgroup searchPathFlags Search Path Flags * @{ @@ -73,73 +70,104 @@ namespace de FallbackPaths }; - typedef unsigned short NameHashKey; - - typedef NameHashKey (HashNameFunc) (ddstring_t const* name); - struct SearchPath { - int flags; /// @see searchPathFlags - Uri* uri; + public: + /** + * @param _flags @ref searchPathFlags + * @param _uri Unresolved search URI (may include symbolic names or + * other symbol references). SearchPath takes ownership. + */ + SearchPath(int _flags, Uri* _uri); + + /** + * Construct a copy from @a other. This is a "deep copy". + */ + SearchPath(SearchPath const& other); + + ~SearchPath(); + + SearchPath& operator = (SearchPath other); + + friend void swap(SearchPath& first, SearchPath& second); // nothrow + + /// @return @ref searchPathFlags + int flags() const; + + SearchPath& setFlags(int flags); - SearchPath(int _flags, Uri const* _uri) - : flags(_flags), uri(Uri_NewCopy(_uri)) - {} + /// @return Unresolved URI. + Uri const* uri() const; - ~SearchPath() - { - Uri_Delete(uri); - } + private: + /// @see searchPathFlags + int flags_; + + /// Unresolved search URI. + Uri* uri_; }; typedef QMultiMap SearchPaths; typedef QList ResourceList; public: - ResourceNamespace(HashNameFunc* hashNameFunc); + explicit ResourceNamespace(char const* symbolicName = ""); ~ResourceNamespace(); + /// @return Symbolic name of this namespace (e.g., "Models"). + ddstring_t const* name() const; + + /** + * Rebuild this namespace by re-scanning for resources on all search + * paths and re-populating the internal database. + * + * @note Any manually added resources will not be present after this. + */ + void rebuild(); + /** - * Reset the namespace back to it's "empty" state (i.e., no known symbols). + * Reset this namespace back to it's "empty" state (i.e., no resources). + * The search path groups are unaffected. */ void clear(); /** - * Add a new named resource into the namespace. Multiple names for a given - * resource may coexist however duplicates are automatically pruned. + * Manually add a resource to this namespace. Duplicates are pruned + * automatically. * - * @post Name hash may have been rebuilt. - * - * @param name Name of the resource being added. * @param node PathTree node which represents the resource. * - * @return @c true iff the namespace did not already contain this resource. + * @return @c true iff this namespace did not already contain the resource. */ - bool add(ddstring_t const* name, PathTree::Node& node); + bool add(PathTree::Node& node); /** - * Finds all resources in this namespace.. + * Finds all resources in this namespace. * - * @param name If not @c NULL, only consider resources with this name. + * @param searchpath If not @c NULL, only consider resources whose + * name matches that which will be extracted from + * this path. * @param found Set of resources that match the result. * * @return Number of found resources. */ - int findAll(ddstring_t const* name, ResourceList& found); + int findAll(ddstring_t const* searchPath, ResourceList& found); /** - * Add a new path to this namespace. + * Add a new search path to this namespace. Newer paths have priority + * over previously added paths. * - * @param group Group to add this path to. @see SearchPathGroup + * @param group Group to add this path to. @see PathGroup * @param path New unresolved path to add. * @param flags @see searchPathFlags * - * @return @c true if the path is correctly formed and present in the namespace. + * @return @c true if @a path was well-formed and subsequently added. */ bool addSearchPath(PathGroup group, Uri const* path, int flags); /** - * Clear search paths in @a group in this namespace. + * Clear search paths in @a group from this namespace. + * * @param group Search path group to be cleared. */ void clearSearchPaths(PathGroup group); @@ -150,8 +178,8 @@ namespace de void clearSearchPaths(); /** - * Append to @a pathList all resolved search paths in @a group, in descending, - * (i.e., most recent order), using @a delimiter to separate each. + * Append to @a pathList all resolved search paths in @a group, in + * descending (i.e., most recent) order. * * @param group Group of paths to append. * @param pathList String to receive the search path list. @@ -163,9 +191,9 @@ namespace de char delimiter = ';') const; /** - * Append to @a pathList all resolved search paths from all groups, firstly - * in group order (from Override to Fallback) and in descending, (i.e., most - * recent order), using @a delimiter to separate each. + * Append to @a pathList all resolved search paths from all groups, + * firstly in priority group order (from Override to Fallback) and + * secondly in descending (i.e., most recent) order. * * @param pathList String to receive the search paths. * @param delimiter Discreet paths will be delimited by this character. diff --git a/doomsday/engine/portable/include/sys_reslocator.h b/doomsday/engine/portable/include/sys_reslocator.h index e1ff5e7c58..f332b135ae 100644 --- a/doomsday/engine/portable/include/sys_reslocator.h +++ b/doomsday/engine/portable/include/sys_reslocator.h @@ -125,12 +125,6 @@ void F_ResetResourceNamespace(resourcenamespaceid_t rni); void F_CreateNamespacesForFileResourcePaths(void); -/** - * @return Newly created hash name. Ownership passes to the caller who should - * ensure to release it with Str_Delete when done. - */ -AutoStr* F_ComposeHashNameForFilePath(Str const* filePath); - ddstring_t const* F_ResourceNamespaceName(resourcenamespaceid_t rni); /// @return Number of resource namespaces. @@ -144,16 +138,15 @@ boolean F_IsValidResourceNamespaceId(int value); * Given an id return the associated resource namespace object. */ ResourceNamespace* F_ToResourceNamespace(resourcenamespaceid_t rni); +#endif /** * @param rni Unique identifier of the namespace to add to. * @param flags @see searchPathFlags * @param searchPath Uri representing the search path to be added. - * @param group Group to add the new search path to. */ -boolean F_AddSearchPathToResourceNamespace(resourcenamespaceid_t rni, int flags, - Uri const* searchPath, de::ResourceNamespace::SearchPathGroup group); -#endif +boolean F_AddExtraSearchPathToResourceNamespace(resourcenamespaceid_t rni, int flags, + Uri const* searchPath); /** * Attempt to locate a known resource. diff --git a/doomsday/engine/portable/src/dd_main.cpp b/doomsday/engine/portable/src/dd_main.cpp index 6f2c45f186..ff611d5529 100644 --- a/doomsday/engine/portable/src/dd_main.cpp +++ b/doomsday/engine/portable/src/dd_main.cpp @@ -1331,6 +1331,8 @@ void DD_FinishInitializationAfterWindowReady(void) Window_SetDrawFunc(Window_Main(), DD_GameLoopDrawer); } +extern ResourceNamespace* F_ToResourceNamespace(resourcenamespaceid_t rni); + /** * Engine initialization. After completed, the game loop is ready to be started. * Called from the app entrypoint function. @@ -1424,12 +1426,12 @@ boolean DD_Init(void) while(++p != CommandLine_Count() && !CommandLine_IsOption(p)) { - const char* filePath = CommandLine_PathAt(p); + char const* filePath = CommandLine_PathAt(p); directory_t* dir; Uri* searchPath; - /// @todo Do not add these as search paths, publish them directly to - /// the FileDirectory owned by the "packages" ResourceNamespace. + /// @todo Do not add these as search paths, publish them directly + /// to the "packages" ResourceNamespace. dir = Dir_ConstructFromPathDir(filePath); searchPath = Uri_NewWithPath2(Dir_Path(dir), RC_PACKAGE); diff --git a/doomsday/engine/portable/src/def_read.c b/doomsday/engine/portable/src/def_read.c index 88145c5834..5de34a98d6 100644 --- a/doomsday/engine/portable/src/def_read.c +++ b/doomsday/engine/portable/src/def_read.c @@ -845,8 +845,8 @@ static int DED_ReadData(ded_t* ded, const char* buffer, const char* _sourceFile) CHECKSC; newSearchPath = Uri_NewWithPath2(label, RC_NULL); - F_AddSearchPathToResourceNamespace(F_DefaultResourceNamespaceForClass(RC_MODEL), - 0, newSearchPath, SPG_EXTRA); + F_AddExtraSearchPathToResourceNamespace(F_DefaultResourceNamespaceForClass(RC_MODEL), + 0, newSearchPath); Uri_Delete(newSearchPath); } diff --git a/doomsday/engine/portable/src/resourcenamespace.cpp b/doomsday/engine/portable/src/resourcenamespace.cpp index 5f6cea4f9b..c5416c6f30 100644 --- a/doomsday/engine/portable/src/resourcenamespace.cpp +++ b/doomsday/engine/portable/src/resourcenamespace.cpp @@ -21,13 +21,18 @@ * 02110-1301 USA */ -#include "de_platform.h" -#include "de_console.h" +#include + #include "de_filesys.h" +#include +#include "filedirectory.h" #include "pathtree.h" + #include "resourcenamespace.h" +extern "C" filename_t ddBasePath; + namespace de { struct ResourceNode @@ -65,6 +70,13 @@ struct ResourceNode */ struct NameHash { +public: + /// Type used to represent hash keys. + typedef unsigned short key_type; + + /// Number of buckets in the hash table. + static unsigned short const hash_size = 512; + struct Node { Node* next; @@ -80,7 +92,15 @@ struct NameHash Node* first; Node* last; }; - Bucket buckets[RESOURCENAMESPACE_HASHSIZE]; + +public: + Bucket buckets[hash_size]; + +public: + NameHash() + { + memset(buckets, 0, sizeof(buckets)); + } ~NameHash() { @@ -89,7 +109,7 @@ struct NameHash void clear() { - for(uint i = 0; i < RESOURCENAMESPACE_HASHSIZE; ++i) + for(uint i = 0; i < hash_size; ++i) { while(buckets[i].first) { @@ -102,10 +122,62 @@ struct NameHash } }; +//unsigned short const NameHash::hash_size; + +static AutoStr* composeResourceName(ddstring_t const* filePath) +{ + AutoStr* name = AutoStr_NewStd(); + F_FileName(name, Str_Text(filePath)); + return name; +} + +/** + * This is a hash function. It uses the resource name to generate a + * somewhat-random number between 0 and NameHash::hash_size. + * + * @return The generated hash key. + */ +static NameHash::key_type hashResourceName(ddstring_t const* name) +{ + DENG_ASSERT(name); + + NameHash::key_type key = 0; + byte op = 0; + + for(char const* c = Str_Text(name); *c; c++) + { + switch(op) + { + case 0: key ^= tolower(*c); ++op; break; + case 1: key *= tolower(*c); ++op; break; + case 2: key -= tolower(*c); op=0; break; + } + } + return key % NameHash::hash_size; +} + +static int addResourceWorker(PathTree::Node& node, void* parameters) +{ + // We are only interested in leafs (i.e., files and not directories). + if(node.type() == PathTree::Leaf) + { + ResourceNamespace& rnamespace = *((ResourceNamespace*) parameters); + rnamespace.add(node); + } + return 0; // Continue adding. +} + +#define RNF_IS_DIRTY 0x80 // Filehash needs to be (re)built (avoid allocating an empty name hash). + struct ResourceNamespace::Instance { - /// Resource name hashing callback. - ResourceNamespace::HashNameFunc* hashNameFunc; + ResourceNamespace& self; + + /// Associated path directory for this namespace. + /// @todo It should not be necessary for a unique directory per namespace. + FileDirectory* directory; + + byte flags; /// Name hash table. NameHash nameHash; @@ -114,11 +186,23 @@ struct ResourceNamespace::Instance /// Each set is in order of greatest-importance, right to left. ResourceNamespace::SearchPaths searchPaths; - Instance(ResourceNamespace::HashNameFunc* _hashNameFunc) - : hashNameFunc(_hashNameFunc) - {} + /// Symbolic name of this namespace. + ddstring_t name; + + Instance(ResourceNamespace& d, char const* _name) + : self(d), directory(new FileDirectory(ddBasePath)), flags(RNF_IS_DIRTY) + { + Str_InitStd(&name); + if(_name) Str_Set(&name, _name); + } + + ~Instance() + { + Str_Free(&name); + delete directory; + } - NameHash::Node* findResourceInNameHash(ResourceNamespace::NameHashKey key, + NameHash::Node* findResourceInNameHash(NameHash::key_type key, PathTree::Node const& ptNode) { NameHash::Node* node = nameHash.buckets[key].first; @@ -128,12 +212,21 @@ struct ResourceNamespace::Instance } return node; } + + void addFromSearchPaths(ResourceNamespace::PathGroup group) + { + for(ResourceNamespace::SearchPaths::const_iterator i = searchPaths.find(group); + i != searchPaths.end() && i.key() == group; ++i) + { + ResourceNamespace::SearchPath const& searchPath = *i; + directory->addPath(searchPath.flags(), searchPath.uri(), addResourceWorker, (void*)&self); + } + } }; -ResourceNamespace::ResourceNamespace(ResourceNamespace::HashNameFunc* hashNameFunc) +ResourceNamespace::ResourceNamespace(char const* symbolicName) { - DENG_ASSERT(hashNameFunc); - d = new Instance(hashNameFunc); + d = new Instance(*this, symbolicName); } ResourceNamespace::~ResourceNamespace() @@ -141,21 +234,50 @@ ResourceNamespace::~ResourceNamespace() delete d; } +ddstring_t const* ResourceNamespace::name() const +{ + return &d->name; +} + void ResourceNamespace::clear() { d->nameHash.clear(); + d->directory->clear(); + d->flags |= RNF_IS_DIRTY; } -bool ResourceNamespace::add(ddstring_t const* name, PathTree::Node& resourceNode) +void ResourceNamespace::rebuild() { - DENG_ASSERT(name); + // Is a rebuild necessary? + if(!(d->flags & RNF_IS_DIRTY)) return; + +/*#if _DEBUG + uint startTime; + VERBOSE( Con_Message("Rebuilding ResourceNamespace '%s'...\n", Str_Text(d->name)) ) + VERBOSE2( startTime = Sys_GetRealTime() ) + VERBOSE2( Con_PrintPathList(Str_Text(listSearchPaths(AutoStr_NewStd()))) ) +#endif*/ + + // (Re)populate the directory and add found resources. + clear(); + d->addFromSearchPaths(ResourceNamespace::OverridePaths); + d->addFromSearchPaths(ResourceNamespace::ExtraPaths); + d->addFromSearchPaths(ResourceNamespace::DefaultPaths); + d->addFromSearchPaths(ResourceNamespace::FallbackPaths); + + d->flags &= ~RNF_IS_DIRTY; + +/*#if _DEBUG + VERBOSE2( FileDirectory::debugPrint(d->directory) ) + VERBOSE2( debugPrint() ) + VERBOSE2( Con_Message(" Done in %.2f seconds.\n", (Sys_GetRealTime() - startTime) / 1000.0f) ) +#endif*/ +} - NameHashKey key = d->hashNameFunc(name); - if(key >= RESOURCENAMESPACE_HASHSIZE) - { - Con_Error("ResourceNamespace::Add: Hashing of name '%s' in [%p] produced invalid key %u.", Str_Text(name), (void*)this, (unsigned short)key); - exit(1); // Unreachable. - } +bool ResourceNamespace::add(PathTree::Node& resourceNode) +{ + AutoStr* name = composeResourceName(resourceNode.name()); + NameHash::key_type key = hashResourceName(name); // Is this a new resource? bool isNewNode = false; @@ -176,6 +298,10 @@ bool ResourceNamespace::add(ddstring_t const* name, PathTree::Node& resourceNode if(bucket.last) bucket.last->next = hashNode; bucket.last = hashNode; if(!bucket.first) bucket.first = hashNode; + + // We will need to rebuild this namespace (if we aren't already doing so, + // in the case of auto-populated namespaces built from FileDirectorys). + d->flags |= RNF_IS_DIRTY; } // (Re)configure this record. @@ -185,11 +311,6 @@ bool ResourceNamespace::add(ddstring_t const* name, PathTree::Node& resourceNode return isNewNode; } -ResourceNamespace::SearchPaths const& ResourceNamespace::searchPaths() const -{ - return d->searchPaths; -} - bool ResourceNamespace::addSearchPath(PathGroup group, Uri const* _searchPath, int flags) { // Is this suitable? @@ -203,12 +324,15 @@ bool ResourceNamespace::addSearchPath(PathGroup group, Uri const* _searchPath, i Uri* searchPath = Uri_NewWithPath2(Str_Text(path), RC_NULL); + // The addition of a new search path means the namespace is now dirty. + d->flags |= RNF_IS_DIRTY; + // Have we seen this path already (we don't want duplicates)? DENG2_FOR_EACH(SearchPaths, i, d->searchPaths) { - if(Uri_Equality(i->uri, searchPath)) + if(Uri_Equality(i->uri(), searchPath)) { - i->flags = flags; + i->setFlags(flags); Uri_Delete(searchPath); return true; } @@ -235,67 +359,76 @@ ddstring_t* ResourceNamespace::listSearchPaths(PathGroup group, ddstring_t* path { if(pathList) { - ResourceNamespace::SearchPaths::const_iterator i = d->searchPaths.find(group); - while(i != d->searchPaths.end() && i.key() == group) + for(ResourceNamespace::SearchPaths::const_iterator i = d->searchPaths.find(group); + i != d->searchPaths.end() && i.key() == group; ++i) { - AutoStr* path = Uri_Compose(i.value().uri); + AutoStr* path = Uri_Compose(i.value().uri()); Str_Appendf(pathList, "%s%c", Str_Text(path), delimiter); } } return pathList; } +ResourceNamespace::SearchPaths const& ResourceNamespace::searchPaths() const +{ + return d->searchPaths; +} + #if _DEBUG void ResourceNamespace::debugPrint() const { - Con_Printf("ResourceNamespace [%p]:\n", (void*)this); + LOG_AS("ResourceNamespace::debugPrint"); + LOG_DEBUG("[%p]:") << de::dintptr(this); + uint resIdx = 0; - for(uint key = 0; key < RESOURCENAMESPACE_HASHSIZE; ++key) + for(NameHash::key_type key = 0; key < NameHash::hash_size; ++key) { NameHash::Bucket& bucket = d->nameHash.buckets[key]; for(NameHash::Node* node = bucket.first; node; node = node->next) { - Con_Printf(" %lu: %lu:", (unsigned long)resIdx, (unsigned long)key); - ResourceNode const& res = node->resource; - AutoStr* path = res.directoryNode->composePath(AutoStr_NewStd(), NULL, DIR_SEP_CHAR); - Con_Printf("\"%s\" -> %s\n", Str_Text(&res.name), Str_Text(path)); + AutoStr* path = res.directoryNode->composePath(AutoStr_NewStd(), NULL); + + LOG_DEBUG(" %u - %u:\"%s\" => %s") + << resIdx << key << Str_Text(&res.name) << Str_Text(path); ++resIdx; } } - Con_Printf(" %lu %s in namespace.\n", (unsigned long) resIdx, (resIdx == 1? "resource" : "resources")); + + LOG_DEBUG(" %lu %s in namespace.") << resIdx << (resIdx == 1? "resource" : "resources"); } #endif -int ResourceNamespace::findAll(ddstring_t const* name, ResourceList& found) +int ResourceNamespace::findAll(ddstring_t const* searchPath, ResourceList& found) { int numFoundSoFar = found.count(); - NameHashKey from, to; + AutoStr* name = 0; + if(searchPath && !Str_IsEmpty(searchPath)) + { + name = composeResourceName(searchPath); + } + + NameHash::key_type from, to; if(name) { - from = d->hashNameFunc(name); - if(from >= RESOURCENAMESPACE_HASHSIZE) - { - Con_Error("ResourceNamespace::Iterate: Hashing of name '%s' in [%p] produced invalid key %u.", Str_Text(name), (void*)this, (unsigned short)from); - exit(1); // Unreachable. - } - to = from; + from = hashResourceName(name); + to = from; } else { from = 0; - to = RESOURCENAMESPACE_HASHSIZE - 1; + to = NameHash::hash_size - 1; } - for(NameHashKey key = from; key < to + 1; ++key) + for(NameHash::key_type key = from; key < to + 1; ++key) { NameHash::Bucket& bucket = d->nameHash.buckets[key]; for(NameHash::Node* hashNode = bucket.first; hashNode; hashNode = hashNode->next) { ResourceNode& resource = hashNode->resource; - if(Str_CompareIgnoreCase(resource.directoryNode->name(), Str_Text(name))) continue; + if(name && qstrnicmp(Str_Text(name), Str_Text(resource.directoryNode->name()), Str_Length(name))) continue; found.push_back(resource.directoryNode); } @@ -304,4 +437,45 @@ int ResourceNamespace::findAll(ddstring_t const* name, ResourceList& found) return found.count() - numFoundSoFar; } +ResourceNamespace::SearchPath::SearchPath(int _flags, Uri* _uri) + : flags_(_flags), uri_(_uri) +{} + +ResourceNamespace::SearchPath::SearchPath(SearchPath const& other) + : flags_(other.flags_), uri_(Uri_NewCopy(other.uri_)) +{} + +ResourceNamespace::SearchPath::~SearchPath() +{ + Uri_Delete(uri_); +} + +ResourceNamespace::SearchPath& ResourceNamespace::SearchPath::operator = (SearchPath other) +{ + swap(*this, other); + return *this; +} + +int ResourceNamespace::SearchPath::flags() const +{ + return flags_; +} + +ResourceNamespace::SearchPath& ResourceNamespace::SearchPath::setFlags(int newFlags) +{ + flags_ = newFlags; + return *this; +} + +Uri const* ResourceNamespace::SearchPath::uri() const +{ + return uri_; +} + +void swap(ResourceNamespace::SearchPath& first, ResourceNamespace::SearchPath& second) +{ + std::swap(first.flags_, second.flags_); + std::swap(first.uri_, second.uri_); +} + } // namespace de diff --git a/doomsday/engine/portable/src/sys_reslocator.cpp b/doomsday/engine/portable/src/sys_reslocator.cpp index 773275736e..b2697afda1 100644 --- a/doomsday/engine/portable/src/sys_reslocator.cpp +++ b/doomsday/engine/portable/src/sys_reslocator.cpp @@ -40,9 +40,6 @@ #include "de_base.h" #include "de_console.h" #include "de_filesys.h" -//#include "m_misc.h" - -#include "filedirectory.h" #include "abstractresource.h" #include "resourcenamespace.h" @@ -66,26 +63,13 @@ typedef struct { * @{ */ #define RNF_USE_VMAP 0x01 // Map resources in packages. -#define RNF_IS_DIRTY 0x80 // Filehash needs to be (re)built (avoid allocating an empty name hash). ///@} -#define RESOURCENAMESPACE_MINNAMELENGTH URI_MINSCHEMELENGTH - struct ResourceNamespaceInfo { - /// Unique symbolic name of this namespace (e.g., "Models"). - /// Must be at least @c RESOURCENAMESPACE_MINNAMELENGTH characters long. - ddstring_t name; - /// ResourceNamespace. ResourceNamespace* rnamespace; - /// Associated path directory for this namespace. - de::FileDirectory* directory; - - /// Algorithm used to compose the name of a resource in this namespace. - AutoStr* (*composeName) (ddstring_t const* path); - byte flags; // @see resourceNamespaceFlags }; @@ -135,19 +119,13 @@ static de::Str const defaultNamespaceForClass[RESOURCECLASS_COUNT] = { static ResourceNamespaceInfo* namespaces = 0; static uint numNamespaces = 0; +#define RESOURCENAMESPACE_MINNAMELENGTH URI_MINSCHEMELENGTH + /** - * This is a hash function. It uses the resource name to generate a - * somewhat-random number between 0 and RESOURCENAMESPACE_HASHSIZE. - * - * @return The generated hash key. + * @param name Unique symbolic name of this namespace. Must be at least + * @c RESOURCENAMESPACE_MINNAMELENGTH characters long. */ -ResourceNamespace::NameHashKey F_HashKeyForAlphaNumericNameIgnoreCase(ddstring_t const* name); - -#define F_HashKeyForFilePathHashName F_HashKeyForAlphaNumericNameIgnoreCase - -ResourceNamespace* F_CreateResourceNamespace(char const* name, - de::FileDirectory& directory, AutoStr* (*composeHashNameFunc) (ddstring_t const* path), - ResourceNamespace::HashNameFunc* hashNameFunc, byte flags); +ResourceNamespace* F_CreateResourceNamespace(char const* name, byte flags); static void errorIfNotInited(const char* callerName) { @@ -171,22 +149,6 @@ static inline ResourceNamespaceInfo* getNamespaceInfoForId(resourcenamespaceid_t return &namespaces[((uint)rni)-1]; } -#if 0 -static resourcenamespaceid_t findNamespaceId(ResourceNamespace* rnamespace) -{ - if(rnamespace) - { - for(uint i = 0; i < numNamespaces; ++i) - { - ResourceNamespaceInfo* info = &namespaces[i]; - if(info->rnamespace == rnamespace) - return (resourcenamespaceid_t)(i+1); - } - } - return 0; -} -#endif - static resourcenamespaceid_t findNamespaceForName(char const* name) { if(name && name[0]) @@ -194,7 +156,7 @@ static resourcenamespaceid_t findNamespaceForName(char const* name) for(uint i = 0; i < numNamespaces; ++i) { ResourceNamespaceInfo* info = &namespaces[i]; - if(!stricmp(Str_Text(&info->name), name)) + if(!stricmp(Str_Text(info->rnamespace->name()), name)) return (resourcenamespaceid_t)(i+1); } } @@ -208,9 +170,7 @@ static void destroyAllNamespaces(void) for(uint i = 0; i < numNamespaces; ++i) { ResourceNamespaceInfo* info = &namespaces[i]; - if(info->directory) delete info->directory; delete info->rnamespace; - Str_Free(&info->name); } M_Free(namespaces); namespaces = 0; @@ -225,136 +185,49 @@ static void resetAllNamespaces(void) } } -static void addResourceToNamespace(ResourceNamespaceInfo& rnInfo, de::PathTree::Node& node) -{ - AutoStr* name = rnInfo.composeName(node.name()); - if(rnInfo.rnamespace->add(name, node)) - { - // We will need to rebuild this namespace (if we aren't already doing so, - // in the case of auto-populated namespaces built from FileDirectorys). - rnInfo.flags |= RNF_IS_DIRTY; - } -} - -static int addFileResourceWorker(de::PathTree::Node& node, void* parameters) -{ - ResourceNamespaceInfo* rnInfo = (ResourceNamespaceInfo*) parameters; - // We are only interested in leafs (i.e., files and not directories). - if(node.type() == de::PathTree::Leaf) - { - addResourceToNamespace(*rnInfo, node); - } - return 0; // Continue adding. -} - -static void addResourcesOnSearchPathsToDirectory(ResourceNamespaceInfo* rnInfo, - ResourceNamespace::PathGroup group) -{ - ResourceNamespace& rnamespace = *rnInfo->rnamespace; - ResourceNamespace::SearchPaths const& searchPaths = rnamespace.searchPaths(); - ResourceNamespace::SearchPaths::const_iterator i = searchPaths.find(group); - while(i != searchPaths.end() && i.key() == group) - { - ResourceNamespace::SearchPath const& searchPath = *i; - rnInfo->directory->addPath(searchPath.flags, searchPath.uri, addFileResourceWorker, (void*)rnInfo); - } -} - -static void rebuildResourceNamespace(ResourceNamespaceInfo* rnInfo) -{ -/*#if _DEBUG - uint startTime; -#endif*/ - - DENG_ASSERT(rnInfo); - if(!(rnInfo->flags & RNF_IS_DIRTY)) return; - - ResourceNamespace& rnamespace = *rnInfo->rnamespace; - -/*#if _DEBUG - VERBOSE( Con_Message("Rebuilding rnamespace '%s'...\n", Str_Text(&rnInfo->name)) ) - VERBOSE2( startTime = Sys_GetRealTime() ) -#endif*/ - - // (Re)populate the directory and insert found paths into the resource namespace. - /// @todo It should not be necessary for a unique directory per namespace. - - rnamespace.clear(); - rnInfo->directory->clear(); - -/*#if _DEBUG - VERBOSE2( Con_PrintPathList(Str_Text(rnamespace.listSearchPaths(AutoStr_NewStd()))) ) -#endif*/ - - addResourcesOnSearchPathsToDirectory(rnInfo, ResourceNamespace::OverridePaths); - addResourcesOnSearchPathsToDirectory(rnInfo, ResourceNamespace::ExtraPaths); - addResourcesOnSearchPathsToDirectory(rnInfo, ResourceNamespace::DefaultPaths); - addResourcesOnSearchPathsToDirectory(rnInfo, ResourceNamespace::FallbackPaths); - - rnInfo->flags &= ~RNF_IS_DIRTY; - -/*#if _DEBUG - VERBOSE2( FileDirectory::debugPrint(rnInfo->directory) ) - VERBOSE2( rnamespace.debugPrint() ) - VERBOSE2( Con_Message(" Done in %.2f seconds.\n", (Sys_GetRealTime() - startTime) / 1000.0f) ) -#endif*/ -} - /** * Find a named resource in this namespace. * - * @param name Name of the resource being searched for. * @param searchPath Relative or absolute path to the resource. * @param searchDelimiter Fragments of @a searchPath are delimited by this character. - * @param foundPath If not @c NULL and a path is found, it is written back here. - * @param foundDelimiter Delimiter to be used when composing @a foundPath. * - * @return @c true iff a resource was found. + * @return The found PathTree node which represents the resource else @c NULL. */ -static bool findResourceInNamespace(ResourceNamespaceInfo* rnInfo, ddstring_t const * name, - ddstring_t const* searchPath, char delimiter, ddstring_t* foundPath, char foundDelimiter) +static de::PathTree::Node* findResourceInNamespace(ResourceNamespace& rnamespace, + ddstring_t const* searchPath, char delimiter) { - DENG_ASSERT(rnInfo && name); - - if(!searchPath || Str_IsEmpty(searchPath)) return false; + if(!searchPath || Str_IsEmpty(searchPath)) return 0; // Ensure the namespace is up to date. - rebuildResourceNamespace(rnInfo); - ResourceNamespace& rnamespace = *rnInfo->rnamespace; + rnamespace.rebuild(); // Perform the search. - bool foundResource = false; - ResourceNamespace::ResourceList found; - if(rnamespace.findAll(name, found)) + de::PathTree::Node* found = 0; + ResourceNamespace::ResourceList foundResources; + if(rnamespace.findAll(searchPath, foundResources)) { - // There is at least on name-matched resource. + // There is at least one name-matched (perhaps partially) resource. PathMap searchPattern; PathMap_Initialize2(&searchPattern, de::PathTree::hashPathFragment, Str_Text(searchPath), delimiter); - DENG2_FOR_EACH_CONST(ResourceNamespace::ResourceList, i, found) + DENG2_FOR_EACH_CONST(ResourceNamespace::ResourceList, i, foundResources) { - de::PathTree::Node& node = **i; - if(!node.comparePath(&searchPattern, PCF_NO_BRANCH)) continue; + de::PathTree::Node* node = *i; + if(!node->comparePath(&searchPattern, PCF_NO_BRANCH)) continue; - /* - * This is the resource we are looking for. - */ - - // Does the caller want to know the matched path? - if(foundPath) node.composePath(foundPath, NULL, foundDelimiter); - - foundResource = true; + // This is the resource we are looking for. + found = node; break; } // Cleanup. PathMap_Destroy(&searchPattern); } - return foundResource; + return found; } static bool tryFindResource2(resourceclass_t rclass, ddstring_t const* rawSearchPath, - ddstring_t* foundPath, ResourceNamespaceInfo* rnamespaceInfo) + ddstring_t* foundPath, ResourceNamespaceInfo* rnInfo) { DENG_ASSERT(inited && rawSearchPath && !Str_IsEmpty(rawSearchPath)); DENG_UNUSED(rclass); @@ -363,12 +236,17 @@ static bool tryFindResource2(resourceclass_t rclass, ddstring_t const* rawSearch F_FixSlashes(searchPath, rawSearchPath); // Is there a namespace we should use? - if(rnamespaceInfo) + if(rnInfo) { - AutoStr* name = rnamespaceInfo->composeName(searchPath); - if(findResourceInNamespace(rnamespaceInfo, name, searchPath, '/', foundPath, '/')) + ResourceNamespace& rnamespace = *rnInfo->rnamespace; + if(de::PathTree::Node* found = findResourceInNamespace(rnamespace, searchPath, '/')) { - if(foundPath) F_PrependBasePath(foundPath, foundPath); + // Does the caller want to know the matched path? + if(foundPath) + { + found->composePath(foundPath, NULL, '/'); + F_PrependBasePath(foundPath, foundPath); + } return true; } } @@ -452,7 +330,7 @@ static bool findResource2(resourceclass_t rclass, ddstring_t const* searchPath, DENG_ASSERT(inited && searchPath && !Str_IsEmpty(searchPath)); #if _DEBUG - VERBOSE2( Con_Message("Using rnamespace '%s'...\n", rnamespaceInfo? Str_Text(&rnamespaceInfo->name) : "None") ) + VERBOSE2( Con_Message("Using rnamespace '%s'...\n", rnamespaceInfo? Str_Text(rnamespaceInfo->rnamespace->name()) : "None") ) #endif bool found = false; @@ -544,32 +422,6 @@ static int findResource(resourceclass_t rclass, Uri const* const* list, return result; } -AutoStr* F_ComposeHashNameForFilePath(ddstring_t const* filePath) -{ - AutoStr* hashName = AutoStr_NewStd(); - F_FileName(hashName, Str_Text(filePath)); - return hashName; -} - -ResourceNamespace::NameHashKey F_HashKeyForAlphaNumericNameIgnoreCase(ddstring_t const* name) -{ - DENG_ASSERT(name); - - ResourceNamespace::NameHashKey key = 0; - byte op = 0; - - for(char const* c = Str_Text(name); *c; c++) - { - switch(op) - { - case 0: key ^= tolower(*c); ++op; break; - case 1: key *= tolower(*c); ++op; break; - case 2: key -= tolower(*c); op=0; break; - } - } - return key % RESOURCENAMESPACE_HASHSIZE; -} - static void createPackagesResourceNamespace(void) { ddstring_t** doomWadPaths = 0, *doomWadDir = 0; @@ -688,10 +540,7 @@ static void createPackagesResourceNamespace(void) Str_Delete(doomWadDir); } - de::FileDirectory* directory = new de::FileDirectory(ddBasePath); - ResourceNamespace* rnamespace = F_CreateResourceNamespace(PACKAGES_RESOURCE_NAMESPACE_NAME, *directory, - F_ComposeHashNameForFilePath, - F_HashKeyForFilePathHashName, 0); + ResourceNamespace* rnamespace = F_CreateResourceNamespace(PACKAGES_RESOURCE_NAMESPACE_NAME, 0); if(searchPathsCount != 0) { for(uint i = 0; i < searchPathsCount; ++i) @@ -762,10 +611,7 @@ void F_CreateNamespacesForFileResourcePaths(void) { uint j, searchPathCount; struct namespacedef_s* def = &defs[i]; - de::FileDirectory* directory = new de::FileDirectory(ddBasePath); - ResourceNamespace* rnamespace = F_CreateResourceNamespace(def->name, *directory, - F_ComposeHashNameForFilePath, - F_HashKeyForFilePathHashName, def->flags); + ResourceNamespace* rnamespace = F_CreateResourceNamespace(def->name, def->flags); searchPathCount = 0; while(def->searchPaths[searchPathCount] && ++searchPathCount < NAMESPACEDEF_MAX_SEARCHPATHS) @@ -830,13 +676,9 @@ void F_ResetResourceNamespace(resourcenamespaceid_t rni) if(!F_IsValidResourceNamespaceId(rni)) return; ResourceNamespaceInfo* info = getNamespaceInfoForId(rni); - info->rnamespace->clearSearchPaths(ResourceNamespace::ExtraPaths); - info->rnamespace->clear(); - if(info->directory) - { - info->directory->clear(); - } - info->flags |= RNF_IS_DIRTY; + ResourceNamespace& rnamespace = *info->rnamespace; + rnamespace.clearSearchPaths(ResourceNamespace::ExtraPaths); + rnamespace.clear(); } ResourceNamespace* F_ToResourceNamespace(resourcenamespaceid_t rni) @@ -870,17 +712,15 @@ boolean F_IsValidResourceNamespaceId(int val) return (boolean)(val > 0 && (unsigned)val < (F_NumResourceNamespaces() + 1)? 1 : 0); } -ResourceNamespace* F_CreateResourceNamespace(char const* name, de::FileDirectory& directory, - AutoStr* (*composeNameFunc) (ddstring_t const* path), - ResourceNamespace::HashNameFunc* hashNameFunc, byte flags) +ResourceNamespace* F_CreateResourceNamespace(char const* name, byte flags) { - DENG_ASSERT(name && composeNameFunc); + DENG_ASSERT(name); errorIfNotInited("F_CreateResourceNamespace"); if(strlen(name) < RESOURCENAMESPACE_MINNAMELENGTH) Con_Error("F_CreateResourceNamespace: Invalid name '%s' (min length:%i)", name, (int)RESOURCENAMESPACE_MINNAMELENGTH); - ResourceNamespace* rn = new ResourceNamespace(hashNameFunc); + ResourceNamespace* rn = new ResourceNamespace(name); // Add this new namespace to the global list. namespaces = (ResourceNamespaceInfo*) M_Realloc(namespaces, sizeof *namespaces * ++numNamespaces); @@ -889,33 +729,25 @@ ResourceNamespace* F_CreateResourceNamespace(char const* name, de::FileDirectory (unsigned long) sizeof *namespaces * numNamespaces); ResourceNamespaceInfo* info = &namespaces[numNamespaces-1]; - Str_Init(&info->name); - Str_Set(&info->name, name); info->rnamespace = rn; - info->directory = &directory; - info->composeName = composeNameFunc; - info->flags = flags | RNF_IS_DIRTY; + info->flags = flags; return rn; } -boolean F_AddSearchPathToResourceNamespace(resourcenamespaceid_t rni, int flags, - Uri const* searchPath, ResourceNamespace::PathGroup group) +boolean F_AddExtraSearchPathToResourceNamespace(resourcenamespaceid_t rni, int flags, + Uri const* searchPath) { errorIfNotInited("F_AddSearchPathToResourceNamespace"); ResourceNamespaceInfo* info = getNamespaceInfoForId(rni); - if(info->rnamespace->addSearchPath(group, searchPath, flags)) - { - info->flags |= RNF_IS_DIRTY; - return true; - } - return false; + ResourceNamespace& rnamespace = *info->rnamespace; + return rnamespace.addSearchPath(ResourceNamespace::ExtraPaths, searchPath, flags); } ddstring_t const* F_ResourceNamespaceName(resourcenamespaceid_t rni) { - return &(getNamespaceInfoForId(rni))->name; + return (getNamespaceInfoForId(rni))->rnamespace->name(); } Uri** F_CreateUriList2(resourceclass_t rclass, char const* searchPaths, int* count) @@ -1233,11 +1065,14 @@ boolean F_MapResourcePath(resourcenamespaceid_t rni, ddstring_t* path) if(path && !Str_IsEmpty(path)) { ResourceNamespaceInfo* info = getNamespaceInfoForId(rni); + ResourceNamespace& rnamespace = *info->rnamespace; if(info->flags & RNF_USE_VMAP) { - int nameLen = Str_Length(&info->name), pathLen = Str_Length(path); + int const nameLen = Str_Length(rnamespace.name()); + int const pathLen = Str_Length(path); + if(nameLen <= pathLen && Str_At(path, nameLen) == '/' && - !strnicmp(Str_Text(&info->name), Str_Text(path), nameLen)) + !strnicmp(Str_Text(rnamespace.name()), Str_Text(path), nameLen)) { Str_Prepend(path, Str_Text(&reinterpret_cast(App_CurrentGame())->dataPath())); return true;