diff --git a/include/Nazara/Core/ResourceSaver.hpp b/include/Nazara/Core/ResourceSaver.hpp new file mode 100644 index 000000000..c0e22566c --- /dev/null +++ b/include/Nazara/Core/ResourceSaver.hpp @@ -0,0 +1,50 @@ +// Copyright (C) 2015 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 + +#pragma once + +#ifndef NAZARA_RESOURCESAVER_HPP +#define NAZARA_RESOURCESAVER_HPP + +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class Stream; + + template + class ResourceSaver + { + friend Type; + + public: + using FormatQuerier = bool (*)(const String& format); + using FileSaver = bool (*)(const Type& resource, const String& filePath, const Parameters& parameters); + using StreamSaver = bool (*)(const Type& resource, const String& format, Stream& stream, const Parameters& parameters); + + ResourceSaver() = delete; + ~ResourceSaver() = delete; + + static bool IsFormatSupported(const String& extension); + + static bool SaveToFile(const Type& resource, const String& filePath, const Parameters& parameters = Parameters()); + static bool SaveToStream(const Type& resource, Stream& stream, const String& format, const Parameters& parameters = Parameters()); + + static void RegisterSaver(FormatQuerier formatQuerier, StreamSaver streamSaver, FileSaver fileSaver = nullptr); + static void UnregisterSaver(FormatQuerier formatQuerier, StreamSaver streamSaver, FileSaver fileSaver = nullptr); + + private: + using Saver = std::tuple; + using SaverList = std::list; + }; +} + +#include + +#endif // NAZARA_RESOURCESAVER_HPP diff --git a/include/Nazara/Core/ResourceSaver.inl b/include/Nazara/Core/ResourceSaver.inl new file mode 100644 index 000000000..954b86a0e --- /dev/null +++ b/include/Nazara/Core/ResourceSaver.inl @@ -0,0 +1,190 @@ +// Copyright (C) 2015 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 + +namespace Nz +{ + /*! + * \ingroup core + * \class Nz::ResourceSaver + * \brief Core class that represents a list of saver functions for a specific resource type + */ + + /*! + * \brief Checks whether the extension of the file is supported + * \return true if supported + * + * \param extension Extension of the file + */ + template + bool ResourceSaver::IsFormatSupported(const String& extension) + { + for (Saver& saver : Type::s_savers) + { + ExtensionGetter isExtensionSupported = std::get<0>(loader); + + if (isExtensionSupported && isExtensionSupported(extension)) + return true; + } + + return false; + } + + /*! + * \brief Saves a resource to a file + * \return true if successfully saved + * + * \param resource Resource to save + * \param filePath Path to the file where the resource will be written + * \param parameters Parameters for the save + * + * \remark The previous file content will be discarded, to prevent this behavior you should use SaveToStream + * \remark The file extension will be used as format for the saver ("image.png" => "png", to write a specified format to a user-specified extension you should use SaveToStream + * + * \seealso SaveToStream + */ + template + bool ResourceSaver::SaveToFile(const Type& resource, const String& filePath, const Parameters& parameters) + { + NazaraAssert(parameters.IsValid(), "Invalid parameters"); + + String path = File::NormalizePath(filePath); + String ext = path.SubStringFrom('.', -1, true).ToLower(); + if (ext.IsEmpty()) + { + NazaraError("Failed to get file extension from \"" + filePath + '"'); + return false; + } + + File file(path); // Opened only is required + + bool found = false; + for (Saver& saver : Type::s_savers) + { + FormatQuerier formatQuerier = std::get<0>(saver); + if (!formatQuerier || !formatQuerier(ext)) + continue; + + found = true; + + StreamSaver streamSeaver = std::get<1>(saver); + FileSaver fileSaver = std::get<2>(saver); + + if (fileSaver) + { + if (fileSaver(resource, filePath, parameters)) + return true; + } + else + { + if (!file.Open(OpenMode_WriteOnly)) + { + NazaraError("Failed to save to file: unable to open \"" + filePath + "\" in write mode"); + return false; + } + + if (streamSeaver(resource, ext, file, parameters)) + return true; + } + + NazaraWarning("Saver failed"); + } + + if (found) + NazaraError("Failed to save resource: all savers failed"); + else + NazaraError("Failed to save resource: no saver found for extension \"" + ext + '"'); + + return false; + } + + /*! + * \brief Saves a resource to a stream + * \return true if successfully saved + * + * \param resource Resource to load + * \param stream Stream with write access where the resource data will be written + * \param format Data format to save the resource to + * \param parameters Parameters for the saving + * + * \seealso SaveToFile + */ + template + bool ResourceSaver::SaveToStream(const Type& resource, Stream& stream, const String& format, const Parameters& parameters) + { + NazaraAssert(stream.IsWritable(), "Stream is not writable"); + NazaraAssert(parameters.IsValid(), "Invalid parameters"); + + UInt64 streamPos = stream.GetCursorPos(); + bool found = false; + for (Saver& saver : Type::s_savers) + { + FormatQuerier formatQuerier = std::get<0>(saver); + if (!formatQuerier || !formatQuerier(format)) + continue; + + found = true; + + StreamSaver streamSeaver = std::get<1>(saver); + + // We move the stream to its old position + stream.SetCursorPos(streamPos); + + // Load of the resource + if (streamSeaver(resource, format, stream, parameters)) + return true; + + NazaraWarning("Saver failed"); + } + + if (found) + NazaraError("Failed to save resource: all savers failed"); + else + NazaraError("Failed to save resource: no saver found for format \"" + format + '"'); + + return false; + } + + /*! + * \brief Registers a saver + * + * \param formatQuerier A function to test whether the format (as a string) is supported by this saver + * \param streamSaver A function which saves the resource to a stream + * \param fileSaver Optional function which saves the resource directly to a file given a file path + * + * \remark The fileSaver argument is only present for compatibility with external savers which cannot be interfaced with streams + * \remark At least one saver is required + */ + template + void ResourceSaver::RegisterSaver(FormatQuerier formatQuerier, StreamSaver streamSaver, FileSaver fileSaver) + { + NazaraAssert(formatQuerier, "A format querier is mandaroty"); + NazaraAssert(streamSaver || fileSaver, "A saver function is mandaroty"); + + Type::s_savers.push_front(std::make_tuple(formatQuerier, streamSaver, fileSaver)); + } + + /*! + * \brief Unregisters a saver + * + * \param formatQuerier A function to test whether the format (as a string) is supported by this saver + * \param streamSaver A function which saves the resource to a stream + * \param fileSaver A function function which saves the resource directly to a file given a file path + * + * \remark The saver will only be unregistered if the function pointers are exactly the same + */ + template + void ResourceSaver::UnregisterSaver(FormatQuerier formatQuerier, StreamSaver streamSaver, FileSaver fileSaver) + { + Type::s_savers.remove(std::make_tuple(formatQuerier, streamSaver, fileSaver)); + } +} + +#include