// Copyright (C) 2020 Jérôme Leclercq // 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 namespace Nz { /*! * \ingroup core * \class Nz::ResourceLoader * \brief Core class that represents a loader of resources */ /*! * \brief Checks whether the extension of the file is supported * \return true if supported * * \param extension Extension of the file */ template bool ResourceLoader::IsExtensionSupported(const std::string& extension) { for (Loader& loader : Type::s_loaders) { ExtensionGetter isExtensionSupported = std::get<0>(loader); if (isExtensionSupported && isExtensionSupported(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 ObjectRef ResourceLoader::LoadFromFile(const std::filesystem::path& filePath, const Parameters& parameters) { 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.generic_u8string()); // Open only if needed bool found = false; for (Loader& loader : Type::s_loaders) { ExtensionGetter isExtensionSupported = std::get<0>(loader); if (!isExtensionSupported || !isExtensionSupported(ext)) continue; StreamChecker checkFunc = std::get<1>(loader); StreamLoader streamLoader = std::get<2>(loader); FileLoader fileLoader = std::get<3>(loader); if (checkFunc && !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 (fileLoader) { if (checkFunc) { file.SetCursorPos(0); recognized = checkFunc(file, parameters); if (recognized == Ternary_False) continue; else found = true; } else { recognized = Ternary_Unknown; found = true; } ObjectRef resource = fileLoader(filePath, parameters); if (resource) { resource->SetFilePath(filePath); return resource; } } else { file.SetCursorPos(0); recognized = checkFunc(file, parameters); if (recognized == Ternary_False) continue; else if (recognized == Ternary_True) found = true; file.SetCursorPos(0); ObjectRef resource = 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 ObjectRef ResourceLoader::LoadFromMemory(const void* data, std::size_t size, const Parameters& parameters) { NazaraAssert(data, "Invalid data pointer"); NazaraAssert(size, "No data to load"); NazaraAssert(parameters.IsValid(), "Invalid parameters"); MemoryView stream(data, size); bool found = false; for (Loader& loader : Type::s_loaders) { StreamChecker checkFunc = std::get<1>(loader); StreamLoader streamLoader = std::get<2>(loader); MemoryLoader memoryLoader = std::get<4>(loader); Ternary recognized = Ternary_Unknown; if (memoryLoader) { if (checkFunc) { stream.SetCursorPos(0); recognized = checkFunc(stream, parameters); if (recognized == Ternary_False) continue; else found = true; } else { recognized = Ternary_Unknown; found = true; } ObjectRef resource = memoryLoader(data, size, parameters); if (resource) return resource; } else { stream.SetCursorPos(0); recognized = checkFunc(stream, parameters); if (recognized == Ternary_False) continue; else if (recognized == Ternary_True) found = true; stream.SetCursorPos(0); ObjectRef resource = 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 ObjectRef ResourceLoader::LoadFromStream(Stream& stream, const Parameters& parameters) { NazaraAssert(stream.GetCursorPos() < stream.GetSize(), "No data to load"); NazaraAssert(parameters.IsValid(), "Invalid parameters"); UInt64 streamPos = stream.GetCursorPos(); bool found = false; for (Loader& loader : Type::s_loaders) { StreamChecker checkFunc = std::get<1>(loader); StreamLoader streamLoader = std::get<2>(loader); stream.SetCursorPos(streamPos); // Does the loader support these data ? Ternary recognized = checkFunc(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 ObjectRef resource = 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 the loader * * \param extensionGetter A function to test whether the extension (as a string) is supported by this loader * \param checkFunc A function to check the stream with the parser * \param streamLoader A function to load the data from a stream in the resource * \param fileLoader Optional function to load the data from a file in the resource * \param memoryLoader Optional function to load the data from a raw memory in the resource */ template void ResourceLoader::RegisterLoader(ExtensionGetter extensionGetter, StreamChecker checkFunc, StreamLoader streamLoader, FileLoader fileLoader, MemoryLoader memoryLoader) { NazaraAssert(checkFunc || !streamLoader, "StreamLoader present without StreamChecker"); NazaraAssert(fileLoader || memoryLoader || streamLoader, "A loader function is mandatory"); Type::s_loaders.push_front(std::make_tuple(extensionGetter, checkFunc, streamLoader, fileLoader, memoryLoader)); } /*! * \brief Unregisters the loader * * \param extensionGetter A function to test whether the extension (as a string) is supported by this loader * \param checkFunc A function to check the stream with the parser * \param streamLoader A function to load the data from a stream in the resource * \param fileLoader Optional function to load the data from a file in the resource * \param memoryLoader Optional function to load the data from a raw memory in the resource */ template void ResourceLoader::UnregisterLoader(ExtensionGetter extensionGetter, StreamChecker checkFunc, StreamLoader streamLoader, FileLoader fileLoader, MemoryLoader memoryLoader) { Type::s_loaders.remove(std::make_tuple(extensionGetter, checkFunc, streamLoader, fileLoader, memoryLoader)); } } #include