From 26e298d604fbc05d22fd4ab21b07b7a071a8626e Mon Sep 17 00:00:00 2001 From: danij Date: Tue, 1 Nov 2011 04:22:38 +0000 Subject: [PATCH] Refactoring resource locator in preparation for finding non-file resources Redesigned ResourceNamespace. Namespace rebuilding has been moved out of this class. New mechanics for adding and iteration of existing resources have also been implemented. Redesigned interface for searching PathDirectory. The previous implementation had the shortcoming that only one instance of PathDirectory was searchable at a time. This revised implementation separates the construction of the optimized search term from the search iteration process. --- doomsday/engine/portable/include/dd_main.h | 13 - .../engine/portable/include/filedirectory.h | 2 + .../engine/portable/include/pathdirectory.h | 179 ++++-- .../portable/include/resourcenamespace.h | 88 ++- .../engine/portable/include/sys_reslocator.h | 25 +- doomsday/engine/portable/src/con_data.c | 2 +- doomsday/engine/portable/src/dd_main.c | 28 - doomsday/engine/portable/src/def_read.c | 2 +- doomsday/engine/portable/src/filedirectory.c | 12 +- doomsday/engine/portable/src/fonts.c | 2 +- doomsday/engine/portable/src/fs_main.c | 5 +- doomsday/engine/portable/src/gl_texmanager.c | 4 +- .../engine/portable/src/p_materialmanager.c | 2 +- doomsday/engine/portable/src/pathdirectory.c | 563 +++++++++--------- .../engine/portable/src/resourcenamespace.c | 393 +++++------- doomsday/engine/portable/src/sys_reslocator.c | 377 +++++++++--- doomsday/engine/portable/src/uri.c | 4 +- 17 files changed, 975 insertions(+), 726 deletions(-) diff --git a/doomsday/engine/portable/include/dd_main.h b/doomsday/engine/portable/include/dd_main.h index c050fe12cf..83d7893f89 100644 --- a/doomsday/engine/portable/include/dd_main.h +++ b/doomsday/engine/portable/include/dd_main.h @@ -93,19 +93,6 @@ fontnamespaceid_t DD_ParseFontNamespace(const char* str); struct material_s* DD_MaterialForTextureIndex(uint index, texturenamespaceid_t texNamespace); -int DD_SearchPathDirectoryCompare(struct pathdirectory_node_s* node, void* paramaters); - -/** - * Search the directory for @a searchPath. - * - * @param flags @see pathComparisonFlags - * @param searchPath Relative or absolute path. - * @param delimiter Fragments of the path are delimited by this character. - * - * @return Pointer to the associated node iff found else @c 0 - */ -struct pathdirectory_node_s* DD_SearchPathDirectory(struct pathdirectory_s* pd, int flags, const char* searchPath, char delimiter); - const char* value_Str(int val); /** diff --git a/doomsday/engine/portable/include/filedirectory.h b/doomsday/engine/portable/include/filedirectory.h index 4bfde1d2ff..1a95bff067 100644 --- a/doomsday/engine/portable/include/filedirectory.h +++ b/doomsday/engine/portable/include/filedirectory.h @@ -140,7 +140,9 @@ int FileDirectory_Iterate_Const(const filedirectory_t* fd, pathdirectory_nodetyp const struct pathdirectory_node_s* parent, ushort hash, int (*callback) (const struct pathdirectory_node_s* node, void* paramaters)); +#if _DEBUG void FileDirectory_Print(filedirectory_t* fd); void FileDirectory_PrintHashDistribution(filedirectory_t* fd); +#endif #endif /* LIBDENG_FILEDIRECTORY_H */ diff --git a/doomsday/engine/portable/include/pathdirectory.h b/doomsday/engine/portable/include/pathdirectory.h index ba2b23d480..8459fcbe3a 100644 --- a/doomsday/engine/portable/include/pathdirectory.h +++ b/doomsday/engine/portable/include/pathdirectory.h @@ -55,23 +55,92 @@ typedef enum { /**@}*/ /** - * Path fragment info. Record. + * Path fragment info. */ -typedef struct { +typedef struct pathdirectorysearch_fragment_s { ushort hash; const char* from, *to; -} pathdirectory_fragmentinfo_t; + struct pathdirectorysearch_fragment_s* next; +} pathdirectorysearch_fragment_t; + +/** + * PathDirectorySearch. Can be allocated on the stack. + */ +/// Size of the fixed-length "small" search path (in characters) allocated with the search. +#define PATHDIRECTORYSEARCH_SMALL_PATH 256 + +/// Size of the fixed-length "small" fragment buffer allocated with the search. +#define PATHDIRECTORYSEARCH_SMALL_FRAGMENTBUFFER_SIZE 8 typedef struct { - char delimiter; - int flags; - size_t fragments; - pathdirectory_fragmentinfo_t* info; - ddstring_t path; -/// \todo Refactor it out? - int result; - struct pathdirectory_node_s* resultNode; -} pathdirectory_search_t; + /// The search term. + char _smallSearchPath[PATHDIRECTORYSEARCH_SMALL_PATH+1]; + char* _searchPath; // The long version. + char _delimiter; + + /// @see pathComparisonFlags + int _flags; + + /// Total number of fragments in the search term. + uint _fragmentCount; + + /** + * Fragment map of the search term. The map is split into two components. + * The first PATHDIRECTORYSEARCH_SMALL_FRAGMENTBUFFER_SIZE elements are placed + * into a fixed-size buffer allocated along with "this". Any additional fragments + * are attached to "this" using a linked list. + * + * This optimized representation hopefully means that the majority of searches + * can be fulfilled without dynamically allocating memory. + */ + pathdirectorysearch_fragment_t _smallFragmentBuffer[PATHDIRECTORYSEARCH_SMALL_FRAGMENTBUFFER_SIZE]; + + /// Head of the linked list of "extra" fragments, in reverse order. + pathdirectorysearch_fragment_t* _extraFragments; +} pathdirectorysearch_t; + +/** + * Initialize the specified search from the given search term. + * \note On C++ rewrite make this PathDirectory static + * + * \post The search term will have been subdivided into a fragment map and some or + * all of the fragment hashes will have been calculated (dependant on the number of + * discreet fragments). + * + * @param flags @see pathComparisonFlags + * @param path Relative or absolute path to be searched for. + * @param delimiter Fragments of @a path are delimited by this character. + * @return Pointer to "this" instance for caller convenience. + */ +pathdirectorysearch_t* PathDirectory_InitSearch(pathdirectorysearch_t* search, int flags, + const char* path, char delimiter); + +/** + * Destroy @a search releasing any resources acquired for it. + * \note On C++ rewrite make this PathDirectory static. + */ +void PathDirectory_DestroySearch(pathdirectorysearch_t* search); + +/// @return @see pathComparisonFlags +int PathDirectorySearch_Flags(pathdirectorysearch_t* search); + +/// @return Number of path fragments in the search term. +uint PathDirectorySearch_Size(pathdirectorysearch_t* search); + +/** + * Retrieve the info for fragment @a idx within the search term. Note that + * fragments are indexed in reverse order (compared to the logical, left-to-right + * order of the search term). + * + * For example, if the search term is "c:/mystuff/myaddon.addon" the corresponding + * fragment map will be: [0:{myaddon.addon}, 1:{mystuff}, 2:{c:}]. + * + * \post Hash may have been calculated for the referenced fragment. + * + * @param idx Reverse-index of the fragment to be retrieved. + * @return Processed fragment info else @c NULL if @a idx is invalid. + */ +const pathdirectorysearch_fragment_t* PathDirectorySearch_GetFragment(pathdirectorysearch_t* search, uint idx); typedef struct { struct pathdirectory_node_s* head[PATHDIRECTORY_NODETYPES_COUNT]; @@ -81,8 +150,8 @@ typedef struct { * PathDirectory. Data structure for modelling a hierarchical relationship tree of * string+value data pairs. * - * Somewhat similar to a Prefix Tree (Trie) representationally although that is where - * the similarity ends. + * Somewhat similar to a Prefix Tree (Trie) representationally although that is + * where the similarity ends. * * @ingroup data */ @@ -123,31 +192,6 @@ const ddstring_t* PathDirectory_NodeTypeName(pathdirectory_nodetype_t type); */ void PathDirectory_Clear(pathdirectory_t* pd); -/** - * Setup the directory for a 'manual' search for @a searchPath (caller already - * knows which (sub)set of the nodes owned PathDirectory should be checked and - * implements the search logic externally to this module). - * - * @param flags @see pathComparisonFlags - * @param searchPath Relative or absolute path. - * @param delimiter Fragments of the path are delimited by this character. - * - * @return Pointer to the associated node iff found else @c 0 - */ -pathdirectory_search_t* PathDirectory_BeginSearch(pathdirectory_t* pd, int flags, - const char* searchPath, char delimiter); - -/// Same as PathDirectory::BeginSearch except @a searchPath is a @c ddstring_t -pathdirectory_search_t* PathDirectory_BeginSearchStr(pathdirectory_t* pd, int flags, - const ddstring_t* searchPath, char delimiter); - -/// Clean up after a manual search. -int PathDirectory_EndSearch2(pathdirectory_t* pd, struct pathdirectory_node_s** resultNode); -int PathDirectory_EndSearch(pathdirectory_t* pd); - -/// @return Search state. -pathdirectory_search_t* PathDirectory_Search(pathdirectory_t* pd); - /** * Add a new path. Duplicates are automatically pruned however, note that their * associated value is replaced! @@ -159,6 +203,47 @@ pathdirectory_search_t* PathDirectory_Search(pathdirectory_t* pd); struct pathdirectory_node_s* PathDirectory_Insert2(pathdirectory_t* pd, const char* path, char delimiter, void* userData); struct pathdirectory_node_s* PathDirectory_Insert(pathdirectory_t* pd, const char* path, char delimiter); /*userData = NULL*/ +/** + * Perform a search of the nodes in the directory making a callback for each. + * Pre-selection of nodes is determined by @a search. Iteration ends when all + * selected nodes have been visited or a callback returns non-zero. + * + * This method essentially amounts to "interface sugar". A manual search of the + * directory can be performed using nodes pre-selected by the caller or via the + * PathDirectory::Iterate method. + * + * @param search Pre-initialized search term @see PathDirectory::InitSearch + * @param callback Callback function ptr. The callback should only return a + * non-zero value when the desired node has been found. + * @param paramaters Passed to the callback. + * + * @return @c 0 iff iteration completed wholly. + */ +struct pathdirectory_node_s* PathDirectory_Search2(pathdirectory_t* pd, pathdirectorysearch_t* search, + int (*callback) (struct pathdirectory_node_s* node, pathdirectorysearch_t* search, void* paramaters), void* paramaters); +struct pathdirectory_node_s* PathDirectory_Search(pathdirectory_t* pd, pathdirectorysearch_t* search, + int (*callback) (struct pathdirectory_node_s* node, pathdirectorysearch_t* search, void* paramaters)); /*paramaters=NULL*/ + +/** + * Find a node in the directory. + * + * \note This method essentially amounts to "interface sugar". A convenient + * shorthand of the call tree: + * + * pathdirectorysearch_t search + * PathDirectory_InitSearch(&search, @a flags, @a searchPath, @a delimiter) + * foundNode = PathDirectory_Search("this", &search, PathDirectoryNode_MatchDirectory) + * PathDirectory_DestroySearch(&search) + * return foundNode + * + * @param flags @see pathComparisonFlags + * @param path Relative or absolute path to be searched for. + * @param delimiter Fragments of @a path are delimited by this character. + * @return Found node else @c NULL. + */ +struct pathdirectory_node_s* PathDirectory_Find(pathdirectory_t* pd, int flags, + const char* path, char delimiter); + /** * Iterate over nodes in the directory making a callback for each. * Iteration ends when all nodes have been visited or a callback returns non-zero. @@ -194,10 +279,13 @@ int PathDirectory_Iterate_Const(const pathdirectory_t* pd, int flags, const stru ddstring_t* PathDirectory_ComposePath(pathdirectory_t* pd, const struct pathdirectory_node_s* node, ddstring_t* path, int* length, char delimiter); +/// @return The path fragment which @a node represents. +const ddstring_t* PathDirectory_GetFragment(pathdirectory_t* pd, const struct pathdirectory_node_s* node); + /** * Collate all paths in the directory into a list. * - * @todo Does this really belong here (perhaps a class static non-member)? + * \todo Does this really belong here (perhaps a class static non-member)? * * @param flags @see pathComparisonFlags * @param delimiter Fragments of the path will be delimited by this character. @@ -208,8 +296,10 @@ ddstring_t* PathDirectory_ComposePath(pathdirectory_t* pd, const struct pathdire */ ddstring_t* PathDirectory_CollectPaths(pathdirectory_t* pd, int flags, char delimiter, size_t* count); +#if _DEBUG void PathDirectory_Print(pathdirectory_t* pd, char delimiter); void PathDirectory_PrintHashDistribution(pathdirectory_t* pd); +#endif /// @return PathDirectory which owns this node. pathdirectory_t* PathDirectoryNode_Directory(const struct pathdirectory_node_s* node); @@ -225,14 +315,13 @@ StringPoolInternId PathDirectoryNode_InternId(const struct pathdirectory_node_s* /** * @param node Right-most node in path. - * @param flags @see pathComparisonFlags - * @param delimiter Delimiter used to separate path fragments. - * @param fragments Number of fragments in the path. - * @param info Expected to contain at least @a fragments number of elements.. + * @param search Pre-initialized search term @see PathDirectory::InitSearch + * @param paramaters User data passed to this when used as a search callback. * * @return @c true iff the directory matched this. */ -boolean PathDirectoryNode_MatchDirectory(const struct pathdirectory_node_s* node, pathdirectory_search_t* search); +int PathDirectoryNode_MatchDirectory(const struct pathdirectory_node_s* node, + pathdirectorysearch_t* search, void* paramaters); /** * Attach user data to this. PathDirectoryNode is given ownership of @a data diff --git a/doomsday/engine/portable/include/resourcenamespace.h b/doomsday/engine/portable/include/resourcenamespace.h index 3bd8c70be1..06616a603b 100644 --- a/doomsday/engine/portable/include/resourcenamespace.h +++ b/doomsday/engine/portable/include/resourcenamespace.h @@ -27,11 +27,13 @@ #include "dd_string.h" #include "uri.h" -#include "filedirectory.h" typedef struct resourcenamespace_namehash_node_s { struct resourcenamespace_namehash_node_s* next; - void* data; +#if _DEBUG + ddstring_t name; +#endif + void* userData; } resourcenamespace_namehash_node_t; /** @@ -57,54 +59,33 @@ typedef enum { #define VALID_RESOURCENAMESPACE_SEARCHPATHGROUP(g) ((g) >= SPG_OVERRIDE && (g) < SEARCHPATHGROUP_COUNT) -/** - * @defGroup ResourceNamespaceFlags Resource Namespace Flags - * @ingroup core. - */ -/*@{*/ -#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). -/*@}*/ - /** * Resource Namespace. * * @ingroup core */ -#define RESOURCENAMESPACE_MINNAMELENGTH URI_MINSCHEMELENGTH typedef struct resourcenamespace_s { - /// Unique symbolic name of this namespace (e.g., "Models"). - /// Must be at least @c RESOURCENAMESPACE_MINNAMELENGTH characters long. - ddstring_t _name; - - /// @see ResourceNamespaceFlags - byte _flags; - /// Sets of search paths known by this namespace. /// Each set is in order of greatest-importance, right to left. Uri** _searchPaths[SEARCHPATHGROUP_COUNT]; uint _searchPathsCount[SEARCHPATHGROUP_COUNT]; /// Path hash table. - filedirectory_t* _directory; - ddstring_t* (*_composeHashName) (const ddstring_t* path); - resourcenamespace_namehash_key_t (*_hashName) (const ddstring_t* name); resourcenamespace_namehash_t _pathHash; + + /// Resource name hashing callback. + resourcenamespace_namehash_key_t (*_hashName) (const ddstring_t* name); } resourcenamespace_t; -resourcenamespace_t* ResourceNamespace_New(const char* name, - filedirectory_t* directory, ddstring_t* (*composeHashNameFunc) (const ddstring_t* path), +resourcenamespace_t* ResourceNamespace_New( resourcenamespace_namehash_key_t (*hashNameFunc) (const ddstring_t* name)); -resourcenamespace_t* ResourceNamespace_New2(const char* name, - filedirectory_t* directory, ddstring_t* (*composeHashNameFunc) (const ddstring_t* path), - resourcenamespace_namehash_key_t (*hashNameFunc) (const ddstring_t* name), byte flags); void ResourceNamespace_Delete(resourcenamespace_t* rn); /** * Reset the namespace back to it's "empty" state (i.e., no known symbols). */ -void ResourceNamespace_Reset(resourcenamespace_t* rnamespace); +void ResourceNamespace_Clear(resourcenamespace_t* rnamespace); /** * Add a new path to this namespace. @@ -123,38 +104,43 @@ boolean ResourceNamespace_AddSearchPath(resourcenamespace_t* rn, const Uri* path void ResourceNamespace_ClearSearchPaths(resourcenamespace_t* rn, resourcenamespace_searchpathgroup_t group); /** - * Find a path to a named resource in the namespace. + * Compose the list of search paths into a @a delimited string. * - * \post Name hash may have been rebuilt. - * - * @param searchPath Relative or absolute path. - * @param foundPath If not @c NULL and a path is found, it is written back here. - * - * @return Ptr to the name hash to use when searching. + * @param delimiter Discreet paths will be delimited by this character. + * @return Resultant string which should be released with Str_Delete(). */ -boolean ResourceNamespace_Find2(resourcenamespace_t* rnamespace, const ddstring_t* searchPath, ddstring_t* foundPath); -boolean ResourceNamespace_Find(resourcenamespace_t* rnamespace, const ddstring_t* searchPath); +ddstring_t* ResourceNamespace_ComposeSearchPathList2(resourcenamespace_t* rn, char delimiter); +ddstring_t* ResourceNamespace_ComposeSearchPathList(resourcenamespace_t* rn); /*delimiter= ';'*/ /** - * Apply mapping for this namespace to the specified path (if enabled). - * - * This mapping will translate directives and symbolic identifiers into their default paths, - * which themselves are determined using the current GameInfo. + * Add a new named resource into the namespace. Multiple names for a given + * resource may coexist however duplicates are automatically pruned. * - * e.g.: "Models:my/cool/model.dmd" -> "}data//models/my/cool/model.dmd" + * \post Name hash may have been rebuilt. * - * @param path Ptr to the path to be mapped. - * @return @c true iff mapping was applied to the path. + * @param name Name of the resource being added. + * @param node PathDirectoryNode representing the resource in the owning PathDirectory. + * @return @c true= If the namespace did not already contain this resource. */ -boolean ResourceNamespace_MapPath(resourcenamespace_t* rnamespace, ddstring_t* path); +boolean ResourceNamespace_Add(resourcenamespace_t* rn, const ddstring_t* name, + const struct pathdirectory_node_s* node); /** - * Accessor methods. + * Iterate over resources in this namespace. Iteration ends when all + * selected resources have been visited or a callback returns non-zero. + * + * @param name If not @c NULL, only consider resources with this name. + * @param callback Callback function ptr. + * @param paramaters Passed to the callback. + * @return @c 0 iff iteration completed wholly. */ -/// @return Ptr to a string containing the symbolic name. -const ddstring_t* ResourceNamespace_Name(const resourcenamespace_t* rnamespace); - -/// @return Ptr to the path directory used with this namespace. -filedirectory_t* ResourceNamespace_Directory(const resourcenamespace_t* rnamespace); +int ResourceNamespace_Iterate2(resourcenamespace_t* rn, const ddstring_t* name, + int (*callback) (struct pathdirectory_node_s* node, void* paramaters), void* paramaters); +int ResourceNamespace_Iterate(resourcenamespace_t* rn, const ddstring_t* name, + int (*callback) (struct pathdirectory_node_s* node, void* paramaters)); /*paramaters=NULL*/ + +#if _DEBUG +void ResourceNamespace_Print(resourcenamespace_t* rn); +#endif #endif /* LIBDENG_SYSTEM_RESOURCENAMESPACE_H */ diff --git a/doomsday/engine/portable/include/sys_reslocator.h b/doomsday/engine/portable/include/sys_reslocator.h index 6321ae029c..6fde92366c 100644 --- a/doomsday/engine/portable/include/sys_reslocator.h +++ b/doomsday/engine/portable/include/sys_reslocator.h @@ -22,6 +22,10 @@ * Boston, MA 02110-1301 USA */ +/** + * Routines for locating resources. + */ + #ifndef LIBDENG_SYSTEM_RESOURCE_LOCATOR_H #define LIBDENG_SYSTEM_RESOURCE_LOCATOR_H @@ -120,9 +124,14 @@ resourcenamespace_namehash_key_t F_HashKeyForAlphaNumericNameIgnoreCase(const dd #define F_HashKeyForFilePathHashName F_HashKeyForAlphaNumericNameIgnoreCase resourcenamespace_t* F_CreateResourceNamespace(const char* name, - filedirectory_t* directory, ddstring_t* (*composeHashNameFunc) (const ddstring_t* path), + struct filedirectory_s* directory, ddstring_t* (*composeHashNameFunc) (const ddstring_t* path), resourcenamespace_namehash_key_t (*hashNameFunc) (const ddstring_t* name), byte flags); +boolean F_AddSearchPathToResourceNamespace(resourcenamespaceid_t rni, const Uri* uri, + resourcenamespace_searchpathgroup_t group); + +const ddstring_t* F_ResourceNamespaceName(resourcenamespaceid_t rni); + /// @return Number of resource namespaces. uint F_NumResourceNamespaces(void); @@ -210,6 +219,20 @@ resourcenamespaceid_t F_ResourceNamespaceForName(const char* name); */ resourcetype_t F_GuessResourceTypeByName(const char* name); +/** + * Apply mapping for this namespace to the specified path (if enabled). + * + * This mapping will translate directives and symbolic identifiers into their default paths, + * which themselves are determined using the current GameInfo. + * + * e.g.: "Models:my/cool/model.dmd" -> "}data//models/my/cool/model.dmd" + * + * @param rni Unique identifier of the namespace whose mappings to apply. + * @param path The path to be mapped (applied in-place). + * @return @c true iff mapping was applied to the path. + */ +boolean F_MapResourcePath(resourcenamespaceid_t rni, ddstring_t* path); + /** * Apply all resource namespace mappings to the specified path. * diff --git a/doomsday/engine/portable/src/con_data.c b/doomsday/engine/portable/src/con_data.c index 7d7711488d..a32c089aec 100644 --- a/doomsday/engine/portable/src/con_data.c +++ b/doomsday/engine/portable/src/con_data.c @@ -835,7 +835,7 @@ cvar_t* Con_FindVariable(const char* path) { struct pathdirectory_node_s* node; if(0 == cvarCount) return NULL; - node = DD_SearchPathDirectory(cvarDirectory, PCF_NO_BRANCH|PCF_MATCH_FULL, path, CVARDIRECTORY_DELIMITER); + node = PathDirectory_Find(cvarDirectory, PCF_NO_BRANCH|PCF_MATCH_FULL, path, CVARDIRECTORY_DELIMITER); if(!node) return NULL; return (cvar_t*) PathDirectoryNode_UserData(node); } diff --git a/doomsday/engine/portable/src/dd_main.c b/doomsday/engine/portable/src/dd_main.c index fe23cc9b6f..446b396afd 100644 --- a/doomsday/engine/portable/src/dd_main.c +++ b/doomsday/engine/portable/src/dd_main.c @@ -2329,34 +2329,6 @@ struct material_s* DD_MaterialForTextureIndex(uint index, texturenamespaceid_t t return mat; } -int DD_SearchPathDirectoryCompare(struct pathdirectory_node_s* node, void* paramaters) -{ - pathdirectory_search_t* search = (pathdirectory_search_t*)paramaters; - search->resultNode = node; - return search->result = PathDirectoryNode_MatchDirectory(node, search); -} - -struct pathdirectory_node_s* DD_SearchPathDirectory(pathdirectory_t* pd, int flags, - const char* searchPath, char delimiter) -{ - if(searchPath && searchPath[0] && PathDirectory_Size(pd)) - { - int result; - struct pathdirectory_node_s* node; - pathdirectory_search_t* search = PathDirectory_BeginSearch(pd, flags, searchPath, delimiter); - - PathDirectory_Iterate2(pd, PCF_NO_BRANCH|PCF_MATCH_FULL, NULL, search->info[0].hash, - DD_SearchPathDirectoryCompare, (void*)search); - - result = PathDirectory_EndSearch2(pd, &node); - if(result != 0) - { - return node; - } - } - return NULL; -} - /** * Gets the data of a player. */ diff --git a/doomsday/engine/portable/src/def_read.c b/doomsday/engine/portable/src/def_read.c index 377708678b..13f5eec76a 100644 --- a/doomsday/engine/portable/src/def_read.c +++ b/doomsday/engine/portable/src/def_read.c @@ -818,7 +818,7 @@ static int DED_ReadData(ded_t* ded, const char* buffer, const char* _sourceFile) READSTR(label); CHECKSC; { Uri* newUri = Uri_NewWithPath2(label, RC_NULL); - ResourceNamespace_AddSearchPath(F_ToResourceNamespace(F_DefaultResourceNamespaceForClass(RC_MODEL)), newUri, SPG_EXTRA); + F_AddSearchPathToResourceNamespace(F_DefaultResourceNamespaceForClass(RC_MODEL), newUri, SPG_EXTRA); Uri_Delete(newUri); } } diff --git a/doomsday/engine/portable/src/filedirectory.c b/doomsday/engine/portable/src/filedirectory.c index e2d490549b..728ad46a78 100644 --- a/doomsday/engine/portable/src/filedirectory.c +++ b/doomsday/engine/portable/src/filedirectory.c @@ -400,8 +400,8 @@ boolean FileDirectory_Find(filedirectory_t* fd, pathdirectory_nodetype_t nodeTyp F_FixSlashes(&searchPath, &searchPath); // Perform the search. - flags = (nodeType == PT_LEAF? PCF_NO_BRANCH : PCF_NO_LEAF); - foundNode = DD_SearchPathDirectory(fd->_pathDirectory, flags, Str_Text(&searchPath), FILEDIRECTORY_DELIMITER); + flags = (nodeType == PT_LEAF? PCF_NO_BRANCH : PCF_NO_LEAF) | PCF_MATCH_FULL; + foundNode = PathDirectory_Find(fd->_pathDirectory, flags, Str_Text(&searchPath), FILEDIRECTORY_DELIMITER); Str_Free(&searchPath); // Does caller want to know the full path? @@ -419,14 +419,14 @@ static int C_DECL comparePaths(const void* a, const void* b) return stricmp(Str_Text((ddstring_t*)a), Str_Text((ddstring_t*)b)); } +#if _DEBUG void FileDirectory_Print(filedirectory_t* fd) { - assert(NULL != fd); - { size_t numFiles, n = 0; ddstring_t* fileList; + assert(fd); - Con_Printf("FileDirectory:\n"); + Con_Printf("FileDirectory [%p]:\n", (void*)fd); if(NULL != (fileList = PathDirectory_CollectPaths(fd->_pathDirectory, PT_LEAF, FILEDIRECTORY_DELIMITER, &numFiles))) { qsort(fileList, numFiles, sizeof(*fileList), comparePaths); @@ -438,7 +438,6 @@ void FileDirectory_Print(filedirectory_t* fd) free(fileList); } Con_Printf(" %lu %s in directory.\n", (unsigned long)numFiles, (numFiles==1? "file":"files")); - } } void FileDirectory_PrintHashDistribution(filedirectory_t* fd) @@ -446,3 +445,4 @@ void FileDirectory_PrintHashDistribution(filedirectory_t* fd) assert(NULL != fd); PathDirectory_PrintHashDistribution(fd->_pathDirectory); } +#endif diff --git a/doomsday/engine/portable/src/fonts.c b/doomsday/engine/portable/src/fonts.c index e7ea4373bc..c14fae1741 100644 --- a/doomsday/engine/portable/src/fonts.c +++ b/doomsday/engine/portable/src/fonts.c @@ -217,7 +217,7 @@ static boolean validateFontUri(const Uri* uri, int flags) */ static font_t* findFontForPath(pathdirectory_t* fontDirectory, const char* path) { - struct pathdirectory_node_s* node = DD_SearchPathDirectory(fontDirectory, + struct pathdirectory_node_s* node = PathDirectory_Find(fontDirectory, PCF_NO_BRANCH|PCF_MATCH_FULL, path, FONTDIRECTORY_DELIMITER); if(node) { diff --git a/doomsday/engine/portable/src/fs_main.c b/doomsday/engine/portable/src/fs_main.c index 0fbc005880..e4e23c038f 100644 --- a/doomsday/engine/portable/src/fs_main.c +++ b/doomsday/engine/portable/src/fs_main.c @@ -727,7 +727,8 @@ lumpnum_t F_OpenAuxiliary3(const char* path, size_t baseOffset, boolean silent) Str_Free(&searchPath); if(!file) { - Str_Delete(foundPath); + if(foundPath) + Str_Delete(foundPath); if(!silent) { Con_Message("Warning:F_OpenAuxiliary: Resource \"%s\" not found, aborting.\n", path); @@ -2191,4 +2192,4 @@ D_CMD(ListFiles) } Con_Printf("Total: %lu files in %lu packages.\n", (unsigned long) totalFiles, (unsigned long)totalPackages); return true; -} \ No newline at end of file +} diff --git a/doomsday/engine/portable/src/gl_texmanager.c b/doomsday/engine/portable/src/gl_texmanager.c index e1f02150d1..0f85419b4c 100644 --- a/doomsday/engine/portable/src/gl_texmanager.c +++ b/doomsday/engine/portable/src/gl_texmanager.c @@ -1586,7 +1586,7 @@ void GL_PruneTextureVariantSpecifications(void) numPruned += pruneUnusedVariantSpecifications(TST_GENERAL); numPruned += pruneUnusedVariantSpecifications(TST_DETAIL); #if _DEBUG - Con_Message("Pruned %i unused texture variant %s.\n", numPruned, numPruned == 1? "specification" : "specifications"); + VERBOSE( Con_Message("Pruned %i unused texture variant %s.\n", numPruned, numPruned == 1? "specification" : "specifications") ) #endif } @@ -3310,7 +3310,7 @@ static boolean validateTextureUri(const Uri* uri, int flags) */ static texture_t* findTextureForPath(pathdirectory_t* texDirectory, const char* path) { - struct pathdirectory_node_s* node = DD_SearchPathDirectory(texDirectory, + struct pathdirectory_node_s* node = PathDirectory_Find(texDirectory, PCF_NO_BRANCH|PCF_MATCH_FULL, path, TEXTUREDIRECTORY_DELIMITER); if(node) { diff --git a/doomsday/engine/portable/src/p_materialmanager.c b/doomsday/engine/portable/src/p_materialmanager.c index 89585928aa..e5172d5ece 100644 --- a/doomsday/engine/portable/src/p_materialmanager.c +++ b/doomsday/engine/portable/src/p_materialmanager.c @@ -853,7 +853,7 @@ static boolean validateMaterialUri(const Uri* uri, int flags) */ static material_t* findMaterialForPath(pathdirectory_t* matDirectory, const char* path) { - struct pathdirectory_node_s* node = DD_SearchPathDirectory(matDirectory, + struct pathdirectory_node_s* node = PathDirectory_Find(matDirectory, PCF_NO_BRANCH|PCF_MATCH_FULL, path, MATERIALDIRECTORY_DELIMITER); if(node) { diff --git a/doomsday/engine/portable/src/pathdirectory.c b/doomsday/engine/portable/src/pathdirectory.c index 7f020cf0e6..0aefbd2808 100644 --- a/doomsday/engine/portable/src/pathdirectory.c +++ b/doomsday/engine/portable/src/pathdirectory.c @@ -58,38 +58,6 @@ static pathdirectory_node_t* PathDirectoryNode_New(pathdirectory_t* directory, static void PathDirectoryNode_Delete(pathdirectory_node_t* node); -static volatile int numInstances = 0; -// A mutex is used to protect access to the shared fragment info buffer and search state. -static mutex_t fragmentBuffer_Mutex = 0; - -#define PATHDIRECTORY_FRAGMENT_BUFFER_SIZE 16 - -/// \protected by fragmentBuffer_Mutex; -static pathdirectory_fragmentinfo_t smallFragmentBuffer[PATHDIRECTORY_FRAGMENT_BUFFER_SIZE]; -static pathdirectory_fragmentinfo_t* largeFragmentBuffer = NULL; -static size_t largeFragmentBufferSize = 0; -static pathdirectory_search_t search; - -static volatile boolean searchInProgress = false; - -#if 0//_DEBUG -void lockMutex(mutex_t mutex) -{ - //searchInProgress = true; - Con_Message("Locking PathDirectory.\n"); - Sys_Lock(mutex); -} -#define Sys_Lock lockMutex - -void unlockMutex(mutex_t mutex) -{ - //searchInProgress = false; - Con_Message("Unlocking PathDirectory.\n"); - Sys_Unlock(mutex); -} -#define Sys_Unlock unlockMutex -#endif - /** * This is a hash function. It uses the path fragment string to generate * a somewhat-random number between @c 0 and @c PATHDIRECTORY_PATHHASH_SIZE @@ -500,20 +468,14 @@ static int iteratePaths_const(const pathdirectory_t* pd, int flags, const pathdi pathdirectory_t* PathDirectory_New(void) { - pathdirectory_t* pd = (pathdirectory_t*) malloc(sizeof(*pd)); - if(NULL == pd) + pathdirectory_t* pd = (pathdirectory_t*) malloc(sizeof *pd); + if(!pd) Con_Error("PathDirectory::Construct: Failed on allocation of %lu bytes for " - "new PathDirectory.", (unsigned long) sizeof(*pd)); + "new PathDirectory.", (unsigned long) sizeof *pd); pd->_internPool.strings = NULL; pd->_internPool.idHashMap = NULL; pd->_pathHash = NULL; pd->_size = 0; - - if(numInstances == 0) - { - fragmentBuffer_Mutex = Sys_CreateMutex("PathDirectory::fragmentBuffer_MUTEX"); - } - ++numInstances; return pd; } @@ -524,13 +486,6 @@ void PathDirectory_Delete(pathdirectory_t* pd) destroyPathHash(pd); clearInternPool(pd); free(pd); - - --numInstances; - if(numInstances == 0) - { - Sys_DestroyMutex(fragmentBuffer_Mutex); - fragmentBuffer_Mutex = 0; - } } uint PathDirectory_Size(pathdirectory_t* pd) @@ -541,12 +496,13 @@ uint PathDirectory_Size(pathdirectory_t* pd) const ddstring_t* PathDirectory_NodeTypeName(pathdirectory_nodetype_t type) { - static const ddstring_t nodeNames[PATHDIRECTORY_NODETYPES_COUNT] = { + static const ddstring_t nodeNames[1+PATHDIRECTORY_NODETYPES_COUNT] = { + { "(invalidtype)" }, { "branch" }, - { "leaf" }, + { "leaf" } }; - assert(VALID_PATHDIRECTORY_NODETYPE(type)); - return &nodeNames[type - PATHDIRECTORY_NODETYPES_FIRST]; + if(!VALID_PATHDIRECTORY_NODETYPE(type)) return &nodeNames[0]; + return &nodeNames[1 + (type - PATHDIRECTORY_NODETYPES_FIRST)]; } void PathDirectory_Clear(pathdirectory_t* pd) @@ -596,53 +552,106 @@ int PathDirectory_Iterate_Const(const pathdirectory_t* pd, int flags, const path return PathDirectory_Iterate2_Const(pd, flags, parent, hash, callback, NULL); } +typedef struct { + pathdirectorysearch_t* search; + void* paramaters; + int (*callback)(struct pathdirectory_node_s*, pathdirectorysearch_t*, void*); + struct pathdirectory_node_s* foundNode; +} pathdirectorysearchworker_params_t; + +static int PathDirectory_SearchWorker(struct pathdirectory_node_s* node, void* paramaters) +{ + pathdirectorysearchworker_params_t* p = (pathdirectorysearchworker_params_t*)paramaters; + assert(node && paramaters); + if(p->callback(node, p->search, p->paramaters)) + { + p->foundNode = node; + return 1; // Stop iteration. + } + return 0; // Continue iteration. +} + +struct pathdirectory_node_s* PathDirectory_Search2(pathdirectory_t* pd, pathdirectorysearch_t* search, + int (*callback)(struct pathdirectory_node_s*, pathdirectorysearch_t*, void*), void* paramaters) +{ + pathdirectorysearchworker_params_t p; + int result; + p.search = search; + p.paramaters = paramaters; + p.callback = callback; + p.foundNode = NULL; + result = iteratePaths(pd, PathDirectorySearch_Flags(search), NULL, + PathDirectorySearch_GetFragment(search, 0)->hash, + PathDirectory_SearchWorker, (void*)&p); + return (result? p.foundNode : NULL); +} + +struct pathdirectory_node_s* PathDirectory_Search(pathdirectory_t* pd, pathdirectorysearch_t* search, + int (*callback)(struct pathdirectory_node_s*, pathdirectorysearch_t*, void*)) +{ + return PathDirectory_Search2(pd, search, callback, NULL); +} + +struct pathdirectory_node_s* PathDirectory_Find(pathdirectory_t* pd, int flags, + const char* searchPath, char delimiter) +{ + struct pathdirectory_node_s* foundNode = NULL; + if(searchPath && searchPath[0] && PathDirectory_Size(pd)) + { + pathdirectorysearch_t search; + PathDirectory_InitSearch(&search, flags, searchPath, delimiter); + foundNode = PathDirectory_Search(pd, &search, PathDirectoryNode_MatchDirectory); + PathDirectory_DestroySearch(&search); + } + return foundNode; +} + const ddstring_t* PathDirectory_GetFragment(pathdirectory_t* pd, const pathdirectory_node_t* node) { assert(pd); return StringPool_String(pd->_internPool.strings, PathDirectoryNode_InternId(node)); } -/** - * Calculate the total length of the final composed path. - */ +/// Calculate the total length of the final composed path. static int PathDirectory_CalcPathLength(pathdirectory_t* pd, const pathdirectory_node_t* node, char delimiter) { - assert(pd && NULL != node); - { const int delimiterLen = delimiter? 1 : 0; int requiredLen = 0; + assert(pd && node); + if(PT_BRANCH == PathDirectoryNode_Type(node)) requiredLen += delimiterLen; requiredLen += Str_Length(PathDirectory_GetFragment(pd, node)); - if(NULL != PathDirectoryNode_Parent(node)) + if(PathDirectoryNode_Parent(node)) { const pathdirectory_node_t* trav = PathDirectoryNode_Parent(node); requiredLen += delimiterLen; do { requiredLen += Str_Length(PathDirectory_GetFragment(pd, trav)); - if(NULL != PathDirectoryNode_Parent(trav)) + if(PathDirectoryNode_Parent(trav)) requiredLen += delimiterLen; - } while(NULL != (trav = PathDirectoryNode_Parent(trav))); + } while((trav = PathDirectoryNode_Parent(trav))); } return requiredLen; - } } /// \assume @a foundPath already has sufficent characters reserved to hold the fully composed path. static ddstring_t* PathDirectory_ConstructPath(pathdirectory_t* pd, const pathdirectory_node_t* node, ddstring_t* foundPath, char delimiter) { - assert(pd && NULL != node && foundPath != NULL); - { const int delimiterLen = delimiter? 1 : 0; const pathdirectory_node_t* trav; const ddstring_t* fragment; + + assert(pd && node && foundPath); + if(PT_BRANCH == PathDirectoryNode_Type(node) && 0 != delimiterLen) Str_AppendChar(foundPath, delimiter); + trav = node; do { @@ -650,9 +659,8 @@ static ddstring_t* PathDirectory_ConstructPath(pathdirectory_t* pd, const pathdi Str_Prepend(foundPath, Str_Text(fragment)); if(NULL != PathDirectoryNode_Parent(trav) && 0 != delimiterLen) Str_PrependChar(foundPath, delimiter); - } while(NULL != (trav = PathDirectoryNode_Parent(trav))); + } while((trav = PathDirectoryNode_Parent(trav))); return foundPath; - } } ddstring_t* PathDirectory_ComposePath(pathdirectory_t* pd, const pathdirectory_node_t* node, @@ -679,155 +687,13 @@ ddstring_t* PathDirectory_ComposePath(pathdirectory_t* pd, const pathdirectory_n } } -static size_t splitSearchPath(const char* searchPath, size_t searchPathLen, char delimiter, - pathdirectory_fragmentinfo_t* storage) -{ - assert(searchPath && searchPath[0] && searchPathLen != 0); - { - size_t i, fragments; - const char* begin = searchPath; - const char* to = begin + searchPathLen - 1; - const char* from; - - // Skip over any trailing delimiters. - for(i = searchPathLen; *to && *to == delimiter && i-- > 0; to--) {} - - // In reverse order scan for distinct fragments in the search term. - fragments = 0; - for(;;) - { - // Find the start of the next path fragment. - for(from = to; from > begin && !(*from == delimiter); from--) {} - - // One more. - fragments++; - - // Are we storing info? - if(storage) - { - storage->from = (*from == delimiter? from + 1 : from); - storage->to = to; - // Hashing is deferred; means not-hashed yet. - storage->hash = PATHDIRECTORY_NOHASH; - } - - // Are there no more parent directories? - if(from == begin) break; - - // So far so good. Move one directory level upwards. - // The next fragment ends here. - to = from-1; - - if(storage) storage++; - } - - return fragments; - } -} - -static pathdirectory_fragmentinfo_t* enlargeFragmentBuffer(size_t fragments) -{ - if(fragments <= PATHDIRECTORY_FRAGMENT_BUFFER_SIZE) - { - return smallFragmentBuffer; - } - if(largeFragmentBuffer == NULL || fragments > largeFragmentBufferSize) - { - largeFragmentBufferSize = fragments; - largeFragmentBuffer = (pathdirectory_fragmentinfo_t*)realloc(largeFragmentBuffer, - sizeof(*largeFragmentBuffer) * largeFragmentBufferSize); - if(largeFragmentBuffer == NULL) - Con_Error("PathDirectory::enlargeFragmentBuffer: Failed on reallocation of %lu bytes.", - (unsigned long)(sizeof(*largeFragmentBuffer) * largeFragmentBufferSize)); - } - return largeFragmentBuffer; -} - -static void freeFragmentBuffer(void) -{ - if(largeFragmentBuffer == NULL) - return; - free(largeFragmentBuffer), largeFragmentBuffer = NULL; - largeFragmentBufferSize = 0; -} - -pathdirectory_search_t* PathDirectory_BeginSearchStr(pathdirectory_t* pd, int flags, - const ddstring_t* searchPath, char delimiter) -{ - assert(pd); - - if(searchInProgress) - Con_Error("PathDirectory::BeginSearch: Search already in progress."); - - Sys_Lock(fragmentBuffer_Mutex); - - search.resultNode = NULL; - search.result = 0; - search.flags = flags; - search.delimiter = delimiter; - Str_Init(&search.path); - Str_Reserve(&search.path, Str_Length(searchPath)); - Str_Set(&search.path, Str_Text(searchPath)); - search.fragments = splitSearchPath(Str_Text(&search.path), Str_Length(&search.path), delimiter, NULL); - searchInProgress = true; - search.info = enlargeFragmentBuffer(search.fragments); - splitSearchPath(Str_Text(&search.path), Str_Length(&search.path), delimiter, search.info); - // Hash the first (i.e., rightmost) fragment now. - search.info[0].hash = hashName(search.info[0].from, (search.info[0].to - search.info[0].from) + 1, delimiter); - - return &search; -} - -pathdirectory_search_t* PathDirectory_BeginSearch(pathdirectory_t* pd, int flags, - const char* searchPath, char delimiter) -{ - pathdirectory_search_t* result; - ddstring_t temp; Str_Init(&temp); Str_Set(&temp, searchPath); - result = PathDirectory_BeginSearchStr(pd, flags, &temp, delimiter); - Str_Free(&temp); - return result; -} - -int PathDirectory_EndSearch2(pathdirectory_t* pd, pathdirectory_node_t** resultNode) -{ - assert(pd); - { - int result = search.result; - if(resultNode) *resultNode = search.resultNode; - - if(!searchInProgress) - Con_Error("PathDirectory::EndSearch: Attempted with no search in progress.\n"); - - freeFragmentBuffer(); - Str_Free(&search.path); - Sys_Unlock(fragmentBuffer_Mutex); - searchInProgress = false; - return result; - } -} - -int PathDirectory_EndSearch(pathdirectory_t* pd) -{ - return PathDirectory_EndSearch2(pd, NULL); -} - -pathdirectory_search_t* PathDirectory_Search(pathdirectory_t* pd) -{ - assert(pd); - - if(!searchInProgress) - Con_Error("PathDirectory::Search: Attempted with no search in progress.\n"); - return &search; -} - ddstring_t* PathDirectory_CollectPaths(pathdirectory_t* pd, int flags, char delimiter, size_t* retCount) { - assert(pd); - { ddstring_t* paths = NULL; size_t count = countNodes(pd, flags); - if(0 != count) + + if(count) { pathdirectory_nodetype_t type = ((flags & PCF_NO_BRANCH) != 0? PT_LEAF : PT_BRANCH); pathdirectory_nodetype_t lastType = ((flags & PCF_NO_LEAF) != 0? PT_BRANCH : PT_LEAF); @@ -836,7 +702,7 @@ ddstring_t* PathDirectory_CollectPaths(pathdirectory_t* pd, int flags, char deli ushort hash; paths = (ddstring_t*) malloc(sizeof *paths * count); - if(NULL == paths) + if(!paths) Con_Error("PathDirectory::AllPaths: Failed on allocation of %lu bytes for " "new path list.", (unsigned long) (sizeof *paths * count)); pathPtr = paths; @@ -844,17 +710,16 @@ ddstring_t* PathDirectory_CollectPaths(pathdirectory_t* pd, int flags, char deli for(; type <= lastType; ++type) for(hash = 0; hash < PATHDIRECTORY_PATHHASH_SIZE; ++hash) for(node = (pathdirectory_node_t*) (*pd->_pathHash)[hash].head[type]; - NULL != node; node = node->next) + node; node = node->next) { Str_Init(pathPtr); PathDirectory_ComposePath(PathDirectoryNode_Directory(node), node, pathPtr, NULL, delimiter); pathPtr++; } } - if(retCount) - *retCount = count; + + if(retCount) *retCount = count; return paths; - } } static int C_DECL comparePaths(const void* a, const void* b) @@ -862,15 +727,16 @@ static int C_DECL comparePaths(const void* a, const void* b) return stricmp(Str_Text((ddstring_t*)a), Str_Text((ddstring_t*)b)); } +#if _DEBUG void PathDirectory_Print(pathdirectory_t* pd, char delimiter) { - assert(pd); - { size_t numLeafs, n = 0; ddstring_t* pathList; + assert(pd); - Con_Printf("PathDirectory: %p\n", pd); - if(NULL != (pathList = PathDirectory_CollectPaths(pd, PT_LEAF, delimiter, &numLeafs))) + Con_Printf("PathDirectory [%p]:\n", (void*)pd); + pathList = PathDirectory_CollectPaths(pd, PT_LEAF, delimiter, &numLeafs); + if(pathList) { qsort(pathList, numLeafs, sizeof(*pathList), comparePaths); do @@ -881,7 +747,6 @@ void PathDirectory_Print(pathdirectory_t* pd, char delimiter) free(pathList); } Con_Printf(" %lu %s in directory.\n", (unsigned long)numLeafs, (numLeafs==1? "path":"paths")); - } } static void printDistributionOverviewElement(const int* colWidths, const char* name, @@ -1267,17 +1132,19 @@ void PathDirectory_PrintHashDistribution(pathdirectory_t* pd) printDistributionHistogram(pd, 16, nodeCountTotal); } } +#endif static pathdirectory_node_t* PathDirectoryNode_New(pathdirectory_t* directory, pathdirectory_nodetype_t type, pathdirectory_node_t* parent, StringPoolInternId internId, void* userData) { - assert(NULL != directory); - { - pathdirectory_node_t* node = (pathdirectory_node_t*) malloc(sizeof(*node)); - if(NULL == node) + pathdirectory_node_t* node; + assert(directory); + + node = (pathdirectory_node_t*) malloc(sizeof *node); + if(!node) Con_Error("PathDirectory::direcNode: Failed on allocation of %lu bytes for " - "new PathDirectory::Node.", (unsigned long) sizeof(*node)); + "new PathDirectory::Node.", (unsigned long) sizeof *node); node->_directory = directory; node->_type = type; @@ -1285,94 +1152,88 @@ static pathdirectory_node_t* PathDirectoryNode_New(pathdirectory_t* directory, node->_pair.internId = internId; node->_pair.data = userData; return node; - } } static void PathDirectoryNode_Delete(pathdirectory_node_t* node) { - assert(NULL != node); + assert(node); free(node); } pathdirectory_t* PathDirectoryNode_Directory(const struct pathdirectory_node_s* node) { - assert(NULL != node); + assert(node); return node->_directory; } struct pathdirectory_node_s* PathDirectoryNode_Parent(const struct pathdirectory_node_s* node) { - assert(NULL != node); + assert(node); return node->_parent; } pathdirectory_nodetype_t PathDirectoryNode_Type(const pathdirectory_node_t* node) { - assert(NULL != node); + assert(node); return node->_type; } StringPoolInternId PathDirectoryNode_InternId(const pathdirectory_node_t* node) { - assert(NULL != node); + assert(node); return node->_pair.internId; } -boolean PathDirectoryNode_MatchDirectory(const pathdirectory_node_t* node, pathdirectory_search_t* s) +/// \note This routine is also used as an iteration callback, so only return +/// a non-zero value when the node is a match for the search term. +int PathDirectoryNode_MatchDirectory(const pathdirectory_node_t* node, pathdirectorysearch_t* s, void* paramaters) { - assert(NULL != node && NULL != s); - { pathdirectory_t* pd = PathDirectoryNode_Directory(node); - pathdirectory_fragmentinfo_t* info; + const pathdirectorysearch_fragment_t* sfragment; const ddstring_t* fragment; - size_t i; + uint i, fragmentCount; + int sflags; - if(NULL == s->info || 0 == s->fragments) + sflags = PathDirectorySearch_Flags(s); + if(((sflags & PCF_NO_LEAF) && PT_LEAF == PathDirectoryNode_Type(node)) || + ((sflags & PCF_NO_BRANCH) && PT_BRANCH == PathDirectoryNode_Type(node))) return false; - if(((s->flags & PCF_NO_LEAF) && PT_LEAF == PathDirectoryNode_Type(node)) || - ((s->flags & PCF_NO_BRANCH) && PT_BRANCH == PathDirectoryNode_Type(node))) - return false; + sfragment = PathDirectorySearch_GetFragment(s, 0); + if(!sfragment) return false; // Hmm... - // In reverse order, compare path s.fragments in the search term. - info = s->info; - for(i = 0; i < s->fragments; ++i) + // In reverse order, compare path fragments in the search term. + fragmentCount = PathDirectorySearch_Size(s); + for(i = 0; i < fragmentCount; ++i) { - // Is it time to compute the hash for this path fragment? - if(info->hash == PATHDIRECTORY_NOHASH) - { - info->hash = hashName(info->from, (info->to - info->from) + 1, s->delimiter); - } - // If the hashes don't match it can't possibly be this. - if(info->hash != hashForInternId(pd, PathDirectoryNode_InternId(node))) + if(sfragment->hash != hashForInternId(pd, PathDirectoryNode_InternId(node))) return false; + // Compare the path fragment to that of the search term. fragment = PathDirectory_GetFragment(pd, node); - if(Str_Length(fragment) < (info->to - info->from)+1 || - strnicmp(Str_Text(fragment), info->from, Str_Length(fragment))) + if(Str_Length(fragment) < (sfragment->to - sfragment->from)+1 || + strnicmp(Str_Text(fragment), sfragment->from, Str_Length(fragment))) return false; // Have we arrived at the search target? - if(i == s->fragments-1) - return (!(s->flags & PCF_MATCH_FULL) || NULL == PathDirectoryNode_Parent(node)); + if(i == fragmentCount-1) + return (!(sflags & PCF_MATCH_FULL) || !PathDirectoryNode_Parent(node)); // Are there no more parent directories? - if(NULL == PathDirectoryNode_Parent(node)) + if(!PathDirectoryNode_Parent(node)) return false; // So far so good. Move one directory level upwards. node = PathDirectoryNode_Parent(node); - info++; + sfragment = PathDirectorySearch_GetFragment(s, i+1); } - return false; - } } -void PathDirectoryNode_AttachUserData(pathdirectory_node_t* node, void* data) +void PathDirectoryNode_AttachUserData(pathdirectory_node_t* node, void* userData) { - assert(NULL != node); + assert(node); #if _DEBUG if(node->_pair.data) { @@ -1380,21 +1241,191 @@ void PathDirectoryNode_AttachUserData(pathdirectory_node_t* node, void* data) "with this node, will be replaced.\n"); } #endif - node->_pair.data = data; + node->_pair.data = userData; } void* PathDirectoryNode_DetachUserData(pathdirectory_node_t* node) { - assert(NULL != node); - { - void* data = node->_pair.data; + void* userData; + assert(node); + userData = node->_pair.data; node->_pair.data = NULL; - return data; - } + return userData; } void* PathDirectoryNode_UserData(const pathdirectory_node_t* node) { - assert(NULL != node); + assert(node); return node->_pair.data; } + +static ushort PathDirectorySearch_HashFragment(pathdirectorysearch_t* s, + pathdirectorysearch_fragment_t* fragment) +{ + assert(s && fragment); + // Is it time to compute the hash for this fragment? + if(fragment->hash == PATHDIRECTORY_NOHASH) + { + fragment->hash = hashName(fragment->from, (fragment->to - fragment->from) + 1, s->_delimiter); + } + return fragment->hash; +} + +static void PathDirectorySearch_BuildFragmentMap(pathdirectorysearch_t* s, + const char* searchPath, size_t searchPathLen) +{ + pathdirectorysearch_fragment_t* fragment = NULL; + const char* begin = searchPath; + const char* to = begin + searchPathLen - 1; + const char* from; + size_t i; + + assert(s && searchPath && searchPath[0] && searchPathLen != 0); + + // Skip over any trailing delimiters. + for(i = searchPathLen; *to && *to == s->_delimiter && i-- > 0; to--) {} + + s->_fragmentCount = 0; + s->_extraFragments = NULL; + + // In reverse order scan for distinct fragments in the search term. + for(;;) + { + // Find the start of the next path fragment. + for(from = to; from > begin && !(*from == s->_delimiter); from--) {} + + // Retrieve another fragment. + if(s->_fragmentCount < PATHDIRECTORYSEARCH_SMALL_FRAGMENTBUFFER_SIZE) + { + fragment = s->_smallFragmentBuffer + s->_fragmentCount; + } + else + { + // Allocate another "extra" fragment. + pathdirectorysearch_fragment_t* f = (pathdirectorysearch_fragment_t*)malloc(sizeof *f); + if(!f) Con_Error("PathDirectorySearch::BuildFragmentMap: Failed on allocation of %lu bytes for new PathDirectorySearch::Fragment.", (unsigned long) sizeof *f); + + if(!s->_extraFragments) + { + s->_extraFragments = fragment = f; + } + else + { + fragment->next = f; + fragment = f; + } + } + + fragment->from = (*from == s->_delimiter? from + 1 : from); + fragment->to = to; + // Hashing is deferred; means not-hashed yet. + fragment->hash = PATHDIRECTORY_NOHASH; + fragment->next = NULL; + + // There is now one more fragment in the map. + s->_fragmentCount += 1; + + // Are there no more parent directories? + if(from == begin) break; + + // So far so good. Move one directory level upwards. + // The next fragment ends here. + to = from-1; + } +} + +static void PathDirectorySearch_ClearFragmentMap(pathdirectorysearch_t* s) +{ + assert(s); + while(s->_extraFragments) + { + pathdirectorysearch_fragment_t* next = s->_extraFragments->next; + free(s->_extraFragments); + s->_extraFragments = next; + } +} + +static void PathDirectorySearch_ClearSearchPath(pathdirectorysearch_t* s) +{ + assert(s); + if(s->_searchPath) free(s->_searchPath), s->_searchPath = NULL; +} + +pathdirectorysearch_t* PathDirectory_InitSearch(pathdirectorysearch_t* s, int flags, + const char* path, char delimiter) +{ + char* pathBuffer; + size_t pathLen; + assert(s); + + // Take a copy of the search term. + pathLen = (path? strlen(path) : 0); + if(pathLen <= PATHDIRECTORYSEARCH_SMALL_PATH) + { + // Use the small search path buffer. + s->_searchPath = NULL; + pathBuffer = s->_smallSearchPath; + } + else + { + // Allocate a buffer large enough to hold the whole path. + s->_searchPath = (char*)malloc(pathLen+1); + pathBuffer = s->_searchPath; + } + if(pathLen) + { + memcpy(pathBuffer, path, pathLen); + } + pathBuffer[pathLen] = '\0'; + + s->_flags = flags; + s->_delimiter = delimiter; + + // Create the fragment map of the search term. + PathDirectorySearch_BuildFragmentMap(s, pathBuffer, pathLen); + + // Hash the first (i.e., rightmost) fragment right away. + PathDirectorySearch_GetFragment(s, 0); + + return s; +} + +void PathDirectory_DestroySearch(pathdirectorysearch_t* s) +{ + assert(s); + PathDirectorySearch_ClearFragmentMap(s); + PathDirectorySearch_ClearSearchPath(s); +} + +int PathDirectorySearch_Flags(pathdirectorysearch_t* s) +{ + assert(s); + return s->_flags; +} + +uint PathDirectorySearch_Size(pathdirectorysearch_t* s) +{ + assert(s); + return s->_fragmentCount; +} + +const pathdirectorysearch_fragment_t* PathDirectorySearch_GetFragment(pathdirectorysearch_t* s, uint idx) +{ + pathdirectorysearch_fragment_t* fragment; + if(!s || idx >= s->_fragmentCount) return NULL; + if(idx < PATHDIRECTORYSEARCH_SMALL_FRAGMENTBUFFER_SIZE) + { + fragment = s->_smallFragmentBuffer + idx; + } + else + { + uint n = PATHDIRECTORYSEARCH_SMALL_FRAGMENTBUFFER_SIZE; + fragment = s->_extraFragments; + while(n++ < idx) + { + fragment = fragment->next; + } + } + PathDirectorySearch_HashFragment(s, fragment); + return fragment; +} diff --git a/doomsday/engine/portable/src/resourcenamespace.c b/doomsday/engine/portable/src/resourcenamespace.c index 23eaecbdaf..738409ca97 100644 --- a/doomsday/engine/portable/src/resourcenamespace.c +++ b/doomsday/engine/portable/src/resourcenamespace.c @@ -35,267 +35,124 @@ static void clearPathHash(resourcenamespace_t* rn) { + uint i; assert(rn); - { uint i; + for(i = 0; i < RESOURCENAMESPACE_HASHSIZE; ++i) { resourcenamespace_hashentry_t* entry = &rn->_pathHash[i]; while(entry->first) { resourcenamespace_namehash_node_t* nextNode = entry->first->next; +#if _DEBUG + Str_Free(&entry->first->name); +#endif free(entry->first); entry->first = nextNode; } entry->last = 0; - }} -} - -#if _DEBUG -static void printPathHash(resourcenamespace_t* rn) -{ - assert(rn); - { - ddstring_t path; - size_t n = 0; - - Str_Init(&path); - - { uint i; - for(i = 0; i < RESOURCENAMESPACE_HASHSIZE; ++i) - { - resourcenamespace_hashentry_t* entry = &rn->_pathHash[i]; - resourcenamespace_namehash_node_t* node = entry->first; - while(node) - { - Str_Clear(&path); - PathDirectory_ComposePath(PathDirectoryNode_Directory(node->data), node->data, &path, NULL, FILEDIRECTORY_DELIMITER); - { ddstring_t* hashName = rn->_composeHashName(&path); - Con_Printf(" %lu: %lu:\"%s\" -> %s\n", (unsigned long)n, (unsigned long)i, - Str_Text(hashName), Str_Text(&path)); - Str_Delete(hashName); - } - ++n; - node = node->next; - } - }} - - Str_Free(&path); } } -#endif -static void appendSearchPathsInGroup(ddstring_t* pathList, resourcenamespace_t* rn, - resourcenamespace_searchpathgroup_t group) +static void appendSearchPathsInGroup(resourcenamespace_t* rn, + resourcenamespace_searchpathgroup_t group, char delimiter, ddstring_t* pathList) { - assert(pathList && rn && VALID_RESOURCENAMESPACE_SEARCHPATHGROUP(group)); - { uint i; + uint i; + assert(rn && VALID_RESOURCENAMESPACE_SEARCHPATHGROUP(group) && pathList); + for(i = 0; i < rn->_searchPathsCount[group]; ++i) { ddstring_t* path = Uri_ComposePath(rn->_searchPaths[group][i]); - Str_Appendf(pathList, "%s;", Str_Text(path)); + Str_Appendf(pathList, "%s%c", Str_Text(path), delimiter); Str_Delete(path); - }} -} - -static void formSearchPathList(ddstring_t* pathList, resourcenamespace_t* rn) -{ - assert(pathList && rn); - appendSearchPathsInGroup(pathList, rn, SPG_OVERRIDE); - appendSearchPathsInGroup(pathList, rn, SPG_EXTRA); - appendSearchPathsInGroup(pathList, rn, SPG_DEFAULT); - appendSearchPathsInGroup(pathList, rn, SPG_FALLBACK); -} - -static boolean findPath(resourcenamespace_t* rn, const ddstring_t* hashName, - const ddstring_t* searchPath, ddstring_t* foundPath) -{ - assert(rn && hashName && !Str_IsEmpty(hashName) && searchPath && !Str_IsEmpty(searchPath)); - { - resourcenamespace_namehash_key_t key = rn->_hashName(hashName); - resourcenamespace_namehash_node_t* node = rn->_pathHash[key].first; - struct pathdirectory_node_s* resultNode = NULL; - - if(NULL != node) - { - pathdirectory_t* pd = PathDirectoryNode_Directory(node->data); - pathdirectory_search_t* search = PathDirectory_BeginSearchStr(pd, 0, searchPath, FILEDIRECTORY_DELIMITER); - { - while(NULL != node && !DD_SearchPathDirectoryCompare(node->data, search)) - node = node->next; - } - PathDirectory_EndSearch2(pd, &resultNode); - } - - // Does the caller want to know the matched path? - if(NULL != foundPath && NULL != resultNode) - PathDirectory_ComposePath(PathDirectoryNode_Directory(resultNode), resultNode, foundPath, NULL, FILEDIRECTORY_DELIMITER); - - return (NULL == node? false : true); } } -static int addFilePathWorker(const struct pathdirectory_node_s* fdNode, void* paramaters) +static ddstring_t* formSearchPathList(resourcenamespace_t* rn, ddstring_t* pathList, char delimiter) { - assert(fdNode && paramaters); - { - resourcenamespace_t* rn = (resourcenamespace_t*) paramaters; - ddstring_t* hashName, filePath; - - if(PathDirectoryNode_Type(fdNode) != PT_LEAF) - return 0; // Continue adding. - - // Extract the file name and hash it. - Str_Init(&filePath); - PathDirectory_ComposePath(PathDirectoryNode_Directory(fdNode), fdNode, &filePath, NULL, FILEDIRECTORY_DELIMITER); - hashName = rn->_composeHashName(&filePath); - - // Is this a new resource? - if(false == findPath(rn, hashName, &filePath, 0)) - { - resourcenamespace_namehash_node_t* node; - - // Create a new hash node. - if((node = malloc(sizeof(*node))) == 0) - Con_Error("ResourceNamespace::addFilePathWorker: Failed on allocation of %lu bytes for " - "new ResourcenamespaceNamehashNode.", (unsigned long) sizeof(*node)); - - node->data = (void*) fdNode; - node->next = 0; - - // Link it to the list for this bucket. - { resourcenamespace_hashentry_t* slot = &rn->_pathHash[rn->_hashName(hashName)]; - if(slot->last) - slot->last->next = node; - slot->last = node; - if(!slot->first) - slot->first = node; - } - } - - Str_Delete(hashName); - Str_Free(&filePath); - return 0; // Continue adding. - } + appendSearchPathsInGroup(rn, SPG_OVERRIDE, delimiter, pathList); + appendSearchPathsInGroup(rn, SPG_EXTRA, delimiter, pathList); + appendSearchPathsInGroup(rn, SPG_DEFAULT, delimiter, pathList); + appendSearchPathsInGroup(rn, SPG_FALLBACK, delimiter, pathList); + return pathList; } -static void rebuild(resourcenamespace_t* rn) +static resourcenamespace_namehash_node_t* findPathNodeInHash(resourcenamespace_t* rn, + resourcenamespace_namehash_key_t key, const struct pathdirectory_node_s* pdNode) { - assert(rn); - if(rn->_flags & RNF_IS_DIRTY) + resourcenamespace_namehash_node_t* node; + assert(rn && pdNode); + node = rn->_pathHash[key].first; + while(node && node->userData != pdNode) { - clearPathHash(rn); - FileDirectory_Clear(rn->_directory); - - { ddstring_t tmp; Str_Init(&tmp); - formSearchPathList(&tmp, rn); - if(Str_Length(&tmp) > 0) - { -#if _DEBUG - //uint startTime; - VERBOSE( Con_Message("Rebuilding rnamespace name hash...\n") ) - VERBOSE2( Con_PrintPathList(Str_Text(&tmp)) ); - //startTime = verbose >= 2? Sys_GetRealTime(): 0; -#endif - - FileDirectory_AddPathList3(rn->_directory, Str_Text(&tmp), addFilePathWorker, rn); - -/*#if _DEBUG - printPathHash(rn); - VERBOSE2( Con_Message(" Done in %.2f seconds.\n", (Sys_GetRealTime() - startTime) / 1000.0f) ); -#endif*/ - } - Str_Free(&tmp); - } - - rn->_flags &= ~RNF_IS_DIRTY; + node = node->next; } + return node; } -resourcenamespace_t* ResourceNamespace_New2(const char* name, - filedirectory_t* directory, ddstring_t* (*composeHashNameFunc) (const ddstring_t* path), - resourcenamespace_namehash_key_t (*hashNameFunc) (const ddstring_t* name), byte flags) +resourcenamespace_t* ResourceNamespace_New( + resourcenamespace_namehash_key_t (*hashNameFunc) (const ddstring_t* name)) { - assert(name && directory && composeHashNameFunc && hashNameFunc); - { resourcenamespace_t* rn; + uint i; + assert(hashNameFunc); - if(strlen(name) < RESOURCENAMESPACE_MINNAMELENGTH) - Con_Error("ResourceNamespace::Construct: Invalid name '%s' (min length:%i)", - name, (int)RESOURCENAMESPACE_MINNAMELENGTH); - - if(NULL == (rn = (resourcenamespace_t*) malloc(sizeof(*rn)))) - Con_Error("ResourceNamespace::Construct: Failed on allocation of %lu bytes.", - (unsigned long) sizeof(*rn)); + rn = (resourcenamespace_t*) malloc(sizeof *rn); + if(!rn) + Con_Error("ResourceNamespace::Construct: Failed on allocation of %lu bytes.", (unsigned long) sizeof *rn); - rn->_flags = flags; - Str_Init(&rn->_name); Str_Set(&rn->_name, name); - - { uint i; for(i = 0; i < SEARCHPATHGROUP_COUNT; ++i) { rn->_searchPathsCount[i] = 0; rn->_searchPaths[i] = NULL; - }} + } - rn->_directory = directory; - rn->_composeHashName = composeHashNameFunc; rn->_hashName = hashNameFunc; memset(rn->_pathHash, 0, sizeof(rn->_pathHash)); return rn; - } -} - -resourcenamespace_t* ResourceNamespace_New(const char* name, - filedirectory_t* directory, ddstring_t* (*composeHashNameFunc) (const ddstring_t* path), - resourcenamespace_namehash_key_t (*hashNameFunc) (const ddstring_t* name)) -{ - return ResourceNamespace_New2(name, directory, composeHashNameFunc, hashNameFunc, 0); } void ResourceNamespace_Delete(resourcenamespace_t* rn) { + uint i; assert(rn); - ResourceNamespace_ClearSearchPaths(rn, SPG_OVERRIDE); - ResourceNamespace_ClearSearchPaths(rn, SPG_DEFAULT); - ResourceNamespace_ClearSearchPaths(rn, SPG_EXTRA); - ResourceNamespace_ClearSearchPaths(rn, SPG_FALLBACK); + for(i = 0; i < SEARCHPATHGROUP_COUNT; ++i) + { + ResourceNamespace_ClearSearchPaths(rn, (resourcenamespace_searchpathgroup_t)i); + } clearPathHash(rn); - Str_Free(&rn->_name); free(rn); } -void ResourceNamespace_Reset(resourcenamespace_t* rn) +void ResourceNamespace_Clear(resourcenamespace_t* rn) { - assert(rn); - ResourceNamespace_ClearSearchPaths(rn, SPG_EXTRA); clearPathHash(rn); - FileDirectory_Clear(rn->_directory); - rn->_flags |= RNF_IS_DIRTY; } boolean ResourceNamespace_AddSearchPath(resourcenamespace_t* rn, const Uri* newUri, resourcenamespace_searchpathgroup_t group) { + uint i, j; assert(rn && newUri && VALID_RESOURCENAMESPACE_SEARCHPATHGROUP(group)); if(Str_IsEmpty(Uri_Path(newUri)) || !Str_CompareIgnoreCase(Uri_Path(newUri), DIR_SEP_STR)) return false; // Not suitable. // Have we seen this path already (we don't want duplicates)? - { uint i, j; + for(i = 0; i < SEARCHPATHGROUP_COUNT; ++i) for(j = 0; j < rn->_searchPathsCount[i]; ++j) { if(Uri_Equality(rn->_searchPaths[i][j], newUri)) return true; - }} + } rn->_searchPaths[group] = (Uri**) realloc(rn->_searchPaths[group], - sizeof(*rn->_searchPaths[group]) * ++rn->_searchPathsCount[group]); - if(NULL == rn->_searchPaths[group]) + sizeof **rn->_searchPaths * ++rn->_searchPathsCount[group]); + if(!rn->_searchPaths[group]) Con_Error("ResourceNamespace::AddExtraSearchPath: Failed on reallocation of %lu bytes for " - "searchPath list.", (unsigned long) sizeof(*rn->_searchPaths[group]) * (rn->_searchPathsCount[group]-1)); + "searchPath list.", (unsigned long) sizeof **rn->_searchPaths * (rn->_searchPathsCount[group]-1)); // Prepend to the path list - newer paths have priority. if(rn->_searchPathsCount[group] > 1) @@ -303,81 +160,155 @@ boolean ResourceNamespace_AddSearchPath(resourcenamespace_t* rn, const Uri* newU sizeof(*rn->_searchPaths[group]) * (rn->_searchPathsCount[group]-1)); rn->_searchPaths[group][0] = Uri_NewCopy(newUri); - rn->_flags |= RNF_IS_DIRTY; return true; } void ResourceNamespace_ClearSearchPaths(resourcenamespace_t* rn, resourcenamespace_searchpathgroup_t group) { + uint i; assert(rn && VALID_RESOURCENAMESPACE_SEARCHPATHGROUP(group)); - if(!rn->_searchPaths[group]) - return; - { uint i; + + if(!rn->_searchPaths[group]) return; + for(i = 0; i < rn->_searchPathsCount[group]; ++i) + { Uri_Delete(rn->_searchPaths[group][i]); } free(rn->_searchPaths[group]); rn->_searchPaths[group] = 0; rn->_searchPathsCount[group] = 0; - rn->_flags |= RNF_IS_DIRTY; } -boolean ResourceNamespace_Find2(resourcenamespace_t* rn, const ddstring_t* _searchPath, - ddstring_t* foundPath) +ddstring_t* ResourceNamespace_ComposeSearchPathList2(resourcenamespace_t* rn, char delimiter) { - assert(rn && _searchPath); - { - boolean result = false; - if(!Str_IsEmpty(_searchPath)) - { - ddstring_t* hashName, searchPath; + return formSearchPathList(rn, Str_New(), delimiter); +} - // Ensure the namespace is clean. - rebuild(rn); +ddstring_t* ResourceNamespace_ComposeSearchPathList(resourcenamespace_t* rn) +{ + return ResourceNamespace_ComposeSearchPathList2(rn, ';'); +} - // Extract the file name and hash it. - Str_Init(&searchPath); - F_FixSlashes(&searchPath, _searchPath); - hashName = rn->_composeHashName(&searchPath); +int ResourceNamespace_Iterate2(resourcenamespace_t* rn, const ddstring_t* name, + int (*callback) (struct pathdirectory_node_s* node, void* paramaters), void* paramaters) +{ + int result = 0; + assert(rn); + if(rn->_pathHash && callback) + { + resourcenamespace_namehash_node_t* node, *next; + resourcenamespace_namehash_key_t from, to, key; + if(name) + { + from = rn->_hashName(name); + if(from >= RESOURCENAMESPACE_HASHSIZE) + { + Con_Error("ResourceNamespace::Iterate: Hashing of name '%s' in [%p] produced invalid key %u.", Str_Text(name), (void*)rn, (unsigned short)from); + exit(1); // Unreachable. + } + to = from; + } + else + { + from = 0; + to = RESOURCENAMESPACE_HASHSIZE-1; + } - result = findPath(rn, hashName, &searchPath, foundPath); - Str_Free(&searchPath); - Str_Delete(hashName); + for(key = from; key < to+1; ++key) + { + node = rn->_pathHash[key].first; + while(node) + { + next = node->next; + result = callback(node->userData, paramaters); + if(result) break; + node = next; + } + if(result) break; + } } return result; - } } -boolean ResourceNamespace_Find(resourcenamespace_t* rn, const ddstring_t* searchPath) +int ResourceNamespace_Iterate(resourcenamespace_t* rn, const ddstring_t* name, + int (*callback) (struct pathdirectory_node_s* node, void* paramaters)) { - return ResourceNamespace_Find2(rn, searchPath, 0); + return ResourceNamespace_Iterate2(rn, name, callback, NULL); } -boolean ResourceNamespace_MapPath(resourcenamespace_t* rn, ddstring_t* path) +boolean ResourceNamespace_Add(resourcenamespace_t* rn, const ddstring_t* name, + const struct pathdirectory_node_s* pdNode) { - assert(rn && path); + resourcenamespace_namehash_node_t* node; + resourcenamespace_namehash_key_t key; + boolean isNewNode = false; + assert(rn && name && pdNode); - if(rn->_flags & RNF_USE_VMAP) + key = rn->_hashName(name); + if(key >= RESOURCENAMESPACE_HASHSIZE) { - int nameLen = Str_Length(&rn->_name), pathLen = Str_Length(path); - if(nameLen <= pathLen && Str_At(path, nameLen) == DIR_SEP_CHAR && - !strnicmp(Str_Text(&rn->_name), Str_Text(path), nameLen)) - { - Str_Prepend(path, Str_Text(GameInfo_DataPath(DD_GameInfo()))); - return true; - } + Con_Error("ResourceNamespace::Add: Hashing of name '%s' in [%p] produced invalid key %u.", Str_Text(name), (void*)rn, (unsigned short)key); + exit(1); // Unreachable. } - return false; -} -const ddstring_t* ResourceNamespace_Name(const resourcenamespace_t* rn) -{ - assert(rn); - return &rn->_name; + // Is this a new resource name & path pair? + node = findPathNodeInHash(rn, key, pdNode); + if(!node) + { + // Create a new node for this name. + resourcenamespace_hashentry_t* slot; + + isNewNode = true; + node = (resourcenamespace_namehash_node_t*)malloc(sizeof *node); + if(!node) + Con_Error("ResourceNamespace::Add: Failed on allocation of %lu bytes for " + "new ResourceNamespace::NameHash::Node.", (unsigned long) sizeof *node); + +#if _DEBUG + Str_Init(&node->name); Str_Set(&node->name, Str_Text(name)); +#endif + node->userData = NULL; + node->next = NULL; + + // Link it to the list for this bucket. + slot = &rn->_pathHash[key]; + if(slot->last) slot->last->next = node; + slot->last = node; + if(!slot->first) slot->first = node; + } + + // (Re)configure this node. + node->userData = (void*)pdNode; + + return isNewNode; } -filedirectory_t* ResourceNamespace_Directory(const resourcenamespace_t* rn) +#if _DEBUG +void ResourceNamespace_Print(resourcenamespace_t* rn) { + ddstring_t path; + size_t n; + uint i; assert(rn); - return rn->_directory; + + Con_Printf("ResourceNamespace [%p]:\n", (void*)rn); + n = 0; + Str_Init(&path); + for(i = 0; i < RESOURCENAMESPACE_HASHSIZE; ++i) + { + resourcenamespace_hashentry_t* entry = &rn->_pathHash[i]; + resourcenamespace_namehash_node_t* node = entry->first; + while(node) + { + Str_Clear(&path); + PathDirectory_ComposePath(PathDirectoryNode_Directory(node->userData), node->userData, &path, NULL, FILEDIRECTORY_DELIMITER); + Con_Printf(" %lu: %lu:\"%s\" -> %s\n", (unsigned long)n, (unsigned long)i, + Str_Text(&node->name), Str_Text(&path)); + ++n; + node = node->next; + } + } + Con_Printf(" %lu %s in namespace.\n", (unsigned long) n, (n==1? "resource":"resources")); + Str_Free(&path); } +#endif diff --git a/doomsday/engine/portable/src/sys_reslocator.c b/doomsday/engine/portable/src/sys_reslocator.c index 7d7fd87b51..0f78d7c3a3 100644 --- a/doomsday/engine/portable/src/sys_reslocator.c +++ b/doomsday/engine/portable/src/sys_reslocator.c @@ -23,12 +23,6 @@ * Boston, MA 02110-1301 USA */ -/** - * Routines for locating resources. - */ - -// HEADER FILES ------------------------------------------------------------ - #ifdef WIN32 # include #endif @@ -50,13 +44,9 @@ #include "resourcerecord.h" #include "resourcenamespace.h" -// MACROS ------------------------------------------------------------------ - #define PATH_DELIMIT_CHAR ';' #define PATH_DELIMIT_STR ";" -// TYPES ------------------------------------------------------------------- - #define MAX_EXTENSIONS (3) typedef struct { /// Default class attributed to resources of this type. @@ -64,17 +54,33 @@ typedef struct { char* knownFileNameExtensions[MAX_EXTENSIONS]; } resourcetypeinfo_t; -// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- +/** + * @defGroup ResourceNamespaceFlags Resource Namespace Flags + * @ingroup core. + */ +/*@{*/ +#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). +/*@}*/ -// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- +#define RESOURCENAMESPACE_MINNAMELENGTH URI_MINSCHEMELENGTH -// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- +typedef struct { + /// Unique symbolic name of this namespace (e.g., "Models"). + /// Must be at least @c RESOURCENAMESPACE_MINNAMELENGTH characters long. + ddstring_t name; + + /// ResourceNamespace. + resourcenamespace_t* rnamespace; -// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + /// Associated path directory for this namespace. + filedirectory_t* directory; -// PUBLIC DATA DEFINITIONS ------------------------------------------------- + /// Algorithm used to compose the name of a resource in this namespace. + ddstring_t* (*composeName) (const ddstring_t* path); -// PRIVATE DATA DEFINITIONS ------------------------------------------------ + byte flags; // @see resourceNamespaceFlags +} resourcenamespaceinfo_t; static boolean inited = false; @@ -118,11 +124,9 @@ static const ddstring_t defaultNamespaceForClass[RESOURCECLASS_COUNT] = { /* RC_FONT */ { FONTS_RESOURCE_NAMESPACE_NAME } }; -static resourcenamespace_t** namespaces = 0; +static resourcenamespaceinfo_t* namespaces = 0; static uint numNamespaces = 0; -// CODE -------------------------------------------------------------------- - static void errorIfNotInited(const char* callerName) { if(inited) return; @@ -137,23 +141,23 @@ static __inline const resourcetypeinfo_t* getInfoForResourceType(resourcetype_t return &typeInfo[((uint)type)-1]; } -static __inline resourcenamespace_t* getNamespaceForId(resourcenamespaceid_t rni) +static __inline resourcenamespaceinfo_t* getNamespaceInfoForId(resourcenamespaceid_t rni) { errorIfNotInited("getNamespaceForId"); if(!F_IsValidResourceNamespaceId(rni)) Con_Error("getNamespaceForId: Invalid namespace id %i.", (int)rni); - return namespaces[((uint)rni)-1]; + return &namespaces[((uint)rni)-1]; } static resourcenamespaceid_t findNamespaceId(resourcenamespace_t* rnamespace) { - if(NULL != rnamespace) + if(rnamespace) { uint i; for(i = 0; i < numNamespaces; ++i) { - resourcenamespace_t* other = namespaces[i]; - if(other == rnamespace) + resourcenamespaceinfo_t* info = &namespaces[i]; + if(info->rnamespace == rnamespace) return (resourcenamespaceid_t)(i+1); } } @@ -167,8 +171,8 @@ static resourcenamespaceid_t findNamespaceForName(const char* name) uint i; for(i = 0; i < numNamespaces; ++i) { - resourcenamespace_t* rnamespace = namespaces[i]; - if(!stricmp(Str_Text(ResourceNamespace_Name(rnamespace)), name)) + resourcenamespaceinfo_t* info = &namespaces[i]; + if(!stricmp(Str_Text(&info->name), name)) return (resourcenamespaceid_t)(i+1); } } @@ -177,51 +181,205 @@ static resourcenamespaceid_t findNamespaceForName(const char* name) static void destroyAllNamespaces(void) { - if(numNamespaces == 0) - return; - { uint i; + uint i; + if(numNamespaces == 0) return; + for(i = 0; i < numNamespaces; ++i) { - resourcenamespace_t* rnamespace = namespaces[i]; - if(NULL != ResourceNamespace_Directory(rnamespace)) - { - FileDirectory_Delete(ResourceNamespace_Directory(rnamespace)); - } - ResourceNamespace_Delete(rnamespace); - }} + resourcenamespaceinfo_t* info = &namespaces[i]; + if(info->directory) FileDirectory_Delete(info->directory); + ResourceNamespace_Delete(info->rnamespace); + Str_Free(&info->name); + } free(namespaces); namespaces = 0; } static void resetAllNamespaces(void) { - uint i; - for(i = 0; i < numNamespaces; ++i) - ResourceNamespace_Reset(namespaces[i]); + resourcenamespaceid_t rni; + for(rni = 1; rni < numNamespaces+1; ++rni) + { + F_ResetResourceNamespace(rni); + } } -static boolean tryFindResource2(resourceclass_t rclass, const ddstring_t* searchPath, - ddstring_t* foundPath, resourcenamespace_t* rnamespace) +static void addResourceToNamespace(resourcenamespaceinfo_t* rnInfo, const struct pathdirectory_node_s* node) { - assert(inited && searchPath && !Str_IsEmpty(searchPath)); + ddstring_t* name; + assert(rnInfo && node); + + name = rnInfo->composeName(PathDirectory_GetFragment(PathDirectoryNode_Directory(node), node)); + if(ResourceNamespace_Add(rnInfo->rnamespace, 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; + } + Str_Delete(name); +} + +static int addFileResourceWorker(const struct pathdirectory_node_s* node, void* paramaters) +{ + resourcenamespaceinfo_t* rnInfo = (resourcenamespaceinfo_t*) paramaters; + // We are only interested in leafs (i.e., files and not directories). + if(PathDirectoryNode_Type(node) == PT_LEAF) + { + addResourceToNamespace(rnInfo, node); + } + return 0; // Continue adding. +} + +static void rebuildResourceNamespace(resourcenamespaceinfo_t* rnInfo) +{ +/*#if _DEBUG + uint startTime; +#endif*/ + ddstring_t* searchPaths; + + assert(rnInfo); + if(!(rnInfo->flags & RNF_IS_DIRTY)) return; + +/*#if _DEBUG + VERBOSE( Con_Message("Rebuilding rnamespace '%s'...\n", Str_Text(&rnInfo->name)) ) + VERBOSE2( startTime = Sys_GetRealTime() ) +#endif*/ + + ResourceNamespace_Clear(rnInfo->rnamespace); + FileDirectory_Clear(rnInfo->directory); + + searchPaths = ResourceNamespace_ComposeSearchPathList(rnInfo->rnamespace); + if(searchPaths) + { + if(!Str_IsEmpty(searchPaths)) + { +/*#if _DEBUG + VERBOSE2( Con_PrintPathList(Str_Text(searchPaths)) ) +#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. + FileDirectory_AddPathList3(rnInfo->directory, Str_Text(searchPaths), addFileResourceWorker, (void*)rnInfo); + FileDirectory_Print(rnInfo->directory); + } + Str_Delete(searchPaths); + } + rnInfo->flags &= ~RNF_IS_DIRTY; + +/*#if _DEBUG + VERBOSE2( ResourceNamespace_Print(rnInfo->rnamespace) ) + VERBOSE2( Con_Message(" Done in %.2f seconds.\n", (Sys_GetRealTime() - startTime) / 1000.0f) ) +#endif*/ +} + +typedef struct { + const char* path; + char delimiter; + pathdirectorysearch_t search; + boolean searchInited; + struct pathdirectory_node_s* foundNode; +} findresourceinnamespaceworker_params_t; + +static int findResourceInNamespaceWorker(struct pathdirectory_node_s* node, void* paramaters) +{ + findresourceinnamespaceworker_params_t* p = (findresourceinnamespaceworker_params_t*)paramaters; + assert(node && p); + // Are we yet to initialize the search? + if(!p->searchInited) + { + PathDirectory_InitSearch(&p->search, PCF_NO_BRANCH, p->path, p->delimiter); + p->searchInited = true; + } + // Stop iteration of resources as soon as a match is found. + if(PathDirectoryNode_MatchDirectory(node, &p->search, NULL)) + { + p->foundNode = node; + return 1; + } + return 0; // Continue iteration. +} + +/** + * 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 delimiter 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. + * @return @c true= A resource was found. + */ +static boolean findResourceInNamespace(resourcenamespaceinfo_t* rnInfo, const ddstring_t* name, + const ddstring_t* searchPath, char delimiter, ddstring_t* foundPath) +{ + boolean found = false; + assert(rnInfo && name && searchPath); + + if(!Str_IsEmpty(searchPath)) + { + findresourceinnamespaceworker_params_t p; + + // Ensure the namespace is up to date. + rebuildResourceNamespace(rnInfo); + + // There may not be any matching named resources, so we defer initialization + // of the PathDirectorySearch until the first name-match is found. + p.path = Str_Text(searchPath); + p.delimiter = delimiter; + p.searchInited = false; + p.foundNode = NULL; + + // Perform the search. + if(found = ResourceNamespace_Iterate2(rnInfo->rnamespace, name, findResourceInNamespaceWorker, (void*)&p)) + { + // Does the caller want to know the matched path? + if(foundPath) + { + struct pathdirectory_node_s* node = p.foundNode; + PathDirectory_ComposePath(PathDirectoryNode_Directory(node), node, foundPath, NULL, delimiter); + } + } + + // Cleanup. + if(p.searchInited) PathDirectory_DestroySearch(&p.search); + } + return found; +} + +static boolean tryFindResource2(resourceclass_t rclass, const ddstring_t* rawSearchPath, + ddstring_t* foundPath, resourcenamespaceinfo_t* rnamespaceInfo) +{ + ddstring_t searchPath; + assert(inited && rawSearchPath && !Str_IsEmpty(rawSearchPath)); + + Str_Init(&searchPath); + F_FixSlashes(&searchPath, rawSearchPath); + // Is there a namespace we should use? - if(rnamespace && ResourceNamespace_Find2(rnamespace, searchPath, foundPath)) + if(rnamespaceInfo) { - if(foundPath) - F_PrependBasePath(foundPath, foundPath); - return true; + ddstring_t* name = rnamespaceInfo->composeName(&searchPath); + if(findResourceInNamespace(rnamespaceInfo, name, &searchPath, FILEDIRECTORY_DELIMITER, foundPath)) + { + Str_Free(&searchPath); + Str_Delete(name); + if(foundPath) F_PrependBasePath(foundPath, foundPath); + return true; + } + Str_Delete(name); } - if(0 != F_Access(Str_Text(searchPath))) + + if(0 != F_Access(Str_Text(&searchPath))) { - if(foundPath) - F_PrependBasePath(foundPath, searchPath); + Str_Free(&searchPath); + if(foundPath) F_PrependBasePath(foundPath, &searchPath); return true; } + Str_Free(&searchPath); return false; } static boolean tryFindResource(resourceclass_t rclass, const ddstring_t* searchPath, - ddstring_t* foundPath, resourcenamespace_t* rnamespace) + ddstring_t* foundPath, resourcenamespaceinfo_t* rnamespaceInfo) { assert(inited && searchPath && !Str_IsEmpty(searchPath)); { @@ -231,7 +389,7 @@ static boolean tryFindResource(resourceclass_t rclass, const ddstring_t* searchP // Has an extension been specified? ptr = F_FindFileExtension(Str_Text(searchPath)); if(ptr && *ptr != '*') // Try this first. - found = tryFindResource2(rclass, searchPath, foundPath, rnamespace); + found = tryFindResource2(rclass, searchPath, foundPath, rnamespaceInfo); if(!found) { @@ -256,15 +414,15 @@ static boolean tryFindResource(resourceclass_t rclass, const ddstring_t* searchP Str_Init(&tmp); do { - const resourcetypeinfo_t* info = getInfoForResourceType(*type); - if(info->knownFileNameExtensions[0]) + const resourcetypeinfo_t* typeInfo = getInfoForResourceType(*type); + if(typeInfo->knownFileNameExtensions[0]) { - char* const* ext = info->knownFileNameExtensions; + char* const* ext = typeInfo->knownFileNameExtensions; do { Str_Clear(&tmp); Str_Appendf(&tmp, "%s%s", Str_Text(&path2), *ext); - found = tryFindResource2(rclass, &tmp, foundPath, rnamespace); + found = tryFindResource2(rclass, &tmp, foundPath, rnamespaceInfo); } while(!found && *(++ext)); } } while(!found && *(++type) != RT_NONE); @@ -278,14 +436,14 @@ static boolean tryFindResource(resourceclass_t rclass, const ddstring_t* searchP } static boolean findResource2(resourceclass_t rclass, const ddstring_t* searchPath, - const ddstring_t* optionalSuffix, ddstring_t* foundPath, resourcenamespace_t* rnamespace) + const ddstring_t* optionalSuffix, ddstring_t* foundPath, resourcenamespaceinfo_t* rnamespaceInfo) { assert(inited && searchPath && !Str_IsEmpty(searchPath)); { boolean found = false; #if _DEBUG - VERBOSE2( Con_Message("Using rnamespace '%s'...\n", rnamespace? Str_Text(ResourceNamespace_Name(rnamespace)) : "None") ) + VERBOSE2( Con_Message("Using rnamespace '%s'...\n", rnamespaceInfo? Str_Text(&rnamespaceInfo->name) : "None") ) #endif // First try with the optional suffix. @@ -309,13 +467,13 @@ static boolean findResource2(resourceclass_t rclass, const ddstring_t* searchPat Str_Appendf(&fn, "%s%s", Str_Text(searchPath), Str_Text(optionalSuffix)); }} - found = tryFindResource(rclass, &fn, foundPath, rnamespace); + found = tryFindResource(rclass, &fn, foundPath, rnamespaceInfo); Str_Free(&fn); } // Try without a suffix. if(!found) - found = tryFindResource(rclass, searchPath, foundPath, rnamespace); + found = tryFindResource(rclass, searchPath, foundPath, rnamespaceInfo); return found; } @@ -348,10 +506,11 @@ static int findResource(resourceclass_t rclass, const Uri* const* list, // Has a namespace identifier been included? if(!Str_IsEmpty(Uri_Scheme(searchPath))) { - resourcenamespaceid_t rni; - if((rni = F_SafeResourceNamespaceForName(Str_Text(Uri_Scheme(searchPath)))) != 0) + resourcenamespaceid_t rni = F_SafeResourceNamespaceForName(Str_Text(Uri_Scheme(searchPath))); + if(rni) { - if(findResource2(rclass, resolvedPath, optionalSuffix, foundPath, getNamespaceForId(rni))) + resourcenamespaceinfo_t* rnamespaceInfo = getNamespaceInfoForId(rni); + if(findResource2(rclass, resolvedPath, optionalSuffix, foundPath, rnamespaceInfo)) result = n; } else @@ -634,12 +793,22 @@ void F_ResetAllResourceNamespaces(void) void F_ResetResourceNamespace(resourcenamespaceid_t rni) { - ResourceNamespace_Reset(F_ToResourceNamespace(rni)); + resourcenamespaceinfo_t* info; + if(!F_IsValidResourceNamespaceId(rni)) return; + + info = getNamespaceInfoForId(rni); + ResourceNamespace_ClearSearchPaths(info->rnamespace, SPG_EXTRA); + ResourceNamespace_Clear(info->rnamespace); + if(info->directory) + { + FileDirectory_Clear(info->directory); + } + info->flags |= RNF_IS_DIRTY; } struct resourcenamespace_s* F_ToResourceNamespace(resourcenamespaceid_t rni) { - return getNamespaceForId(rni); + return getNamespaceInfoForId(rni)->rnamespace; } resourcenamespaceid_t F_SafeResourceNamespaceForName(const char* name) @@ -668,23 +837,62 @@ boolean F_IsValidResourceNamespaceId(int val) return (boolean)(val>0 && (unsigned)val < (F_NumResourceNamespaces()+1)? 1 : 0); } -resourcenamespace_t* F_CreateResourceNamespace(const char* name, - filedirectory_t* directory, ddstring_t* (*composeHashNameFunc) (const ddstring_t* path), +resourcenamespace_t* F_CreateResourceNamespace(const char* name, filedirectory_t* directory, + ddstring_t* (*composeNameFunc) (const ddstring_t* path), resourcenamespace_namehash_key_t (*hashNameFunc) (const ddstring_t* name), byte flags) { - assert(name); + resourcenamespaceinfo_t* info; + resourcenamespace_t* rn; + + assert(name && directory && composeNameFunc); errorIfNotInited("F_CreateResourceNamespace"); - { - resourcenamespace_t* rn = ResourceNamespace_New2(name, directory, composeHashNameFunc, hashNameFunc, flags); + + if(strlen(name) < RESOURCENAMESPACE_MINNAMELENGTH) + Con_Error("F_CreateResourceNamespace: Invalid name '%s' (min length:%i)", + name, (int)RESOURCENAMESPACE_MINNAMELENGTH); + + rn = ResourceNamespace_New(hashNameFunc); // Add this new namespace to the global list. - namespaces = (resourcenamespace_t**) realloc(namespaces, sizeof(*namespaces) * ++numNamespaces); - if(namespaces == NULL) + namespaces = (resourcenamespaceinfo_t*) realloc(namespaces, sizeof *namespaces * ++numNamespaces); + if(!namespaces) Con_Error("F_CreateResourceNamespace: Failed on (re)allocation of %lu bytes for new resource namespace\n", - (unsigned long) (sizeof(*namespaces) * numNamespaces)); - namespaces[numNamespaces-1] = rn; + (unsigned long) sizeof *namespaces * numNamespaces); + 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; + return rn; +} + +boolean F_AddSearchPathToResourceNamespace(resourcenamespaceid_t rni, const Uri* uri, + resourcenamespace_searchpathgroup_t group) +{ + resourcenamespaceinfo_t* info; + errorIfNotInited("F_AddSearchPathToResourceNamespace"); + info = getNamespaceInfoForId(rni); + if(ResourceNamespace_AddSearchPath(info->rnamespace, uri, group)) + { + info->flags |= RNF_IS_DIRTY; + return true; } + return false; +} + +void F_AddResourceToNamespace(resourcenamespaceid_t rni, const struct pathdirectory_node_s* node) +{ + errorIfNotInited("F_AddResourceToNamespace"); + addResourceToNamespace(getNamespaceInfoForId(rni), node); +} + +const ddstring_t* F_ResourceNamespaceName(resourcenamespaceid_t rni) +{ + return &(getNamespaceInfoForId(rni))->name; } Uri** F_CreateUriList2(resourceclass_t rclass, const char* searchPaths, @@ -992,13 +1200,32 @@ resourcetype_t F_GuessResourceTypeByName(const char* path) return RT_NONE; // Unrecognizable. } +boolean F_MapResourcePath(resourcenamespaceid_t rni, ddstring_t* path) +{ + if(path && !Str_IsEmpty(path)) + { + resourcenamespaceinfo_t* info = getNamespaceInfoForId(rni); + if(info->flags & RNF_USE_VMAP) + { + int nameLen = Str_Length(&info->name), pathLen = Str_Length(path); + if(nameLen <= pathLen && Str_At(path, nameLen) == DIR_SEP_CHAR && + !strnicmp(Str_Text(&info->name), Str_Text(path), nameLen)) + { + Str_Prepend(path, Str_Text(GameInfo_DataPath(DD_GameInfo()))); + return true; + } + } + } + return false; +} + boolean F_ApplyPathMapping(ddstring_t* path) { assert(path); errorIfNotInited("F_ApplyPathMapping"); - { uint i = 0; + { uint i = 1; boolean result = false; - while(i < numNamespaces && !(result = ResourceNamespace_MapPath(namespaces[i++], path))); + while(i < numNamespaces+1 && !(result = F_MapResourcePath(i++, path))); return result; } } diff --git a/doomsday/engine/portable/src/uri.c b/doomsday/engine/portable/src/uri.c index 464a51f867..af9636fa60 100644 --- a/doomsday/engine/portable/src/uri.c +++ b/doomsday/engine/portable/src/uri.c @@ -78,8 +78,8 @@ static void parseScheme(Uri* uri, resourceclass_t defaultResourceClass) if(VALID_RESOURCE_CLASS(defaultResourceClass)) { - resourcenamespace_t* rn = F_ToResourceNamespace(F_DefaultResourceNamespaceForClass(defaultResourceClass)); - Str_Copy(&uri->_scheme, ResourceNamespace_Name(rn)); + const ddstring_t* name = F_ResourceNamespaceName(F_DefaultResourceNamespaceForClass(defaultResourceClass)); + Str_Copy(&uri->_scheme, name); } }