bool ProjectViewModel::IsContainer( const wxDataViewItem& item ) const { // root node can have children if ( !item.IsOk() ) { return true; } AssetPath *node = static_cast< AssetPath* >( item.GetID() ); return node ? node->IsPackage() : false; }
/// Find an object based on its path name. /// /// @param[in] path FilePath of the object to locate. /// /// @return Pointer to the object if found, null pointer if not found. Asset* Asset::FindObject( AssetPath path ) { // Make sure the path isn't empty. if( path.IsEmpty() ) { return NULL; } // Assemble a list of object names and instance indices, from the top level on down. size_t pathDepth = 0; size_t packageDepth = 0; for( AssetPath testPath = path; !testPath.IsEmpty(); testPath = testPath.GetParent() ) { ++pathDepth; if( testPath.IsPackage() ) { ++packageDepth; } } StackMemoryHeap<>& rStackHeap = ThreadLocalStackAllocator::GetMemoryHeap(); StackMemoryHeap<>::Marker stackMarker( rStackHeap ); Name* pPathNames = static_cast< Name* >( rStackHeap.Allocate( sizeof( Name ) * pathDepth ) ); HELIUM_ASSERT( pPathNames ); uint32_t* pInstanceIndices = static_cast< uint32_t* >( rStackHeap.Allocate( sizeof( uint32_t ) * pathDepth ) ); HELIUM_ASSERT( pInstanceIndices ); size_t pathIndex = pathDepth; for( AssetPath testPath = path; !testPath.IsEmpty(); testPath = testPath.GetParent() ) { HELIUM_ASSERT( pathIndex != 0 ); --pathIndex; pPathNames[ pathIndex ] = testPath.GetName(); pInstanceIndices[ pathIndex ] = testPath.GetInstanceIndex(); } HELIUM_ASSERT( pathIndex == 0 ); // Search from the root. return FindChildOf( NULL, pPathNames, pInstanceIndices, pathDepth, packageDepth ); }
/// Initialize this package loader. /// /// @param[in] packagePath Asset path of the package to load. /// /// @return True if this loader was initialized successfully, false if not. /// /// @see Shutdown() bool LoosePackageLoader::Initialize( AssetPath packagePath ) { Shutdown(); // Make sure the path represents a package. if( packagePath.IsEmpty() ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoader::Initialize(): Empty package path specified.\n" ) ); return false; } HELIUM_TRACE( TraceLevels::Debug, TXT( "LoosePackageLoader::Initialize(): Initializing loader for package \"%s\".\n" ), *packagePath.ToString() ); if( !packagePath.IsPackage() ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoader::Initialize(): \"%s\" does not represent a package path.\n" ), *packagePath.ToString() ); return false; } // Store the package path. m_packagePath = packagePath; // Attempt to locate the specified package if it already happens to exist. m_spPackage = Asset::Find< Package >( packagePath ); Package* pPackage = m_spPackage; if( pPackage ) { if( pPackage->GetLoader() ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoader::Initialize(): Package \"%s\" already has a loader.\n" ), *packagePath.ToString() ); m_spPackage.Release(); return false; } pPackage->SetLoader( this ); } else { // Make sure we don't have a name clash with a non-package object. AssetPtr spObject( Asset::FindObject( packagePath ) ); if( spObject ) { HELIUM_ASSERT( !spObject->IsPackage() ); HELIUM_TRACE( TraceLevels::Error, ( TXT( "PackageLoader::Initialize(): Package loader cannot be initialized for \"%s\", as an " ) TXT( "object with the same name exists that is not a package.\n" ) ), *packagePath.ToString() ); return false; } } // Build the package file path. If the package is a user configuration package, use the user data directory, // otherwise use the global data directory. Config& rConfig = Config::GetStaticInstance(); FilePath dataDirectory; if ( !FileLocations::GetDataDirectory( dataDirectory ) ) { HELIUM_TRACE( TraceLevels::Error, TXT( "PackageLoader::Initialize(): Could not obtain user data directory." ) ); return false; } // Set up to read the TOC (which may not exist) //SetInvalid( m_packageTocFileSize ); // First do this check without a trailing "/" so that FilePath has to actually look at the file system FilePath package_dir = dataDirectory + packagePath.ToFilePathString().GetData(); if (!package_dir.Exists()) { // Some packages like types or uninitialized user config packages may not exist on file system m_packageDirPath = package_dir + TXT("/"); return true; } if (!package_dir.IsDirectory()) { // Packages should not be files return false; } // But internally we will store this m_packageDirPath = package_dir + TXT("/"); return true; }
/// @copydoc PackageLoader::BeginLoadObject() size_t CachePackageLoader::BeginLoadObject( AssetPath path, Reflect::ObjectResolver *pResolver, bool forceReload ) { HELIUM_ASSERT( m_pCache ); HELIUM_ASSERT( !forceReload ); // Not supported // Don't load packages from the cache, but instead create them dynamically. if( path.IsPackage() ) { HELIUM_TRACE( TraceLevels::Debug, "CachePackageLoader::BeginLoadObject(): \"%s\" is a package, resolving immediately.\n", *path.ToString() ); LoadRequest* pRequest = m_loadRequestPool.Allocate(); HELIUM_ASSERT( pRequest ); pRequest->pEntry = NULL; pRequest->pResolver = pResolver; ResolvePackage( pRequest->spObject, path ); HELIUM_ASSERT( pRequest->spObject ); SetInvalid( pRequest->asyncLoadId ); pRequest->pAsyncLoadBuffer = NULL; pRequest->pPropertyDataBegin = NULL; pRequest->pPropertyDataEnd = NULL; pRequest->pPersistentResourceDataBegin = NULL; pRequest->pPersistentResourceDataEnd = NULL; SetInvalid( pRequest->ownerLoadIndex ); HELIUM_ASSERT( !pRequest->spOwner ); pRequest->forceReload = forceReload; pRequest->flags = LOAD_FLAG_PRELOADED; size_t requestId = m_loadRequests.Add( pRequest ); return requestId; } const Cache::Entry* pEntry = m_pCache->FindEntry( path, 0 ); if( !pEntry ) { HELIUM_TRACE( TraceLevels::Debug, "CachePackageLoader::BeginLoadObject(): \"%s\" is not cached in this package. No load request added.\n", *path.ToString() ); return Invalid< size_t >(); } #ifndef NDEBUG size_t loadRequestSize = m_loadRequests.GetSize(); for( size_t loadRequestIndex = 0; loadRequestIndex < loadRequestSize; ++loadRequestIndex ) { if( !m_loadRequests.IsElementValid( loadRequestIndex ) ) { continue; } LoadRequest* pRequest = m_loadRequests[ loadRequestIndex ]; HELIUM_ASSERT( pRequest ); HELIUM_ASSERT( pRequest->pEntry != pEntry ); if( pRequest->pEntry == pEntry ) { HELIUM_TRACE( TraceLevels::Error, "CachePackageLoader::BeginLoadObject(): Duplicate load request of \"%s\". No load request added.\n", *path.ToString() ); return Invalid< size_t >(); } } #endif LoadRequest* pRequest = m_loadRequestPool.Allocate(); HELIUM_ASSERT( pRequest ); pRequest->pEntry = pEntry; pRequest->pResolver = pResolver; HELIUM_ASSERT( !pRequest->spObject ); SetInvalid( pRequest->asyncLoadId ); pRequest->pAsyncLoadBuffer = NULL; pRequest->pPropertyDataBegin = NULL; pRequest->pPropertyDataEnd = NULL; pRequest->pPersistentResourceDataBegin = NULL; pRequest->pPersistentResourceDataEnd = NULL; SetInvalid( pRequest->ownerLoadIndex ); HELIUM_ASSERT( !pRequest->spOwner ); pRequest->forceReload = forceReload; pRequest->flags = 0; // If a fully-loaded object already exists with the same name, do not attempt to re-load the object (just mark // the request as complete). pRequest->spObject = Asset::FindObject( pEntry->path ); Asset* pObject = pRequest->spObject; if( pObject && pObject->IsFullyLoaded() ) { HELIUM_TRACE( TraceLevels::Debug, "CachePackageLoader::BeginLoadObject(): \"%s\" is already fully loaded. Bypassing load process.\n", *path.ToString() ); pRequest->flags = LOAD_FLAG_PRELOADED; } else { HELIUM_ASSERT( !pObject || !pObject->GetAnyFlagSet( Asset::FLAG_LOADED | Asset::FLAG_LINKED ) ); HELIUM_TRACE( TraceLevels::Debug, "CachePackageLoader::BeginLoadObject(): Issuing async load of property data for \"%s\".\n", *path.ToString() ); size_t entrySize = pEntry->size; pRequest->pAsyncLoadBuffer = static_cast< uint8_t* >( DefaultAllocator().Allocate( entrySize ) ); HELIUM_ASSERT( pRequest->pAsyncLoadBuffer ); AsyncLoader* pAsyncLoader = AsyncLoader::GetInstance(); HELIUM_ASSERT( pAsyncLoader ); pRequest->asyncLoadId = pAsyncLoader->QueueRequest( pRequest->pAsyncLoadBuffer, m_pCache->GetCacheFileName(), pEntry->offset, entrySize ); HELIUM_ASSERT( IsValid( pRequest->asyncLoadId ) ); } size_t requestId = m_loadRequests.Add( pRequest ); HELIUM_TRACE( TraceLevels::Debug, "CachePackageLoader::BeginLoadObject(): Load request for \"%s\" added (ID: %" PRIuSZ ").\n", *path.ToString(), requestId ); return requestId; }
/// Get the package loader to use to load the object with the given path, creating it if necessary. /// /// @param[in] path Asset path. /// /// @return Package loader to use to load the specified object. LoosePackageLoader* LoosePackageLoaderMap::GetPackageLoader( AssetPath path ) { HELIUM_ASSERT( !path.IsEmpty() ); // Resolve the object's package. AssetPath packagePath = path; while( !packagePath.IsPackage() ) { packagePath = packagePath.GetParent(); if( packagePath.IsEmpty() ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "LoosePackageLoaderMap::GetPackageLoader(): Cannot resolve package loader for \"%s\", as it " ) TXT( "is not located in a package.\n" ) ), *path.ToString() ); return NULL; } } // Locate an existing package loader. ConcurrentHashMap< AssetPath, LoosePackageLoader* >::ConstAccessor constMapAccessor; if( m_packageLoaderMap.Find( constMapAccessor, packagePath ) ) { LoosePackageLoader* pLoader = constMapAccessor->Second(); HELIUM_ASSERT( pLoader ); return pLoader; } // Add a new package loader entry. ConcurrentHashMap< AssetPath, LoosePackageLoader* >::Accessor mapAccessor; bool bInserted = m_packageLoaderMap.Insert( mapAccessor, KeyValue< AssetPath, LoosePackageLoader* >( packagePath, NULL ) ); if( bInserted ) { // Entry added, so create and initialize the package loader. LoosePackageLoader* pLoader = new LoosePackageLoader; HELIUM_ASSERT( pLoader ); bool bInitResult = pLoader->Initialize( packagePath ); HELIUM_ASSERT( bInitResult ); if( !bInitResult ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LoosePackageLoaderMap::GetPackageLoader(): Failed to initialize package loader for \"%s\".\n" ), *packagePath.ToString() ); m_packageLoaderMap.Remove( mapAccessor ); return NULL; } HELIUM_VERIFY( pLoader->BeginPreload() ); mapAccessor->Second() = pLoader; } // PMD: I am not 100% sure this is all thread safe. I think it could break if: // - Thread 1 inserts a key/value of path and NULL, as above // - Thread 2 returns false from the insert so we come straight here // - Thread 2 tries to use pLoader before thread 1 assigns ploader to the value of the key // Note: If we did not do this assert here, we could potentially get nulls from this same thread later when // trying to find pLoader. // // Leaving it alone for now since I'm not sure, but if this assert gets tripped, we need to revisit this. // Easy fix may be to allocate and construct (but don't completely init) an LoosePackageLoader, and try // to insert that directly rather than the null above. If insert succeeds, finish, else ditch our loader // and grab the one out of the array LoosePackageLoader* pLoader = mapAccessor->Second(); HELIUM_ASSERT( pLoader ); return pLoader; }
/// @copydoc AssetLoader::CacheObject() bool LooseAssetLoader::CacheObject( Asset* pAsset, bool bEvictPlatformPreprocessedResourceData ) { HELIUM_ASSERT( pAsset ); HELIUM_TRACE( TraceLevels::Info, TXT( "LooseAssetLoader::CacheObject(): Caching asset %s.\n" ), *pAsset->GetPath().ToString() ); // Don't cache broken objects or packages. if( pAsset->GetAnyFlagSet( Asset::FLAG_BROKEN ) || pAsset->IsPackage() ) { return false; } // Make sure we have an object preprocessor instance with which to cache the object. AssetPreprocessor* pAssetPreprocessor = AssetPreprocessor::GetStaticInstance(); if( !pAssetPreprocessor ) { HELIUM_TRACE( TraceLevels::Warning, TXT( "LooseAssetLoader::CacheObject(): Missing AssetPreprocessor to use for caching.\n" ) ); return false; } // User configuration objects should not be cached. AssetPath objectPath = pAsset->GetPath(); Config& rConfig = Config::GetStaticInstance(); // Only cache the files we care about if ( rConfig.IsAssetPathInUserConfigPackage(objectPath) ) { return false; } int64_t objectTimestamp = pAsset->GetAssetFileTimeStamp(); if( !pAsset->IsDefaultTemplate() ) { Resource* pResource = Reflect::SafeCast< Resource >( pAsset ); if( pResource ) { AssetPath baseResourcePath = pResource->GetPath(); HELIUM_ASSERT( !baseResourcePath.IsPackage() ); for( ; ; ) { AssetPath parentPath = baseResourcePath.GetParent(); if( parentPath.IsEmpty() || parentPath.IsPackage() ) { break; } baseResourcePath = parentPath; } FilePath sourceFilePath; if ( !FileLocations::GetDataDirectory( sourceFilePath ) ) { HELIUM_TRACE( TraceLevels::Warning, TXT( "LooseAssetLoader::CacheObject(): Could not obtain data directory.\n" ) ); return false; } sourceFilePath += baseResourcePath.ToFilePathString().GetData(); Status stat; stat.Read( sourceFilePath.Get().c_str() ); int64_t sourceFileTimestamp = stat.m_ModifiedTime; if( sourceFileTimestamp > objectTimestamp ) { objectTimestamp = sourceFileTimestamp; } } } // Cache the object. bool bSuccess = pAssetPreprocessor->CacheObject( pAsset, objectTimestamp, bEvictPlatformPreprocessedResourceData ); if( !bSuccess ) { HELIUM_TRACE( TraceLevels::Error, TXT( "LooseAssetLoader: Failed to cache object \"%s\".\n" ), *objectPath.ToString() ); } return bSuccess; }
/// Set this path to the combination of two paths. /// /// @param[in] pRootPath Root portion of the path. /// @param[in] subPath Sub-path component. /// /// @return True if the paths could be joined into a valid path (to which this path was set), false if joining was /// invalid. bool AssetPath::Join( const char* pRootPath, AssetPath subPath ) { if( !pRootPath || pRootPath[ 0 ] == TXT( '\0' ) ) { m_pEntry = subPath.m_pEntry; return true; } // Parse the root path into a series of names. StackMemoryHeap<>& rStackHeap = ThreadLocalStackAllocator::GetMemoryHeap(); StackMemoryHeap<>::Marker stackMarker( rStackHeap ); Name* pRootPathNames; uint32_t* pRootPathIndices; size_t rootPathNameCount; size_t rootPathPackageCount; if( !Parse( pRootPath, rStackHeap, pRootPathNames, pRootPathIndices, rootPathNameCount, rootPathPackageCount ) ) { return false; } if( rootPathNameCount != rootPathPackageCount ) { AssetPath testSubPathComponent = subPath.GetParent(); AssetPath subPathComponent; do { subPathComponent = testSubPathComponent; testSubPathComponent = testSubPathComponent.GetParent(); if( subPathComponent.IsPackage() ) { HELIUM_TRACE( TraceLevels::Error, ( TXT( "AssetPath::Join(): Cannot combine \"%s\" and \"%s\" (second path is rooted in a " ) TXT( "package, while the first path ends in an object).\n" ) ), pRootPath, *subPath.ToString() ); return false; } } while( !testSubPathComponent.IsEmpty() ); } // Assemble the list of path names in reverse order for performing the object path lookup/add. size_t nameCount = rootPathNameCount; size_t packageCount = rootPathPackageCount; AssetPath testPath; for( testPath = subPath; !testPath.IsEmpty(); testPath = testPath.GetParent() ) { ++nameCount; if( testPath.IsPackage() ) { ++packageCount; } } Name* pEntryNames = static_cast< Name* >( rStackHeap.Allocate( sizeof( Name ) * nameCount ) ); HELIUM_ASSERT( pEntryNames ); uint32_t* pInstanceIndices = static_cast< uint32_t* >( rStackHeap.Allocate( sizeof( uint32_t ) * nameCount ) ); HELIUM_ASSERT( pInstanceIndices ); Name* pCurrentName = pEntryNames; uint32_t* pCurrentIndex = pInstanceIndices; for( testPath = subPath; !testPath.IsEmpty(); testPath = testPath.GetParent() ) { *pCurrentName = testPath.GetName(); *pCurrentIndex = testPath.GetInstanceIndex(); ++pCurrentName; ++pCurrentIndex; } ArrayCopy( pCurrentName, pRootPathNames, rootPathNameCount ); ArrayCopy( pCurrentIndex, pRootPathIndices, rootPathNameCount ); // Set the path. Set( pEntryNames, pInstanceIndices, nameCount, packageCount ); return true; }