// 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 { 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 true if successfully loaded * * \param resource Resource to load * \param filePath Path to the resource * \param parameters Parameters for the load * * \remark Produces a NazaraError if resource is nullptr with NAZARA_CORE_SAFE defined * \remark Produces a NazaraError if parameters are invalid with NAZARA_CORE_SAFE defined * \remark Produces a NazaraError if filePath has no extension * \remark Produces a NazaraError if file count not be opened * \remark Produces a NazaraWarning if loader failed * \remark Produces a NazaraError if all loaders failed or no loader was found */ template std::shared_ptr ResourceLoader::LoadFromFile(const std::filesystem::path& filePath, const Parameters& parameters) const { NazaraAssert(parameters.IsValid(), "Invalid parameters"); std::string ext = ToLower(filePath.extension().generic_u8string()); if (ext.empty()) { NazaraError("Failed to get file extension from \"" + filePath.generic_u8string() + '"'); return nullptr; } if (ext[0] == '.') ext.erase(ext.begin()); 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.streamChecker && !file.IsOpen()) { if (!file.Open(OpenMode::ReadOnly)) { NazaraError("Failed to load file: unable to open \"" + filePath.generic_u8string() + '"'); return nullptr; } } Ternary recognized = Ternary::Unknown; if (loader.fileLoader) { if (loader.streamChecker) { file.SetCursorPos(0); recognized = loader.streamChecker(file, parameters); if (recognized == Ternary::False) continue; else found = true; } else { recognized = Ternary::Unknown; found = true; } std::shared_ptr resource = loader.fileLoader(filePath, parameters); if (resource) { resource->SetFilePath(filePath); return resource; } } else { assert(loader.streamChecker); file.SetCursorPos(0); recognized = loader.streamChecker(file, parameters); if (recognized == Ternary::False) continue; else if (recognized == Ternary::True) found = true; file.SetCursorPos(0); std::shared_ptr resource = loader.streamLoader(file, parameters); if (resource) { resource->SetFilePath(filePath); return resource; } } if (recognized == Ternary::True) NazaraWarning("Loader failed"); } if (found) NazaraError("Failed to load file: all loaders failed"); else NazaraError("Failed to load file: no loader found for extension \"" + ext + '"'); return nullptr; } /*! * \brief Loads a resource from a raw memory, a size and parameters * \return true if successfully loaded * * \param resource Resource to load * \param data Raw memory of the resource * \param size Size available for the read * \param parameters Parameters for the load * * \remark Produces a NazaraError if resource is nullptr with NAZARA_CORE_SAFE defined * \remark Produces a NazaraError if size is 0 with NAZARA_CORE_SAFE defined * \remark Produces a NazaraError if parameters are invalid with NAZARA_CORE_SAFE defined * \remark Produces a NazaraWarning if loader failed * \remark Produces a NazaraError if all loaders failed or no loader was found */ 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); UInt64 streamPos = stream.GetCursorPos(); bool found = false; for (auto& loaderPtr : m_loaders) { const Entry& loader = *loaderPtr; Ternary recognized = Ternary::Unknown; if (loader.memoryLoader) { if (loader.streamChecker) { stream.SetCursorPos(streamPos); recognized = loader.streamChecker(stream, parameters); if (recognized == Ternary::False) continue; else found = true; } else { recognized = Ternary::Unknown; found = true; } std::shared_ptr resource = loader.memoryLoader(data, size, parameters); if (resource) return resource; } else { stream.SetCursorPos(streamPos); recognized = loader.streamChecker(stream, parameters); if (recognized == Ternary::False) continue; else if (recognized == Ternary::True) found = true; stream.SetCursorPos(streamPos); std::shared_ptr resource = loader.streamLoader(stream, parameters); if (resource) return resource; } if (recognized == Ternary::True) NazaraWarning("Loader failed"); } if (found) NazaraError("Failed to load file: all loaders failed"); else NazaraError("Failed to load file: no loader found"); return nullptr; } /*! * \brief Loads a resource from a stream and parameters * \return true if successfully loaded * * \param resource Resource to load * \param stream Stream of the resource * \param parameters Parameters for the load * * \remark Produces a NazaraError if resource is nullptr with NAZARA_CORE_SAFE defined * \remark Produces a NazaraError if stream has no data with NAZARA_CORE_SAFE defined * \remark Produces a NazaraError if parameters are invalid with NAZARA_CORE_SAFE defined * \remark Produces a NazaraWarning if loader failed * \remark Produces a NazaraError if all loaders failed or no loader was found */ 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; stream.SetCursorPos(streamPos); // Does the loader support these data ? Ternary recognized = loader.streamChecker(stream, parameters); if (recognized == Ternary::False) continue; else if (recognized == Ternary::True) found = true; // We move the stream to its old position stream.SetCursorPos(streamPos); // Load of the resource std::shared_ptr resource = loader.streamLoader(stream, parameters); if (resource) return resource; if (recognized == Ternary::True) NazaraWarning("Loader failed"); } if (found) NazaraError("Failed to load file: all loaders failed"); else NazaraError("Failed to load file: 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.streamChecker || !loader.streamLoader, "StreamLoader present without StreamChecker"); 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