// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) // This file is part of the "Nazara Engine - Core module" // For conditions of distribution and use, see copyright notice in Config.hpp #include #include #include #include #include #include #include #include namespace Nz { /*! * \ingroup core * \class Nz::ResourceLoader * \brief Core class that represents a loader of resources */ /*! * \brief Unregister every loader registered */ template void ResourceLoader::Clear() { m_loaders.clear(); } /*! * \brief Checks whether the extension of the file is supported * \return true if supported * * \param extension Extension of the file (ex: ".png") */ template bool ResourceLoader::IsExtensionSupported(const std::string_view& extension) const { NazaraAssert(extension.size() >= 2 || extension.front() != '.', "extension should start with a ."); for (auto& loaderPtr : m_loaders) { const Entry& loader = *loaderPtr; if (loader.extensionSupport && loader.extensionSupport(extension)) return true; } return false; } /*! * \brief Loads a resource from a file * \return loaded resources or null pointer if failed * * \param filePath Path to the resource * \param parameters Parameters for the load * * \ret Loaded resources */ template std::shared_ptr ResourceLoader::LoadFromFile(const std::filesystem::path& filePath, const Parameters& parameters) const { NazaraAssert(parameters.IsValid(), "Invalid parameters"); std::string ext = ToLower(PathToString(filePath.extension())); if (ext.empty()) { NazaraError("Failed to get file extension from \"" + PathToString(filePath) + '"'); return nullptr; } File file(filePath); // Open only if needed bool found = false; for (auto& loaderPtr : m_loaders) { const Entry& loader = *loaderPtr; if (loader.extensionSupport && !loader.extensionSupport(ext)) continue; if (loader.parameterFilter && !loader.parameterFilter(parameters)) continue; Result, ResourceLoadingError> result = Nz::Err(ResourceLoadingError::Unrecognized); if (loader.fileLoader) result = loader.fileLoader(filePath, parameters); else if (loader.streamLoader) { if (!file.IsOpen()) { if (!file.Open(OpenMode::ReadOnly)) { NazaraError("failed to load resource: unable to open \"" + PathToString(filePath) + '"'); return nullptr; } } else file.SetCursorPos(0); result = loader.streamLoader(file, parameters); } if (!result) { ResourceLoadingError error = result.GetError(); if (error != ResourceLoadingError::Unrecognized) NazaraError("failed to load resource: loader failed"); else found = true; continue; } std::shared_ptr resource = std::move(result).GetValue(); resource->SetFilePath(filePath); return resource; } if (found) NazaraError("failed to load resource from file \"" + PathToString(filePath) + "\": all loaders failed"); else NazaraError("failed to load resource from file \"" + PathToString(filePath) + "\": no loader found for extension \"" + ext + '"'); return nullptr; } /*! * \brief Loads a resource from a raw memory, a size and parameters * \return loaded resources or null pointer if failed * * \param data Raw memory of the resource * \param size Size available for the read * \param parameters Parameters for the load */ template std::shared_ptr ResourceLoader::LoadFromMemory(const void* data, std::size_t size, const Parameters& parameters) const { NazaraAssert(data, "Invalid data pointer"); NazaraAssert(size, "No data to load"); NazaraAssert(parameters.IsValid(), "Invalid parameters"); MemoryView stream(data, size); bool found = false; for (auto& loaderPtr : m_loaders) { const Entry& loader = *loaderPtr; if (loader.parameterFilter && !loader.parameterFilter(parameters)) continue; Result, ResourceLoadingError> result = Nz::Err(ResourceLoadingError::Unrecognized); if (loader.memoryLoader) result = loader.memoryLoader(data, size, parameters); else if (loader.streamLoader) { stream.SetCursorPos(0); result = loader.streamLoader(stream, parameters); } if (!result) { ResourceLoadingError error = result.GetError(); if (error != ResourceLoadingError::Unrecognized) NazaraError("failed to load resource: loader failed"); else found = true; continue; } return std::move(result).GetValue(); } if (found) NazaraError("failed to load resource from memory: all loaders failed"); else NazaraError("failed to load resource from memory: no loader found"); return nullptr; } /*! * \brief Loads a resource from a stream and parameters * \return loaded resources or null pointer if failed * * \param stream Stream of the resource * \param parameters Parameters for the load */ template std::shared_ptr ResourceLoader::LoadFromStream(Stream& stream, const Parameters& parameters) const { NazaraAssert(stream.GetCursorPos() < stream.GetSize(), "No data to load"); NazaraAssert(parameters.IsValid(), "Invalid parameters"); UInt64 streamPos = stream.GetCursorPos(); bool found = false; for (auto& loaderPtr : m_loaders) { const Entry& loader = *loaderPtr; if (loader.parameterFilter && !loader.parameterFilter(parameters)) continue; stream.SetCursorPos(streamPos); // Load resource Result, ResourceLoadingError> result = loader.streamLoader(stream, parameters); if (!result) { ResourceLoadingError error = result.GetError(); if (error != ResourceLoadingError::Unrecognized) NazaraError("failed to load resource: loader failed"); else found = true; continue; } return std::move(result).GetValue(); } if (found) NazaraError("failed to load resource from stream: all loaders failed"); else NazaraError("Failed to load resource from from stream: no loader found"); return nullptr; } /*! * \brief Registers a loader * \return A pointer to the registered Entry which can be unsed to unregister it later * * \param loader A collection of loader callbacks that will be registered * * \see UnregisterLoader */ template auto ResourceLoader::RegisterLoader(Entry loader) -> const Entry* { NazaraAssert(loader.fileLoader || loader.memoryLoader || loader.streamLoader, "A loader function is mandatory"); auto it = m_loaders.emplace(m_loaders.begin(), std::make_unique(std::move(loader))); return it->get(); } /*! * \brief Unregisters a loader * * \param loader A pointer to a loader returned by RegisterLoad * * \see RegisterLoader */ template void ResourceLoader::UnregisterLoader(const Entry* loader) { auto it = std::find_if(m_loaders.begin(), m_loaders.end(), [&](const std::unique_ptr& loaderPtr) { return loaderPtr.get() == loader; }); if (it != m_loaders.end()) m_loaders.erase(it); } } #include