diff --git a/include/Nazara/Core/Enums.hpp b/include/Nazara/Core/Enums.hpp index f0e06d94f..e1a9cb571 100644 --- a/include/Nazara/Core/Enums.hpp +++ b/include/Nazara/Core/Enums.hpp @@ -191,6 +191,15 @@ namespace Nz constexpr std::size_t ProcessorVendorCount = static_cast(ProcessorVendor::Max) + 1; + enum class ResourceLoadingError + { + DecodingError, + FailedToOpenFile, + Internal, + Unsupported, + Unrecognized + }; + enum class SphereType { Cubic, @@ -218,15 +227,6 @@ namespace Nz }; using StreamOptionFlags = Flags; - - enum class Ternary - { - False, - True, - Unknown, - - Max = Unknown - }; } #endif // NAZARA_CORE_ENUMS_HPP diff --git a/include/Nazara/Core/ResourceLoader.hpp b/include/Nazara/Core/ResourceLoader.hpp index 1c7c7888e..1f94460bb 100644 --- a/include/Nazara/Core/ResourceLoader.hpp +++ b/include/Nazara/Core/ResourceLoader.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -29,11 +30,11 @@ namespace Nz public: struct Entry; - using ExtensionSupport = std::function; - using FileLoader = std::function(const std::filesystem::path& filePath, const Parameters& parameters)>; - using MemoryLoader = std::function(const void* data, std::size_t size, const Parameters& parameters)>; - using StreamChecker = std::function; - using StreamLoader = std::function(Stream& stream, const Parameters& parameters)>; + using ExtensionSupport = std::function; + using FileLoader = std::function, ResourceLoadingError>(const std::filesystem::path& filePath, const Parameters& parameters)>; + using MemoryLoader = std::function, ResourceLoadingError>(const void* data, std::size_t size, const Parameters& parameters)>; + using ParameterFilter = std::function; + using StreamLoader = std::function, ResourceLoadingError>(Stream& stream, const Parameters& parameters)>; ResourceLoader() = default; ResourceLoader(const ResourceLoader&) = delete; @@ -59,7 +60,7 @@ namespace Nz ExtensionSupport extensionSupport; FileLoader fileLoader; MemoryLoader memoryLoader; - StreamChecker streamChecker; + ParameterFilter parameterFilter; StreamLoader streamLoader; }; diff --git a/include/Nazara/Core/ResourceLoader.inl b/include/Nazara/Core/ResourceLoader.inl index b49d00dc8..8d43aac7c 100644 --- a/include/Nazara/Core/ResourceLoader.inl +++ b/include/Nazara/Core/ResourceLoader.inl @@ -32,11 +32,13 @@ namespace Nz * \brief Checks whether the extension of the file is supported * \return true if supported * - * \param extension Extension of the file (ex: "png") + * \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; @@ -50,18 +52,12 @@ namespace Nz /*! * \brief Loads a resource from a file - * \return true if successfully loaded + * \return loaded resources or null pointer if failed * - * \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 + * \ret Loaded resources */ template std::shared_ptr ResourceLoader::LoadFromFile(const std::filesystem::path& filePath, const Parameters& parameters) const @@ -75,9 +71,6 @@ namespace Nz return nullptr; } - if (ext[0] == '.') - ext.erase(ext.begin()); - File file(filePath); // Open only if needed bool found = false; @@ -88,89 +81,61 @@ namespace Nz 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 \"" + PathToString(filePath) + '"'); - return nullptr; - } - } + if (loader.parameterFilter && !loader.parameterFilter(parameters)) + continue; + + Result, ResourceLoadingError> result = Nz::Err(ResourceLoadingError::Unrecognized); - Ternary recognized = Ternary::Unknown; if (loader.fileLoader) + result = loader.fileLoader(filePath, parameters); + else if (loader.streamLoader) { - if (loader.streamChecker) + if (!file.IsOpen()) { - file.SetCursorPos(0); - - recognized = loader.streamChecker(file, parameters); - if (recognized == Ternary::False) - continue; - else - found = true; + if (!file.Open(OpenMode::ReadOnly)) + { + NazaraError("failed to load resource: unable to open \"" + PathToString(filePath) + '"'); + return nullptr; + } } else - { - recognized = Ternary::Unknown; - found = true; - } + file.SetCursorPos(0); - std::shared_ptr resource = loader.fileLoader(filePath, parameters); - if (resource) - { - resource->SetFilePath(filePath); - return resource; - } + result = loader.streamLoader(file, parameters); } - else + + if (!result) { - assert(loader.streamChecker); - - file.SetCursorPos(0); - - recognized = loader.streamChecker(file, parameters); - if (recognized == Ternary::False) - continue; - else if (recognized == Ternary::True) + ResourceLoadingError error = result.GetError(); + if (error != ResourceLoadingError::Unrecognized) + NazaraError("failed to load resource: loader failed"); + else found = true; - file.SetCursorPos(0); - - std::shared_ptr resource = loader.streamLoader(file, parameters); - if (resource) - { - resource->SetFilePath(filePath); - return resource; - } + continue; } - if (recognized == Ternary::True) - NazaraWarning("Loader failed"); + std::shared_ptr resource = std::move(result).GetValue(); + resource->SetFilePath(filePath); + + return resource; } if (found) - NazaraError("Failed to load file: all loaders failed"); + NazaraError("failed to load resource from file \"" + PathToString(filePath) + "\": all loaders failed"); else - NazaraError("Failed to load file: no loader found for extension \"" + ext + '"'); + 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 true if successfully loaded + * \return loaded resources or null pointer if failed * - * \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 @@ -181,77 +146,53 @@ namespace Nz 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.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) { - if (loader.streamChecker) - { - stream.SetCursorPos(streamPos); + stream.SetCursorPos(0); - recognized = loader.streamChecker(stream, parameters); - if (recognized == Ternary::False) - continue; - else - found = true; - } + result = loader.streamLoader(stream, parameters); + } + + if (!result) + { + ResourceLoadingError error = result.GetError(); + if (error != ResourceLoadingError::Unrecognized) + NazaraError("failed to load resource: loader failed"); 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; + continue; } - if (recognized == Ternary::True) - NazaraWarning("Loader failed"); + return std::move(result).GetValue(); } if (found) - NazaraError("Failed to load file: all loaders failed"); + NazaraError("failed to load resource from memory: all loaders failed"); else - NazaraError("Failed to load file: no loader found"); + NazaraError("failed to load resource from memory: no loader found"); return nullptr; } /*! * \brief Loads a resource from a stream and parameters - * \return true if successfully loaded + * \return loaded resources or null pointer if failed * - * \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 @@ -265,31 +206,31 @@ namespace Nz { const Entry& loader = *loaderPtr; - stream.SetCursorPos(streamPos); - - // Does the loader support these data ? - Ternary recognized = loader.streamChecker(stream, parameters); - if (recognized == Ternary::False) + if (loader.parameterFilter && !loader.parameterFilter(parameters)) 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; + // 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; - if (recognized == Ternary::True) - NazaraWarning("Loader failed"); + continue; + } + + return std::move(result).GetValue(); } if (found) - NazaraError("Failed to load file: all loaders failed"); + NazaraError("failed to load resource from stream: all loaders failed"); else - NazaraError("Failed to load file: no loader found"); + NazaraError("Failed to load resource from from stream: no loader found"); return nullptr; } @@ -305,7 +246,6 @@ namespace Nz 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))); diff --git a/include/Nazara/Core/ResourceSaver.inl b/include/Nazara/Core/ResourceSaver.inl index 357303900..04ffad9f0 100644 --- a/include/Nazara/Core/ResourceSaver.inl +++ b/include/Nazara/Core/ResourceSaver.inl @@ -37,6 +37,8 @@ namespace Nz template bool ResourceSaver::IsExtensionSupported(const std::string_view& extension) const { + NazaraAssert(extension.size() >= 2 || extension.front() != '.', "extension should start with a ."); + for (const auto& saverPtr : m_savers) { const Entry& saver = *saverPtr; @@ -72,9 +74,6 @@ namespace Nz return false; } - if (extension[0] == '.') - extension.erase(extension.begin()); - bool found = false; for (const auto& saverPtr : m_savers) { @@ -95,7 +94,7 @@ namespace Nz if (!file.Open(OpenMode::WriteOnly | OpenMode::Truncate)) { - NazaraError("Failed to save to file: unable to open \"" + PathToString(filePath) + "\" in write mode"); + NazaraError("failed to save to file: unable to open \"" + PathToString(filePath) + "\" in write mode"); return false; } @@ -107,9 +106,9 @@ namespace Nz } if (found) - NazaraError("Failed to save resource: all savers failed"); + NazaraError("failed to save resource: all savers failed"); else - NazaraError("Failed to save resource: no saver found for extension \"" + extension + '"'); + NazaraError("failed to save resource: no saver found for extension \"" + extension + '"'); return false; } @@ -148,13 +147,13 @@ namespace Nz if (saver.streamSaver(resource, format, stream, parameters)) return true; - NazaraWarning("Saver failed"); + NazaraWarning("saver failed"); } if (found) - NazaraError("Failed to save resource: all savers failed"); + NazaraError("failed to save resource: all savers failed"); else - NazaraError("Failed to save resource: no saver found for format \"" + format + '"'); + NazaraError("failed to save resource: no saver found for format \"" + format + '"'); return false; } diff --git a/include/Nazara/Utility/Formats/MD5AnimParser.hpp b/include/Nazara/Utility/Formats/MD5AnimParser.hpp index 6fbc35daf..509fb9e02 100644 --- a/include/Nazara/Utility/Formats/MD5AnimParser.hpp +++ b/include/Nazara/Utility/Formats/MD5AnimParser.hpp @@ -44,7 +44,7 @@ namespace Nz MD5AnimParser(Stream& stream); ~MD5AnimParser(); - Ternary Check(); + bool Check(); UInt32 GetAnimatedComponentCount() const; const Frame* GetFrames() const; diff --git a/include/Nazara/Utility/Formats/MD5MeshParser.hpp b/include/Nazara/Utility/Formats/MD5MeshParser.hpp index d499c565f..7084b78bc 100644 --- a/include/Nazara/Utility/Formats/MD5MeshParser.hpp +++ b/include/Nazara/Utility/Formats/MD5MeshParser.hpp @@ -55,7 +55,7 @@ namespace Nz MD5MeshParser(Stream& stream); ~MD5MeshParser(); - Ternary Check(); + bool Check(); const Joint* GetJoints() const; UInt32 GetJointCount() const; diff --git a/plugins/Assimp/Plugin.cpp b/plugins/Assimp/Plugin.cpp index 93b31bf1a..82a6912bf 100644 --- a/plugins/Assimp/Plugin.cpp +++ b/plugins/Assimp/Plugin.cpp @@ -236,30 +236,16 @@ void ProcessJoints(const Nz::MeshParams& parameters, const Nz::Matrix4f& transfo ProcessJoints(parameters, transformMatrix, invTransformMatrix, skeletalMesh, skeleton, node->mChildren[i], nextJointIndex, boneToJointIndex, seenNodes); } -bool IsSupported(const std::string_view& extension) +bool IsSupported(std::string_view extension) { - std::string dotExt; - dotExt.reserve(extension.size() + 1); - dotExt += '.'; - dotExt += extension; - - return (aiIsExtensionSupported(dotExt.data()) == AI_TRUE); + return (aiIsExtensionSupported(extension.data()) == AI_TRUE); } /************************************************************************/ /* Material loading */ /************************************************************************/ -Nz::Ternary CheckAnimation(Nz::Stream& /*stream*/, const Nz::AnimationParams& parameters) -{ - bool skip; - if (parameters.custom.GetBooleanParameter("SkipAssimpLoader", &skip) && skip) - return Nz::Ternary::False; - - return Nz::Ternary::Unknown; -} - -std::shared_ptr LoadAnimation(Nz::Stream& stream, const Nz::AnimationParams& parameters) +Nz::Result, Nz::ResourceLoadingError> LoadAnimation(Nz::Stream& stream, const Nz::AnimationParams& parameters) { NazaraAssert(parameters.IsValid(), "invalid animation parameters"); @@ -280,13 +266,13 @@ std::shared_ptr LoadAnimation(Nz::Stream& stream, const Nz::Anima if (!scene) { NazaraError("Assimp failed to import file: " + std::string(aiGetErrorString())); - return nullptr; + return Nz::Err(Nz::ResourceLoadingError::DecodingError); } if (!scene->HasAnimations()) { NazaraError("File has no animation"); - return nullptr; + return Nz::Err(Nz::ResourceLoadingError::DecodingError); } SceneInfo sceneInfo; @@ -771,16 +757,7 @@ std::shared_ptr ProcessSubMesh(const std::filesystem::path& originP return subMesh; } -Nz::Ternary CheckMesh(Nz::Stream& /*stream*/, const Nz::MeshParams& parameters) -{ - bool skip; - if (parameters.custom.GetBooleanParameter("SkipAssimpLoader", &skip) && skip) - return Nz::Ternary::False; - - return Nz::Ternary::Unknown; -} - -std::shared_ptr LoadMesh(Nz::Stream& stream, const Nz::MeshParams& parameters) +Nz::Result, Nz::ResourceLoadingError> LoadMesh(Nz::Stream& stream, const Nz::MeshParams& parameters) { std::string streamPath = Nz::PathToString(stream.GetPath()); @@ -838,7 +815,7 @@ std::shared_ptr LoadMesh(Nz::Stream& stream, const Nz::MeshParams& par if (!scene) { NazaraError("Assimp failed to import file: " + std::string(aiGetErrorString())); - return nullptr; + return Nz::Err(Nz::ResourceLoadingError::DecodingError); } SceneInfo sceneInfo; @@ -846,7 +823,7 @@ std::shared_ptr LoadMesh(Nz::Stream& stream, const Nz::MeshParams& par bool handleSkeletalMeshes = parameters.animated && !sceneInfo.skeletalMeshes.empty(); if (handleSkeletalMeshes && !FindSkeletonRoot(sceneInfo, scene->mRootNode)) - return nullptr; + return Nz::Err(Nz::ResourceLoadingError::DecodingError); std::shared_ptr mesh = std::make_shared(); @@ -897,23 +874,35 @@ namespace Nz::Utility* utility = Nz::Utility::Instance(); NazaraAssert(utility, "utility module is not instancied"); + Nz::AnimationLoader::Entry animationLoaderEntry; + animationLoaderEntry.extensionSupport = IsSupported; + animationLoaderEntry.streamLoader = LoadAnimation; + animationLoaderEntry.parameterFilter = [](const Nz::AnimationParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipAssimpLoader", &skip) && skip) + return false; + + return true; + }; + Nz::AnimationLoader& animationLoader = utility->GetAnimationLoader(); - m_animationLoaderEntry = animationLoader.RegisterLoader({ - IsSupported, - nullptr, - nullptr, - CheckAnimation, - LoadAnimation - }); + m_animationLoaderEntry = animationLoader.RegisterLoader(std::move(animationLoaderEntry)); + + Nz::MeshLoader::Entry meshLoaderEntry; + meshLoaderEntry.extensionSupport = IsSupported; + meshLoaderEntry.streamLoader = LoadMesh; + meshLoaderEntry.parameterFilter = [](const Nz::MeshParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipAssimpLoader", &skip) && skip) + return false; + + return true; + }; Nz::MeshLoader& meshLoader = utility->GetMeshLoader(); - m_meshLoaderEntry = meshLoader.RegisterLoader({ - IsSupported, - nullptr, - nullptr, - CheckMesh, - LoadMesh - }); + m_meshLoaderEntry = meshLoader.RegisterLoader(std::move(meshLoaderEntry)); return true; } diff --git a/plugins/FFmpeg/Plugin.cpp b/plugins/FFmpeg/Plugin.cpp index ba4b28fcd..792523b93 100644 --- a/plugins/FFmpeg/Plugin.cpp +++ b/plugins/FFmpeg/Plugin.cpp @@ -73,7 +73,7 @@ namespace av_free(&m_ioBuffer); } - bool Check() + Nz::Result Check() { constexpr std::size_t BufferSize = 32768; @@ -82,14 +82,14 @@ namespace if (!m_ioContext) { NazaraError("failed to create io context"); - return false; + return Nz::Err(Nz::ResourceLoadingError::Internal); } m_formatContext = avformat_alloc_context(); if (!m_formatContext) { NazaraError("failed to allocate format context"); - return false; + return Nz::Err(Nz::ResourceLoadingError::Internal); } m_formatContext->pb = m_ioContext; @@ -99,36 +99,36 @@ namespace if (int errCode = avformat_open_input(&m_formatContext, "", nullptr, nullptr); errCode != 0) { NazaraError("failed to open input: " + ErrorToString(errCode)); - return false; + return Nz::Err(Nz::ResourceLoadingError::Unrecognized); } if (int errCode = avformat_find_stream_info(m_formatContext, nullptr); errCode != 0) { NazaraError("failed to find stream info: " + ErrorToString(errCode)); - return false; + return Nz::Err(Nz::ResourceLoadingError::Unrecognized); } m_videoStream = av_find_best_stream(m_formatContext, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0); if (m_videoStream < 0) { NazaraError("failed to find video stream"); - return false; + return Nz::Err(Nz::ResourceLoadingError::Unrecognized); } if (m_formatContext->streams[m_videoStream]->nb_frames == 0) { NazaraError("unhandled 0 frame count"); - return false; + return Nz::Err(Nz::ResourceLoadingError::Unsupported); } m_codec = avcodec_find_decoder(m_formatContext->streams[m_videoStream]->codecpar->codec_id); if (!m_codec) { NazaraError("codec not found"); - return false; + return Nz::Err(Nz::ResourceLoadingError::Unsupported); } - return true; + return Nz::Ok(); } bool DecodeNextFrame(void* frameBuffer, Nz::UInt64* frameTime) override @@ -219,13 +219,11 @@ namespace return { width, height }; } - bool Open() + Nz::Result Open() { - if (!Check()) - { - NazaraError("stream has invalid GIF header"); - return false; - } + auto checkResult = Check(); + if (!checkResult) + return checkResult; const AVCodecParameters* codecParameters = m_formatContext->streams[m_videoStream]->codecpar; @@ -233,19 +231,19 @@ namespace if (!m_codecContext) { NazaraError("failed to allocate codec context"); - return false; + return Nz::Err(Nz::ResourceLoadingError::Internal); } if (int errCode = avcodec_parameters_to_context(m_codecContext, codecParameters); errCode < 0) { NazaraError("failed to copy codec params to codec context: " + ErrorToString(errCode)); - return false; + return Nz::Err(Nz::ResourceLoadingError::Internal); } if (int errCode = avcodec_open2(m_codecContext, m_codec, nullptr); errCode < 0) { NazaraError("could not open codec: " + ErrorToString(errCode)); - return false; + return Nz::Err(Nz::ResourceLoadingError::Internal); } m_rawFrame = av_frame_alloc(); @@ -253,7 +251,7 @@ namespace if (!m_rawFrame || !m_rgbaFrame) { NazaraError("failed to allocate frames"); - return false; + return Nz::Err(Nz::ResourceLoadingError::Internal); } m_rgbaFrame->format = AVPixelFormat::AV_PIX_FMT_RGBA; @@ -263,17 +261,17 @@ namespace if (int errCode = av_frame_get_buffer(m_rgbaFrame, 0); errCode < 0) { NazaraError("failed to open input: " + ErrorToString(errCode)); - return false; + return Nz::Err(Nz::ResourceLoadingError::Internal); } m_conversionContext = sws_getContext(m_codecContext->width, m_codecContext->height, m_codecContext->pix_fmt, m_codecContext->width, m_codecContext->height, AVPixelFormat::AV_PIX_FMT_RGBA, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr); if (!m_conversionContext) { NazaraError("failed to allocate conversion context"); - return false; + return Nz::Err(Nz::ResourceLoadingError::Internal); } - return true; + return Nz::Ok(); } void Seek(Nz::UInt64 frameIndex) override @@ -396,56 +394,38 @@ namespace bool CheckVideoExtension(const std::string_view& extension) { - // TODO - return extension == "mp4"; + const AVOutputFormat* format = av_guess_format(nullptr, extension.data(), nullptr); + if (!format) + return false; + + return format->video_codec != AV_CODEC_ID_NONE; } - Nz::Ternary CheckVideo(Nz::Stream& stream, const Nz::ImageStreamParams& parameters) - { - bool skip; - if (parameters.custom.GetBooleanParameter("SkipFFMpegLoader", &skip) && skip) - return Nz::Ternary::False; - - FFmpegStream ffmpegStream; - ffmpegStream.SetStream(stream); - - if (ffmpegStream.Check()) - return Nz::Ternary::True; - else - return Nz::Ternary::False; - } - - std::shared_ptr LoadFile(const std::filesystem::path& filePath, const Nz::ImageStreamParams& /*parameters*/) + Nz::Result, Nz::ResourceLoadingError> LoadFile(const std::filesystem::path& filePath, const Nz::ImageStreamParams& /*parameters*/) { std::shared_ptr ffmpegStream = std::make_shared(); ffmpegStream->SetFile(filePath); - if (!ffmpegStream->Open()) - return {}; - - return ffmpegStream; + Nz::Result status = ffmpegStream->Open(); + return status.Map([&] { return std::move(ffmpegStream); }); } - std::shared_ptr LoadMemory(const void* ptr, std::size_t size, const Nz::ImageStreamParams& /*parameters*/) + Nz::Result, Nz::ResourceLoadingError> LoadMemory(const void* ptr, std::size_t size, const Nz::ImageStreamParams& /*parameters*/) { std::shared_ptr ffmpegStream = std::make_shared(); ffmpegStream->SetMemory(ptr, size); - if (!ffmpegStream->Open()) - return {}; - - return ffmpegStream; + Nz::Result status = ffmpegStream->Open(); + return status.Map([&] { return std::move(ffmpegStream); }); } - std::shared_ptr LoadStream(Nz::Stream& stream, const Nz::ImageStreamParams& /*parameters*/) + Nz::Result, Nz::ResourceLoadingError> LoadStream(Nz::Stream& stream, const Nz::ImageStreamParams& /*parameters*/) { std::shared_ptr ffmpegStream = std::make_shared(); ffmpegStream->SetStream(stream); - if (!ffmpegStream->Open()) - return {}; - - return ffmpegStream; + Nz::Result status = ffmpegStream->Open(); + return status.Map([&] { return std::move(ffmpegStream); }); } class FFmpegPluginImpl final : public Nz::FFmpegPlugin @@ -458,10 +438,17 @@ namespace Nz::ImageStreamLoader::Entry loaderEntry; loaderEntry.extensionSupport = CheckVideoExtension; - loaderEntry.streamChecker = CheckVideo; loaderEntry.fileLoader = LoadFile; loaderEntry.memoryLoader = LoadMemory; loaderEntry.streamLoader = LoadStream; + loaderEntry.parameterFilter = [](const Nz::ImageStreamParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipFFMpegLoader", &skip) && skip) + return false; + + return true; + }; Nz::ImageStreamLoader& imageStreamLoader = utility->GetImageStreamLoader(); m_ffmpegLoaderEntry = imageStreamLoader.RegisterLoader(loaderEntry); @@ -476,6 +463,7 @@ namespace Nz::ImageStreamLoader& imageStreamLoader = utility->GetImageStreamLoader(); imageStreamLoader.UnregisterLoader(m_ffmpegLoaderEntry); + m_ffmpegLoaderEntry = nullptr; } diff --git a/src/Nazara/Audio/Formats/drwavLoader.cpp b/src/Nazara/Audio/Formats/drwavLoader.cpp index 5ca13640a..819ba62c9 100644 --- a/src/Nazara/Audio/Formats/drwavLoader.cpp +++ b/src/Nazara/Audio/Formats/drwavLoader.cpp @@ -51,31 +51,14 @@ namespace Nz bool IsWavSupported(const std::string_view& extension) { - return extension == "riff" || extension == "rf64" || extension == "wav" || extension == "w64"; + return extension == ".riff" || extension == ".rf64" || extension == ".wav" || extension == ".w64"; } - Ternary CheckWav(Stream& stream, const ResourceParameters& parameters) - { - bool skip; - if (parameters.custom.GetBooleanParameter("SkipBuiltinWavLoader", &skip) && skip) - return Ternary::False; - - drwav wav; - if (!drwav_init(&wav, &ReadWavCallback, &SeekWavCallback, &stream, nullptr)) - return Ternary::False; - - drwav_uninit(&wav); - return Ternary::True; - } - - std::shared_ptr LoadWavSoundBuffer(Stream& stream, const SoundBufferParams& parameters) + Result, ResourceLoadingError> LoadWavSoundBuffer(Stream& stream, const SoundBufferParams& parameters) { drwav wav; if (!drwav_init(&wav, &ReadWavCallback, &SeekWavCallback, &stream, nullptr)) - { - NazaraError("failed to decode wav stream"); - return {}; - } + return Err(ResourceLoadingError::Unrecognized); CallOnExit uninitOnExit([&] { drwav_uninit(&wav); }); @@ -83,7 +66,7 @@ namespace Nz if (!formatOpt) { NazaraError("unexpected channel count: " + std::to_string(wav.channels)); - return {}; + return Err(ResourceLoadingError::Unsupported); } AudioFormat format = *formatOpt; @@ -94,7 +77,7 @@ namespace Nz if (drwav_read_pcm_frames_s16(&wav, wav.totalPCMFrameCount, samples.get()) != wav.totalPCMFrameCount) { NazaraError("failed to read stream content"); - return {}; + return Err(ResourceLoadingError::DecodingError); } if (parameters.forceMono && format != AudioFormat::I16_Mono) @@ -150,32 +133,29 @@ namespace Nz return m_sampleRate; } - bool Open(const std::filesystem::path& filePath, bool forceMono) + Result Open(const std::filesystem::path& filePath, const SoundStreamParams& parameters) { std::unique_ptr file = std::make_unique(); if (!file->Open(filePath, OpenMode::ReadOnly)) { NazaraError("failed to open stream from file: " + Error::GetLastError()); - return false; + return Err(ResourceLoadingError::FailedToOpenFile); } m_ownedStream = std::move(file); - return Open(*m_ownedStream, forceMono); + return Open(*m_ownedStream, parameters); } - bool Open(const void* data, std::size_t size, bool forceMono) + Result Open(const void* data, std::size_t size, const SoundStreamParams& parameters) { m_ownedStream = std::make_unique(data, size); - return Open(*m_ownedStream, forceMono); + return Open(*m_ownedStream, parameters); } - bool Open(Stream& stream, bool forceMono) + Result Open(Stream& stream, const SoundStreamParams& parameters) { if (!drwav_init(&m_decoder, &ReadWavCallback, &SeekWavCallback, &stream, nullptr)) - { - NazaraError("failed to decode wav stream"); - return {}; - } + return Err(ResourceLoadingError::Unrecognized); CallOnExit resetOnError([this] { @@ -187,7 +167,7 @@ namespace Nz if (!formatOpt) { NazaraError("unexpected channel count: " + std::to_string(m_decoder.channels)); - return false; + return Err(ResourceLoadingError::Unsupported); } m_format = *formatOpt; @@ -197,7 +177,7 @@ namespace Nz m_sampleRate = m_decoder.sampleRate; // Mixing to mono will be done on the fly - if (forceMono && m_format != AudioFormat::I16_Mono) + if (parameters.forceMono && m_format != AudioFormat::I16_Mono) { m_mixToMono = true; m_sampleCount = m_decoder.totalPCMFrameCount; @@ -207,7 +187,7 @@ namespace Nz resetOnError.Reset(); - return true; + return Ok(); } UInt64 Read(void* buffer, UInt64 sampleCount) override @@ -257,40 +237,28 @@ namespace Nz bool m_mixToMono; }; - std::shared_ptr LoadWavSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters) + Result, ResourceLoadingError> LoadWavSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters) { std::shared_ptr soundStream = std::make_shared(); - if (!soundStream->Open(filePath, parameters.forceMono)) - { - NazaraError("failed to open sound stream"); - return {}; - } + Result status = soundStream->Open(filePath, parameters); - return soundStream; + return status.Map([&] { return std::move(soundStream); }); } - std::shared_ptr LoadWavSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters) + Result, ResourceLoadingError> LoadWavSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters) { std::shared_ptr soundStream = std::make_shared(); - if (!soundStream->Open(data, size, parameters.forceMono)) - { - NazaraError("failed to open music stream"); - return {}; - } + Result status = soundStream->Open(data, size, parameters); - return soundStream; + return status.Map([&] { return std::move(soundStream); }); } - std::shared_ptr LoadWavSoundStreamStream(Stream& stream, const SoundStreamParams& parameters) + Result, ResourceLoadingError> LoadWavSoundStreamStream(Stream& stream, const SoundStreamParams& parameters) { std::shared_ptr soundStream = std::make_shared(); - if (!soundStream->Open(stream, parameters.forceMono)) - { - NazaraError("failed to open music stream"); - return {}; - } + Result status = soundStream->Open(stream, parameters); - return soundStream; + return status.Map([&] { return std::move(soundStream); }); } } @@ -300,8 +268,15 @@ namespace Nz { SoundBufferLoader::Entry loaderEntry; loaderEntry.extensionSupport = IsWavSupported; - loaderEntry.streamChecker = [](Stream& stream, const SoundBufferParams& parameters) { return CheckWav(stream, parameters); }; loaderEntry.streamLoader = LoadWavSoundBuffer; + loaderEntry.parameterFilter = [](const SoundBufferParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipBuiltinWavLoader", &skip) && skip) + return false; + + return true; + }; return loaderEntry; } @@ -310,10 +285,17 @@ namespace Nz { SoundStreamLoader::Entry loaderEntry; loaderEntry.extensionSupport = IsWavSupported; - loaderEntry.streamChecker = [](Stream& stream, const SoundStreamParams& parameters) { return CheckWav(stream, parameters); }; loaderEntry.fileLoader = LoadWavSoundStreamFile; loaderEntry.memoryLoader = LoadWavSoundStreamMemory; loaderEntry.streamLoader = LoadWavSoundStreamStream; + loaderEntry.parameterFilter = [](const SoundStreamParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipBuiltinWavLoader", &skip) && skip) + return false; + + return true; + }; return loaderEntry; } diff --git a/src/Nazara/Audio/Formats/libflacLoader.cpp b/src/Nazara/Audio/Formats/libflacLoader.cpp index e1dc11c3c..fc46c97f9 100644 --- a/src/Nazara/Audio/Formats/libflacLoader.cpp +++ b/src/Nazara/Audio/Formats/libflacLoader.cpp @@ -138,46 +138,10 @@ namespace Nz bool IsFlacSupported(const std::string_view& extension) { - return extension == "flac"; + return extension == ".flac"; } - Ternary CheckFlac(Stream& stream, const ResourceParameters& parameters) - { - bool skip; - if (parameters.custom.GetBooleanParameter("SkipBuiltinFlacLoader", &skip) && skip) - return Ternary::False; - - FLAC__StreamDecoder* decoder = FLAC__stream_decoder_new(); - CallOnExit freeDecoder([&] { FLAC__stream_decoder_delete(decoder); }); - - bool hasError = false; - - FlacUserdata ud; - ud.stream = &stream; - ud.errorCallback = [&](const FLAC__StreamDecoder* /*decoder*/, FLAC__StreamDecoderErrorStatus /*status*/) - { - hasError = true; - }; - - FLAC__StreamDecoderInitStatus status = FLAC__stream_decoder_init_stream(decoder, &FlacReadCallback, &FlacSeekCallback, &FlacTellCallback, &FlacLengthCallback, &FlacEofCallback, &WriteCallback, &MetadataCallback, &ErrorCallback, &ud); - if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK) - { - NazaraWarning(FLAC__StreamDecoderInitStatusString[status]); //< an error shouldn't happen at this state - return Ternary::False; - } - - CallOnExit finishDecoder([&] { FLAC__stream_decoder_finish(decoder); }); - - if (!FLAC__stream_decoder_process_until_end_of_metadata(decoder)) - return Ternary::False; - - if (hasError) - return Ternary::False; - - return Ternary::True; - } - - std::shared_ptr LoadFlacSoundBuffer(Stream& stream, const SoundBufferParams& parameters) + Result, ResourceLoadingError> LoadFlacSoundBuffer(Stream& stream, const SoundBufferParams& parameters) { FLAC__StreamDecoder* decoder = FLAC__stream_decoder_new(); CallOnExit freeDecoder([&] { FLAC__stream_decoder_delete(decoder); }); @@ -236,34 +200,37 @@ namespace Nz if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK) { NazaraWarning(FLAC__StreamDecoderInitStatusString[status]); //< an error shouldn't happen at this state - return {}; + return Err(ResourceLoadingError::Internal); } CallOnExit finishDecoder([&] { FLAC__stream_decoder_finish(decoder); }); + if (!FLAC__stream_decoder_process_until_end_of_metadata(decoder)) + return Err(ResourceLoadingError::Unrecognized); + if (!FLAC__stream_decoder_process_until_end_of_stream(decoder)) { NazaraError("flac decoding failed"); - return {}; + return Err(ResourceLoadingError::DecodingError); } if (hasError) { NazaraError("an error occurred during decoding"); - return {}; + return Err(ResourceLoadingError::DecodingError); } if (channelCount == 0 || frameCount == 0 || sampleCount == 0 || sampleRate == 0) { NazaraError("invalid metadata"); - return {}; + return Err(ResourceLoadingError::DecodingError); } std::optional formatOpt = GuessAudioFormat(channelCount); if (!formatOpt) { NazaraError("unexpected channel count: " + std::to_string(channelCount)); - return {}; + return Err(ResourceLoadingError::Unsupported); } AudioFormat format = *formatOpt; @@ -326,26 +293,26 @@ namespace Nz return m_sampleRate; } - bool Open(const std::filesystem::path& filePath, bool forceMono) + Result Open(const std::filesystem::path& filePath, const SoundStreamParams& parameters) { std::unique_ptr file = std::make_unique(); if (!file->Open(filePath, OpenMode::ReadOnly)) { NazaraError("failed to open stream from file: " + Error::GetLastError()); - return false; + return Err(ResourceLoadingError::FailedToOpenFile); } m_ownedStream = std::move(file); - return Open(*m_ownedStream, forceMono); + return Open(*m_ownedStream, parameters); } - bool Open(const void* data, std::size_t size, bool forceMono) + Result Open(const void* data, std::size_t size, const SoundStreamParams& parameters) { m_ownedStream = std::make_unique(data, size); - return Open(*m_ownedStream, forceMono); + return Open(*m_ownedStream, parameters); } - bool Open(Stream& stream, bool forceMono) + Result Open(Stream& stream, const SoundStreamParams& parameters) { m_userData.stream = &stream; m_userData.errorCallback = [this](const FLAC__StreamDecoder* /*decoder*/, FLAC__StreamDecoderErrorStatus status) @@ -373,28 +340,25 @@ namespace Nz if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK) { NazaraWarning(FLAC__StreamDecoderInitStatusString[status]); //< an error shouldn't happen at this state - return {}; + return Err(ResourceLoadingError::Internal); } CallOnExit finishDecoder([&] { FLAC__stream_decoder_finish(decoder); }); if (!FLAC__stream_decoder_process_until_end_of_metadata(decoder)) - { - NazaraError("failed to decode metadata"); - return false; - } + return Err(ResourceLoadingError::Unrecognized); std::optional formatOpt = GuessAudioFormat(m_channelCount); if (!formatOpt) { NazaraError("unexpected channel count: " + std::to_string(m_channelCount)); - return false; + return Err(ResourceLoadingError::Unrecognized); } m_format = *formatOpt; // Mixing to mono will be done on the fly - if (forceMono && m_format != AudioFormat::I16_Mono) + if (parameters.forceMono && m_format != AudioFormat::I16_Mono) { m_mixToMono = true; m_sampleCount = frameCount; @@ -406,7 +370,7 @@ namespace Nz freeDecoder.Reset(); m_decoder = decoder; - return true; + return Ok(); } UInt64 Read(void* buffer, UInt64 sampleCount) override @@ -522,40 +486,28 @@ namespace Nz bool m_mixToMono; }; - std::shared_ptr LoadFlacSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters) + Result, ResourceLoadingError> LoadFlacSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters) { std::shared_ptr soundStream = std::make_shared(); - if (!soundStream->Open(filePath, parameters.forceMono)) - { - NazaraError("failed to open sound stream"); - return {}; - } + Result status = soundStream->Open(filePath, parameters); - return soundStream; + return status.Map([&] { return std::move(soundStream); }); } - std::shared_ptr LoadFlacSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters) + Result, ResourceLoadingError> LoadFlacSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters) { std::shared_ptr soundStream = std::make_shared(); - if (!soundStream->Open(data, size, parameters.forceMono)) - { - NazaraError("failed to open music stream"); - return {}; - } + Result status = soundStream->Open(data, size, parameters); - return soundStream; + return status.Map([&] { return std::move(soundStream); }); } - std::shared_ptr LoadFlacSoundStreamStream(Stream& stream, const SoundStreamParams& parameters) + Result, ResourceLoadingError> LoadFlacSoundStreamStream(Stream& stream, const SoundStreamParams& parameters) { std::shared_ptr soundStream = std::make_shared(); - if (!soundStream->Open(stream, parameters.forceMono)) - { - NazaraError("failed to open music stream"); - return {}; - } + Result status = soundStream->Open(stream, parameters); - return soundStream; + return status.Map([&] { return std::move(soundStream); }); } } @@ -565,8 +517,15 @@ namespace Nz { SoundBufferLoader::Entry loaderEntry; loaderEntry.extensionSupport = IsFlacSupported; - loaderEntry.streamChecker = [](Stream& stream, const SoundBufferParams& parameters) { return CheckFlac(stream, parameters); }; - loaderEntry.streamLoader = LoadFlacSoundBuffer; + loaderEntry.streamLoader = LoadFlacSoundBuffer; + loaderEntry.parameterFilter = [](const SoundBufferParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipBuiltinFlacLoader", &skip) && skip) + return false; + + return true; + }; return loaderEntry; } @@ -575,10 +534,17 @@ namespace Nz { SoundStreamLoader::Entry loaderEntry; loaderEntry.extensionSupport = IsFlacSupported; - loaderEntry.streamChecker = [](Stream& stream, const SoundStreamParams& parameters) { return CheckFlac(stream, parameters); }; - loaderEntry.fileLoader = LoadFlacSoundStreamFile; - loaderEntry.memoryLoader = LoadFlacSoundStreamMemory; - loaderEntry.streamLoader = LoadFlacSoundStreamStream; + loaderEntry.fileLoader = LoadFlacSoundStreamFile; + loaderEntry.memoryLoader = LoadFlacSoundStreamMemory; + loaderEntry.streamLoader = LoadFlacSoundStreamStream; + loaderEntry.parameterFilter = [](const SoundStreamParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipBuiltinFlacLoader", &skip) && skip) + return false; + + return true; + }; return loaderEntry; } diff --git a/src/Nazara/Audio/Formats/libvorbisLoader.cpp b/src/Nazara/Audio/Formats/libvorbisLoader.cpp index 3b6ba7841..7bc1eb9c7 100644 --- a/src/Nazara/Audio/Formats/libvorbisLoader.cpp +++ b/src/Nazara/Audio/Formats/libvorbisLoader.cpp @@ -14,8 +14,9 @@ #include #include #include +#include +#include #include -#include #define OV_EXCLUDE_STATIC_CALLBACKS #include @@ -75,16 +76,16 @@ namespace Nz { switch (errCode) { - case 0: return "no error"; + case 0: return "no error"; case OV_EBADHEADER: return "invalid Vorbis bitstream header"; - case OV_EBADLINK: return "an invalid stream section was supplied to libvorbisfile, or the requested link is corrupt"; - case OV_EFAULT: return "internal logic fault"; - case OV_EINVAL: return "an invalid stream section was supplied to libvorbisfile, or the requested link is corrupt"; + case OV_EBADLINK: return "an invalid stream section was supplied to libvorbisfile, or the requested link is corrupt"; + case OV_EFAULT: return "internal logic fault"; + case OV_EINVAL: return "an invalid stream section was supplied to libvorbisfile, or the requested link is corrupt"; case OV_ENOTVORBIS: return "bitstream does not contain any Vorbis data"; - case OV_EREAD: return "a read from media returned an error"; - case OV_EVERSION: return "Vorbis version mismatch"; - case OV_HOLE: return "there was an interruption in the data"; - default: return "unknown error"; + case OV_EREAD: return "a read from media returned an error"; + case OV_EVERSION: return "Vorbis version mismatch"; + case OV_HOLE: return "there was an interruption in the data"; + default: return "unknown error"; } } @@ -116,38 +117,19 @@ namespace Nz return sampleCount - remainingBytes / sizeof(Int16); } - bool IsVorbisSupported(const std::string_view& extension) - { - static std::set supportedExtensions = { - "oga", "ogg", "ogm", "ogv", "ogx", "opus", "spx" - }; + constexpr auto s_supportedExtensions = frozen::make_unordered_set({ ".oga", ".ogg", ".ogm", ".ogv", ".ogx", ".opus", ".spx" }); - return supportedExtensions.find(extension) != supportedExtensions.end(); + bool IsVorbisSupported(std::string_view extension) + { + return s_supportedExtensions.find(extension) != s_supportedExtensions.end(); } - Ternary CheckOgg(Stream& stream, const ResourceParameters& parameters) - { - bool skip; - if (parameters.custom.GetBooleanParameter("SkipBuiltinVorbisLoader", &skip) && skip) - return Ternary::False; - - OggVorbis_File file; - if (ov_test_callbacks(&stream, &file, nullptr, 0, s_vorbisCallbacks) != 0) - return Ternary::False; - - ov_clear(&file); - return Ternary::True; - } - - std::shared_ptr LoadVorbisSoundBuffer(Stream& stream, const SoundBufferParams& parameters) + Result, ResourceLoadingError> LoadVorbisSoundBuffer(Stream& stream, const SoundBufferParams& parameters) { OggVorbis_File file; int err = ov_open_callbacks(&stream, &file, nullptr, 0, s_vorbisCallbacks); if (err != 0) - { - NazaraError(VorbisErrToString(err)); - return {}; - } + return Err(ResourceLoadingError::Unrecognized); CallOnExit clearOnExit([&] { ov_clear(&file); }); @@ -158,7 +140,7 @@ namespace Nz if (!formatOpt) { NazaraError("unexpected channel count: " + std::to_string(info->channels)); - return {}; + return Err(ResourceLoadingError::Unsupported); } AudioFormat format = *formatOpt; @@ -169,12 +151,12 @@ namespace Nz UInt64 readSample = ReadOgg(&file, samples.get(), sampleCount); if (readSample == 0) - return {}; + return Err(ResourceLoadingError::DecodingError); if (readSample != sampleCount) { NazaraError("failed to read the whole file"); - return {}; + return Err(ResourceLoadingError::DecodingError); } if (parameters.forceMono && format != AudioFormat::I16_Mono) @@ -230,33 +212,30 @@ namespace Nz return m_sampleRate; } - bool Open(const std::filesystem::path& filePath, bool forceMono) + Result Open(const std::filesystem::path& filePath, const SoundStreamParams& parameters) { std::unique_ptr file = std::make_unique(); if (!file->Open(filePath, OpenMode::ReadOnly)) { NazaraError("failed to open stream from file: " + Error::GetLastError()); - return false; + return Err(ResourceLoadingError::FailedToOpenFile); } m_ownedStream = std::move(file); - return Open(*m_ownedStream, forceMono); + return Open(*m_ownedStream, parameters); } - bool Open(const void* data, std::size_t size, bool forceMono) + Result Open(const void* data, std::size_t size, const SoundStreamParams& parameters) { m_ownedStream = std::make_unique(data, size); - return Open(*m_ownedStream, forceMono); + return Open(*m_ownedStream, parameters); } - bool Open(Stream& stream, bool forceMono) + Result Open(Stream& stream, const SoundStreamParams& parameters) { int err = ov_open_callbacks(&stream, &m_decoder, nullptr, 0, s_vorbisCallbacks); if (err != 0) - { - NazaraError(VorbisErrToString(err)); - return {}; - } + return Err(ResourceLoadingError::Unrecognized); CallOnExit clearOnError([&] { @@ -271,7 +250,7 @@ namespace Nz if (!formatOpt) { NazaraError("unexpected channel count: " + std::to_string(info->channels)); - return {}; + return Err(ResourceLoadingError::Unsupported); } m_format = *formatOpt; @@ -284,7 +263,7 @@ namespace Nz m_sampleRate = info->rate; // Mixing to mono will be done on the fly - if (forceMono && m_format != AudioFormat::I16_Mono) + if (parameters.forceMono && m_format != AudioFormat::I16_Mono) { m_mixToMono = true; m_sampleCount = frameCount; @@ -294,7 +273,7 @@ namespace Nz clearOnError.Reset(); - return true; + return Ok(); } UInt64 Read(void* buffer, UInt64 sampleCount) override @@ -347,40 +326,28 @@ namespace Nz bool m_mixToMono; }; - std::shared_ptr LoadVorbisSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters) + Result, ResourceLoadingError> LoadVorbisSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters) { std::shared_ptr soundStream = std::make_shared(); - if (!soundStream->Open(filePath, parameters.forceMono)) - { - NazaraError("failed to open sound stream"); - return {}; - } + Result status = soundStream->Open(filePath, parameters); - return soundStream; + return status.Map([&] { return std::move(soundStream); }); } - std::shared_ptr LoadVorbisSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters) + Result, ResourceLoadingError> LoadVorbisSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters) { std::shared_ptr soundStream = std::make_shared(); - if (!soundStream->Open(data, size, parameters.forceMono)) - { - NazaraError("failed to open music stream"); - return {}; - } + Result status = soundStream->Open(data, size, parameters); - return soundStream; + return status.Map([&] { return std::move(soundStream); }); } - std::shared_ptr LoadVorbisSoundStreamStream(Stream& stream, const SoundStreamParams& parameters) + Result, ResourceLoadingError> LoadVorbisSoundStreamStream(Stream& stream, const SoundStreamParams& parameters) { std::shared_ptr soundStream = std::make_shared(); - if (!soundStream->Open(stream, parameters.forceMono)) - { - NazaraError("failed to open music stream"); - return {}; - } + Result status = soundStream->Open(stream, parameters); - return soundStream; + return status.Map([&] { return std::move(soundStream); }); } } @@ -390,8 +357,15 @@ namespace Nz { SoundBufferLoader::Entry loaderEntry; loaderEntry.extensionSupport = IsVorbisSupported; - loaderEntry.streamChecker = [](Stream& stream, const SoundBufferParams& parameters) { return CheckOgg(stream, parameters); }; loaderEntry.streamLoader = LoadVorbisSoundBuffer; + loaderEntry.parameterFilter = [](const SoundBufferParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipBuiltinVorbisLoader", &skip) && skip) + return false; + + return true; + }; return loaderEntry; } @@ -400,10 +374,17 @@ namespace Nz { SoundStreamLoader::Entry loaderEntry; loaderEntry.extensionSupport = IsVorbisSupported; - loaderEntry.streamChecker = [](Stream& stream, const SoundStreamParams& parameters) { return CheckOgg(stream, parameters); }; loaderEntry.fileLoader = LoadVorbisSoundStreamFile; loaderEntry.memoryLoader = LoadVorbisSoundStreamMemory; loaderEntry.streamLoader = LoadVorbisSoundStreamStream; + loaderEntry.parameterFilter = [](const SoundStreamParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipBuiltinVorbisLoader", &skip) && skip) + return false; + + return true; + }; return loaderEntry; } diff --git a/src/Nazara/Audio/Formats/minimp3Loader.cpp b/src/Nazara/Audio/Formats/minimp3Loader.cpp index 222877584..2cfa4d724 100644 --- a/src/Nazara/Audio/Formats/minimp3Loader.cpp +++ b/src/Nazara/Audio/Formats/minimp3Loader.cpp @@ -53,26 +53,10 @@ namespace Nz bool IsMP3Supported(const std::string_view& extension) { - return extension == "mp3"; + return extension == ".mp3"; } - Ternary CheckMP3(Stream& stream, const ResourceParameters& parameters) - { - bool skip; - if (parameters.custom.GetBooleanParameter("SkipBuiltinMP3Loader", &skip) && skip) - return Ternary::False; - - mp3dec_io_t io; - io.read = &MP3ReadCallback; - io.read_data = &stream; - io.seek = &MP3SeekCallback; - io.seek_data = &stream; - - std::vector buffer(MINIMP3_BUF_SIZE); - return (mp3dec_detect_cb(&io, buffer.data(), buffer.size()) == 0) ? Ternary::True : Ternary::False; - } - - std::shared_ptr LoadMP3SoundBuffer(Stream& stream, const SoundBufferParams& parameters) + Result, ResourceLoadingError> LoadMP3SoundBuffer(Stream& stream, const SoundBufferParams& parameters) { static_assert(std::is_same_v); @@ -91,12 +75,20 @@ namespace Nz mp3dec_t dec; mp3dec_file_info_t info; - std::vector buffer(MINIMP3_BUF_SIZE); - int err = mp3dec_load_cb(&dec, &io, buffer.data(), buffer.size(), &info, nullptr, &userdata); + + Nz::UInt64 cursorPos = stream.GetCursorPos(); + + std::unique_ptr buffer = std::make_unique(MINIMP3_BUF_SIZE); + if (mp3dec_detect_cb(&io, buffer.get(), MINIMP3_BUF_SIZE) != 0) + return Err(ResourceLoadingError::Unrecognized); + + stream.SetCursorPos(cursorPos); + + int err = mp3dec_load_cb(&dec, &io, buffer.get(), MINIMP3_BUF_SIZE, &info, nullptr, &userdata); if (err != 0) { NazaraError(MP3ErrorToString(err)); - return {}; + return Err(ResourceLoadingError::DecodingError); } CallOnExit freeBuffer([&] { std::free(info.buffer); }); @@ -105,7 +97,7 @@ namespace Nz if (!formatOpt) { NazaraError("unexpected channel count: " + std::to_string(info.channels)); - return {}; + return Err(ResourceLoadingError::Unsupported); } AudioFormat format = *formatOpt; @@ -166,37 +158,45 @@ namespace Nz return m_sampleRate; } - bool Open(const std::filesystem::path& filePath, bool forceMono) + Result Open(const std::filesystem::path& filePath, const SoundStreamParams& parameters) { std::unique_ptr file = std::make_unique(); if (!file->Open(filePath, OpenMode::ReadOnly)) { NazaraError("failed to open stream from file: " + Error::GetLastError()); - return false; + return Err(ResourceLoadingError::FailedToOpenFile); } m_ownedStream = std::move(file); - return Open(*m_ownedStream, forceMono); + return Open(*m_ownedStream, parameters); } - bool Open(const void* data, std::size_t size, bool forceMono) + Result Open(const void* data, std::size_t size, const SoundStreamParams& parameters) { m_ownedStream = std::make_unique(data, size); - return Open(*m_ownedStream, forceMono); + return Open(*m_ownedStream, parameters); } - bool Open(Stream& stream, bool forceMono) + Result Open(Stream& stream, const SoundStreamParams& parameters) { m_io.read = &MP3ReadCallback; m_io.read_data = &stream; m_io.seek = &MP3SeekCallback; m_io.seek_data = &stream; + Nz::UInt64 cursorPos = stream.GetCursorPos(); + + std::unique_ptr buffer = std::make_unique(MINIMP3_BUF_SIZE); + if (mp3dec_detect_cb(&m_io, buffer.get(), MINIMP3_BUF_SIZE) != 0) + return Err(ResourceLoadingError::Unrecognized); + + stream.SetCursorPos(cursorPos); + int err = mp3dec_ex_open_cb(&m_decoder, &m_io, MP3D_SEEK_TO_SAMPLE); if (err != 0) { NazaraError(MP3ErrorToString(err)); - return {}; + return Err(ResourceLoadingError::DecodingError); } CallOnExit resetOnError([this] @@ -209,7 +209,7 @@ namespace Nz if (!formatOpt) { NazaraError("unexpected channel count: " + std::to_string(m_decoder.info.channels)); - return false; + return Err(ResourceLoadingError::Unsupported); } m_format = *formatOpt; @@ -219,7 +219,7 @@ namespace Nz m_sampleRate = m_decoder.info.hz; // Mixing to mono will be done on the fly - if (forceMono && m_format != AudioFormat::I16_Mono) + if (parameters.forceMono && m_format != AudioFormat::I16_Mono) { m_mixToMono = true; m_sampleCount = static_cast(m_decoder.samples / m_decoder.info.channels); @@ -229,7 +229,7 @@ namespace Nz resetOnError.Reset(); - return true; + return Ok(); } UInt64 Read(void* buffer, UInt64 sampleCount) override @@ -282,40 +282,28 @@ namespace Nz bool m_mixToMono; }; - std::shared_ptr MP3LoadSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters) + Result, ResourceLoadingError> LoadMP3SoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters) { std::shared_ptr soundStream = std::make_shared(); - if (!soundStream->Open(filePath, parameters.forceMono)) - { - NazaraError("failed to open sound stream"); - return {}; - } + Result status = soundStream->Open(filePath, parameters); - return soundStream; + return status.Map([&] { return std::move(soundStream); }); } - std::shared_ptr MP3LoadSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters) + Result, ResourceLoadingError> LoadMP3SoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters) { std::shared_ptr soundStream = std::make_shared(); - if (!soundStream->Open(data, size, parameters.forceMono)) - { - NazaraError("failed to open music stream"); - return {}; - } + Result status = soundStream->Open(data, size, parameters); - return soundStream; + return status.Map([&] { return std::move(soundStream); }); } - std::shared_ptr MP3LoadSoundStreamStream(Stream& stream, const SoundStreamParams& parameters) + Result, ResourceLoadingError> LoadMP3SoundStreamStream(Stream& stream, const SoundStreamParams& parameters) { std::shared_ptr soundStream = std::make_shared(); - if (!soundStream->Open(stream, parameters.forceMono)) - { - NazaraError("failed to open music stream"); - return {}; - } + Result status = soundStream->Open(stream, parameters); - return soundStream; + return status.Map([&] { return std::move(soundStream); }); } } @@ -325,8 +313,15 @@ namespace Nz { SoundBufferLoader::Entry loaderEntry; loaderEntry.extensionSupport = IsMP3Supported; - loaderEntry.streamChecker = [](Stream& stream, const SoundBufferParams& parameters) { return CheckMP3(stream, parameters); }; loaderEntry.streamLoader = LoadMP3SoundBuffer; + loaderEntry.parameterFilter = [](const SoundBufferParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipBuiltinMP3Loader", &skip) && skip) + return false; + + return true; + }; return loaderEntry; } @@ -335,10 +330,17 @@ namespace Nz { SoundStreamLoader::Entry loaderEntry; loaderEntry.extensionSupport = IsMP3Supported; - loaderEntry.streamChecker = [](Stream& stream, const SoundStreamParams& parameters) { return CheckMP3(stream, parameters); }; - loaderEntry.fileLoader = MP3LoadSoundStreamFile; - loaderEntry.memoryLoader = MP3LoadSoundStreamMemory; - loaderEntry.streamLoader = MP3LoadSoundStreamStream; + loaderEntry.fileLoader = LoadMP3SoundStreamFile; + loaderEntry.memoryLoader = LoadMP3SoundStreamMemory; + loaderEntry.streamLoader = LoadMP3SoundStreamStream; + loaderEntry.parameterFilter = [](const SoundStreamParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipBuiltinMP3Loader", &skip) && skip) + return false; + + return true; + }; return loaderEntry; } diff --git a/src/Nazara/Utility/Formats/DDSLoader.cpp b/src/Nazara/Utility/Formats/DDSLoader.cpp index b7a5bde31..f5f1ad361 100644 --- a/src/Nazara/Utility/Formats/DDSLoader.cpp +++ b/src/Nazara/Utility/Formats/DDSLoader.cpp @@ -20,34 +20,18 @@ namespace Nz static bool IsSupported(const std::string_view& extension) { - return (extension == "dds"); + return (extension == ".dds"); } - static Ternary Check(Stream& stream, const ImageParams& parameters) + static Result, ResourceLoadingError> Load(Stream& stream, const ImageParams& parameters) { - bool skip; - if (parameters.custom.GetBooleanParameter("SkipBuiltinDDSLoader", &skip) && skip) - return Ternary::False; - ByteStream byteStream(&stream); byteStream.SetDataEndianness(Endianness::LittleEndian); UInt32 magic; byteStream >> magic; - - return (magic == DDS_Magic) ? Ternary::True : Ternary::False; - } - - static std::shared_ptr Load(Stream& stream, const ImageParams& parameters) - { - NazaraUnused(parameters); - - ByteStream byteStream(&stream); - byteStream.SetDataEndianness(Endianness::LittleEndian); - - UInt32 magic; - byteStream >> magic; - NazaraAssert(magic == DDS_Magic, "Invalid DDS file"); // The Check function should make sure this doesn't happen + if (magic != DDS_Magic) + return Nz::Err(ResourceLoadingError::Unrecognized); DDSHeader header; byteStream >> header; @@ -81,12 +65,12 @@ namespace Nz // First, identify the type ImageType type; if (!IdentifyImageType(header, headerDX10, &type)) - return nullptr; + return Nz::Err(ResourceLoadingError::Unsupported); // Then the format PixelFormat format; if (!IdentifyPixelFormat(header, headerDX10, &format)) - return nullptr; + return Nz::Err(ResourceLoadingError::Unsupported); std::shared_ptr image = std::make_shared(type, format, width, height, depth, levelCount); @@ -100,7 +84,7 @@ namespace Nz if (byteStream.Read(ptr, byteCount) != byteCount) { NazaraError("Failed to read level #" + NumberToString(i)); - return nullptr; + return Nz::Err(ResourceLoadingError::DecodingError); } if (width > 1) @@ -271,8 +255,15 @@ namespace Nz { ImageLoader::Entry loaderEntry; loaderEntry.extensionSupport = DDSLoader::IsSupported; - loaderEntry.streamChecker = DDSLoader::Check; loaderEntry.streamLoader = DDSLoader::Load; + loaderEntry.parameterFilter = [](const ImageParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipBuiltinDDSLoader", &skip) && skip) + return false; + + return true; + }; return loaderEntry; } diff --git a/src/Nazara/Utility/Formats/FreeTypeLoader.cpp b/src/Nazara/Utility/Formats/FreeTypeLoader.cpp index 4afd72a10..472b21f77 100644 --- a/src/Nazara/Utility/Formats/FreeTypeLoader.cpp +++ b/src/Nazara/Utility/Formats/FreeTypeLoader.cpp @@ -15,8 +15,9 @@ #include #include #include +#include +#include #include -#include #include #include @@ -108,12 +109,6 @@ namespace Nz FT_Done_Face(m_face); } - bool Check() - { - // Test d'ouverture (http://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Open_Face) - return FT_Open_Face(s_freetypeLibrary, &m_args, -1, nullptr) == 0; - } - bool ExtractGlyph(unsigned int characterSize, char32_t character, TextStyleFlags style, float outlineThickness, FontGlyph* dst) override { #ifdef NAZARA_DEBUG @@ -383,32 +378,14 @@ namespace Nz mutable unsigned int m_characterSize; }; + constexpr auto s_supportedExtensions = frozen::make_unordered_set({ ".afm", ".bdf", ".cff", ".cid", ".dfont", ".fnt", ".fon", ".otf", ".pfa", ".pfb", ".pfm", ".pfr", ".sfnt", ".ttc", ".tte", ".ttf" }); + bool IsFreetypeSupported(const std::string_view& extension) { - ///FIXME: Je suppose qu'il en manque quelques unes.. - static std::set supportedExtensions = { - "afm", "bdf", "cff", "cid", "dfont", "fnt", "fon", "otf", "pfa", "pfb", "pfm", "pfr", "sfnt", "ttc", "tte", "ttf" - }; - - return supportedExtensions.find(extension) != supportedExtensions.end(); + return s_supportedExtensions.find(extension) != s_supportedExtensions.end(); } - Ternary CheckFreetype(Stream& stream, const FontParams& parameters) - { - bool skip; - if (parameters.custom.GetBooleanParameter("SkipBuiltinFreeTypeLoader", &skip) && skip) - return Ternary::False; - - FreeTypeStream face; - face.SetStream(stream); - - if (face.Check()) - return Ternary::True; - else - return Ternary::False; - } - - std::shared_ptr LoadFreetypeFile(const std::filesystem::path& filePath, const FontParams& parameters) + Result, ResourceLoadingError> LoadFreetypeFile(const std::filesystem::path& filePath, const FontParams& parameters) { NazaraUnused(parameters); @@ -416,23 +393,20 @@ namespace Nz if (!face->SetFile(filePath)) { NazaraError("Failed to open file"); - return nullptr; + return Err(ResourceLoadingError::FailedToOpenFile); } if (!face->Open()) - { - NazaraError("Failed to open face"); - return nullptr; - } + return Err(ResourceLoadingError::Unrecognized); std::shared_ptr font = std::make_shared(); if (!font->Create(std::move(face))) - return nullptr; + return Err(ResourceLoadingError::Internal); return font; } - std::shared_ptr LoadFreetypeMemory(const void* data, std::size_t size, const FontParams& parameters) + Result, ResourceLoadingError> LoadFreetypeMemory(const void* data, std::size_t size, const FontParams& parameters) { NazaraUnused(parameters); @@ -440,34 +414,28 @@ namespace Nz face->SetMemory(data, size); if (!face->Open()) - { - NazaraError("Failed to open face"); - return nullptr; - } + return Err(ResourceLoadingError::Unrecognized); std::shared_ptr font = std::make_shared(); if (!font->Create(std::move(face))) - return nullptr; + return Err(ResourceLoadingError::Internal); return font; } - std::shared_ptr LoadFreetypeStream(Stream& stream, const FontParams& parameters) + Result, ResourceLoadingError> LoadFreetypeStream(Stream& stream, const FontParams& parameters) { NazaraUnused(parameters); - std::unique_ptr face(new FreeTypeStream); + std::unique_ptr face = std::make_unique(); face->SetStream(stream); if (!face->Open()) - { - NazaraError("Failed to open face"); - return nullptr; - } + return Err(ResourceLoadingError::Unrecognized); std::shared_ptr font = std::make_shared(); if (!font->Create(std::move(face))) - return nullptr; + return Err(ResourceLoadingError::Internal); return font; } @@ -492,14 +460,21 @@ namespace Nz { NazaraAssert(s_freetypeLibraryOwner, "FreeType has not been initialized"); - FontLoader::Entry entry; - entry.extensionSupport = IsFreetypeSupported; - entry.fileLoader = LoadFreetypeFile; - entry.memoryLoader = LoadFreetypeMemory; - entry.streamChecker = CheckFreetype; - entry.streamLoader = LoadFreetypeStream; + FontLoader::Entry loader; + loader.extensionSupport = IsFreetypeSupported; + loader.fileLoader = LoadFreetypeFile; + loader.memoryLoader = LoadFreetypeMemory; + loader.streamLoader = LoadFreetypeStream; + loader.parameterFilter = [](const FontParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipBuiltinFreeTypeLoader", &skip) && skip) + return false; - return entry; + return true; + }; + + return loader; } void UninitializeFreeType() diff --git a/src/Nazara/Utility/Formats/GIFLoader.cpp b/src/Nazara/Utility/Formats/GIFLoader.cpp index bf8680758..2cecd1e60 100644 --- a/src/Nazara/Utility/Formats/GIFLoader.cpp +++ b/src/Nazara/Utility/Formats/GIFLoader.cpp @@ -262,13 +262,10 @@ namespace Nz return m_currentFrame; } - bool Open() + Result Open() { if (!Check()) - { - NazaraError("stream has invalid GIF header"); - return false; - } + return Err(ResourceLoadingError::Unrecognized); m_byteStream >> m_header.width >> m_header.height; m_byteStream >> m_header.flags >> m_header.backgroundPaletteIndex >> m_header.ratio; @@ -322,13 +319,13 @@ namespace Nz if (left + width > m_header.width) { NazaraError("corrupt gif (out of range)"); - return false; + return Err(ResourceLoadingError::DecodingError); } if (top + height > m_header.height) { NazaraError("corrupt gif (out of range)"); - return false; + return Err(ResourceLoadingError::DecodingError); } if (left != 0 || top != 0 || width < m_header.width || height < m_header.height) @@ -343,7 +340,7 @@ namespace Nz else if (!hasGlobalColorTable) { NazaraError("corrupt gif (no color table for image #" + std::to_string(m_frames.size() - 1) + ")"); - return false; + return Err(ResourceLoadingError::DecodingError); } UInt8 minimumCodeSize; @@ -351,7 +348,7 @@ namespace Nz if (minimumCodeSize > 12) { NazaraError("unexpected LZW Minimum Code Size (" + std::to_string(minimumCodeSize) + ")"); - return false; + return Err(ResourceLoadingError::DecodingError); } SkipUntilTerminationBlock(); @@ -382,7 +379,7 @@ namespace Nz if (blockSize != 4) { NazaraError("corrupt gif (invalid block size for graphic control extension)"); - return false; + return Err(ResourceLoadingError::DecodingError); } nextFrame.disposalMethod = (flags & 0b0001'1100) >> 2; @@ -423,7 +420,7 @@ namespace Nz default: NazaraError("corrupt gif (unknown tag 0x" + NumberToString(tag, 16) + ")"); - return false; + return Err(ResourceLoadingError::DecodingError); } } @@ -445,7 +442,7 @@ namespace Nz m_currentFrame = 0; - return true; + return Ok(); } bool SetFile(const std::filesystem::path& filePath) @@ -709,55 +706,35 @@ namespace Nz bool CheckGIFExtension(const std::string_view& extension) { - return extension == "gif"; + return extension == ".gif"; } - Ternary CheckGIF(Stream& stream, const ImageStreamParams& parameters) - { - bool skip; - if (parameters.custom.GetBooleanParameter("SkipBuiltinGIFLoader", &skip) && skip) - return Ternary::False; - - GIFImageStream gif; - gif.SetStream(stream); - - if (gif.Check()) - return Ternary::True; - else - return Ternary::False; - } - - std::shared_ptr LoadGIFFile(const std::filesystem::path& filePath, const ImageStreamParams& /*parameters*/) + Result, ResourceLoadingError> LoadGIFFile(const std::filesystem::path& filePath, const ImageStreamParams& /*parameters*/) { std::shared_ptr gifStream = std::make_shared(); - gifStream->SetFile(filePath); + if (!gifStream->SetFile(filePath)) + return Err(ResourceLoadingError::FailedToOpenFile); - if (!gifStream->Open()) - return {}; - - return gifStream; + Result status = gifStream->Open(); + return status.Map([&] { return std::move(gifStream); }); } - std::shared_ptr LoadGIFMemory(const void* ptr, std::size_t size, const ImageStreamParams& /*parameters*/) + Result, ResourceLoadingError> LoadGIFMemory(const void* ptr, std::size_t size, const ImageStreamParams& /*parameters*/) { std::shared_ptr gifStream = std::make_shared(); gifStream->SetMemory(ptr, size); - if (!gifStream->Open()) - return {}; - - return gifStream; + Result status = gifStream->Open(); + return status.Map([&] { return std::move(gifStream); }); } - std::shared_ptr LoadGIFStream(Stream& stream, const ImageStreamParams& /*parameters*/) + Result, ResourceLoadingError> LoadGIFStream(Stream& stream, const ImageStreamParams& /*parameters*/) { std::shared_ptr gifStream = std::make_shared(); gifStream->SetStream(stream); - if (!gifStream->Open()) - return {}; - - return gifStream; + Result status = gifStream->Open(); + return status.Map([&] { return std::move(gifStream); }); } } @@ -767,10 +744,17 @@ namespace Nz { ImageStreamLoader::Entry loaderEntry; loaderEntry.extensionSupport = CheckGIFExtension; - loaderEntry.streamChecker = CheckGIF; loaderEntry.fileLoader = LoadGIFFile; loaderEntry.memoryLoader = LoadGIFMemory; loaderEntry.streamLoader = LoadGIFStream; + loaderEntry.parameterFilter = [](const ImageStreamParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipBuiltinGIFLoader", &skip) && skip) + return false; + + return true; + }; return loaderEntry; } diff --git a/src/Nazara/Utility/Formats/MD2Loader.cpp b/src/Nazara/Utility/Formats/MD2Loader.cpp index 6fc5c87b8..47260e4bd 100644 --- a/src/Nazara/Utility/Formats/MD2Loader.cpp +++ b/src/Nazara/Utility/Formats/MD2Loader.cpp @@ -23,38 +23,25 @@ namespace Nz { bool IsMD2Supported(const std::string_view& extension) { - return (extension == "md2"); + return (extension == ".md2"); } - Ternary CheckMD2(Stream& stream, const MeshParams& parameters) - { - bool skip; - if (parameters.custom.GetBooleanParameter("SkipBuiltinMD2Loader", &skip) && skip) - return Ternary::False; - - UInt32 magic[2]; - if (stream.Read(&magic[0], 2*sizeof(UInt32)) == 2*sizeof(UInt32)) - { - #ifdef NAZARA_BIG_ENDIAN - SwapBytes(&magic[0], sizeof(UInt32)); - SwapBytes(&magic[1], sizeof(UInt32)); - #endif - - if (magic[0] == md2Ident && magic[1] == 8) - return Ternary::True; - } - - return Ternary::False; - } - - std::shared_ptr LoadMD2(Stream& stream, const MeshParams& parameters) + Result, ResourceLoadingError> LoadMD2(Stream& stream, const MeshParams& parameters) { MD2_Header header; if (stream.Read(&header, sizeof(MD2_Header)) != sizeof(MD2_Header)) - { - NazaraError("Failed to read header"); - return nullptr; - } + return Err(ResourceLoadingError::Unrecognized); + + #ifdef NAZARA_BIG_ENDIAN + SwapBytes(&header.indent, sizeof(UInt32)); + SwapBytes(&header.version, sizeof(UInt32)); + #endif + + if (header.ident != md2Ident) + return Err(ResourceLoadingError::Unrecognized); + + if (header.version != 8) + return Err(ResourceLoadingError::Unsupported); #ifdef NAZARA_BIG_ENDIAN SwapBytes(&header.skinwidth, sizeof(UInt32)); @@ -77,7 +64,7 @@ namespace Nz if (stream.GetSize() < header.offset_end) { NazaraError("Incomplete MD2 file"); - return nullptr; + return Err(ResourceLoadingError::DecodingError); } // Since the engine no longer supports keyframe animations, let's make a static mesh @@ -85,7 +72,7 @@ namespace Nz if (!mesh->CreateStatic()) { NazaraInternalError("Failed to create mesh"); - return nullptr; + return Err(ResourceLoadingError::Internal); } // Extract skins (texture name) @@ -269,8 +256,15 @@ namespace Nz { MeshLoader::Entry loader; loader.extensionSupport = IsMD2Supported; - loader.streamChecker = CheckMD2; loader.streamLoader = LoadMD2; + loader.parameterFilter = [](const MeshParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipBuiltinMD2Loader", &skip) && skip) + return false; + + return true; + }; return loader; } diff --git a/src/Nazara/Utility/Formats/MD5AnimLoader.cpp b/src/Nazara/Utility/Formats/MD5AnimLoader.cpp index d8658d137..46f1fc40e 100644 --- a/src/Nazara/Utility/Formats/MD5AnimLoader.cpp +++ b/src/Nazara/Utility/Formats/MD5AnimLoader.cpp @@ -14,28 +14,26 @@ namespace Nz { bool IsMD5AnimSupported(const std::string_view& extension) { - return (extension == "md5anim"); + return extension == ".md5anim"; } - Ternary CheckMD5Anim(Stream& stream, const AnimationParams& parameters) + Result, ResourceLoadingError> LoadMD5Anim(Stream& stream, const AnimationParams& /*parameters*/) { - bool skip; - if (parameters.custom.GetBooleanParameter("SkipBuiltinMD5AnimLoader", &skip) && skip) - return Ternary::False; + // TODO: Use parameters MD5AnimParser parser(stream); - return parser.Check(); - } - std::shared_ptr LoadMD5Anim(Stream& stream, const AnimationParams& /*parameters*/) - { - ///TODO: Utiliser les paramètres - MD5AnimParser parser(stream); + UInt64 streamPos = stream.GetCursorPos(); + + if (!parser.Check()) + return Err(ResourceLoadingError::Unrecognized); + + stream.SetCursorPos(streamPos); if (!parser.Parse()) { NazaraError("MD5Anim parser failed"); - return nullptr; + return Err(ResourceLoadingError::DecodingError); } const MD5AnimParser::Frame* frames = parser.GetFrames(); @@ -99,8 +97,15 @@ namespace Nz { AnimationLoader::Entry loader; loader.extensionSupport = IsMD5AnimSupported; - loader.streamChecker = CheckMD5Anim; loader.streamLoader = LoadMD5Anim; + loader.parameterFilter = [](const AnimationParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipBuiltinMD5AnimLoader", &skip) && skip) + return false; + + return true; + }; return loader; } diff --git a/src/Nazara/Utility/Formats/MD5AnimParser.cpp b/src/Nazara/Utility/Formats/MD5AnimParser.cpp index 64b1bc95c..06b649e19 100644 --- a/src/Nazara/Utility/Formats/MD5AnimParser.cpp +++ b/src/Nazara/Utility/Formats/MD5AnimParser.cpp @@ -29,7 +29,7 @@ namespace Nz m_stream.EnableTextMode(false); } - Ternary MD5AnimParser::Check() + bool MD5AnimParser::Check() { if (Advance(false)) { @@ -37,11 +37,11 @@ namespace Nz if (std::sscanf(&m_currentLine[0], " MD5Version %u", &version) == 1) { if (version == 10) - return Ternary::True; + return true; } } - return Ternary::False; + return false; } UInt32 MD5AnimParser::GetAnimatedComponentCount() const diff --git a/src/Nazara/Utility/Formats/MD5MeshLoader.cpp b/src/Nazara/Utility/Formats/MD5MeshLoader.cpp index 18d42c18c..d113303a8 100644 --- a/src/Nazara/Utility/Formats/MD5MeshLoader.cpp +++ b/src/Nazara/Utility/Formats/MD5MeshLoader.cpp @@ -22,26 +22,24 @@ namespace Nz { bool IsMD5MeshSupported(const std::string_view& extension) { - return (extension == "md5mesh"); + return (extension == ".md5mesh"); } - Ternary CheckMD5Mesh(Stream& stream, const MeshParams& parameters) - { - bool skip; - if (parameters.custom.GetBooleanParameter("SkipBuiltinMD5MeshLoader", &skip) && skip) - return Ternary::False; - - MD5MeshParser parser(stream); - return parser.Check(); - } - - std::shared_ptr LoadMD5Mesh(Stream& stream, const MeshParams& parameters) + Result, ResourceLoadingError> LoadMD5Mesh(Stream& stream, const MeshParams& parameters) { MD5MeshParser parser(stream); + + UInt64 streamPos = stream.GetCursorPos(); + + if (!parser.Check()) + return Err(ResourceLoadingError::Unrecognized); + + stream.SetCursorPos(streamPos); + if (!parser.Parse()) { NazaraError("MD5Mesh parser failed"); - return nullptr; + return Err(ResourceLoadingError::DecodingError); } UInt32 maxWeightCount = 4; @@ -253,7 +251,7 @@ namespace Nz if (!mesh->CreateStatic()) // Ne devrait jamais échouer { NazaraInternalError("Failed to create mesh"); - return nullptr; + return Err(ResourceLoadingError::Internal); } mesh->SetMaterialCount(meshCount); @@ -360,8 +358,15 @@ namespace Nz { MeshLoader::Entry loader; loader.extensionSupport = IsMD5MeshSupported; - loader.streamChecker = CheckMD5Mesh; loader.streamLoader = LoadMD5Mesh; + loader.parameterFilter = [](const MeshParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipBuiltinMD5MeshLoader", &skip) && skip) + return false; + + return true; + }; return loader; } diff --git a/src/Nazara/Utility/Formats/MD5MeshParser.cpp b/src/Nazara/Utility/Formats/MD5MeshParser.cpp index 7b2cff573..6510faaec 100644 --- a/src/Nazara/Utility/Formats/MD5MeshParser.cpp +++ b/src/Nazara/Utility/Formats/MD5MeshParser.cpp @@ -29,7 +29,7 @@ namespace Nz m_stream.EnableTextMode(false); } - Ternary MD5MeshParser::Check() + bool MD5MeshParser::Check() { if (Advance(false)) { @@ -37,11 +37,11 @@ namespace Nz if (std::sscanf(&m_currentLine[0], " MD5Version %u", &version) == 1) { if (version == 10) - return Ternary::True; + return true; } } - return Ternary::False; + return false; } const MD5MeshParser::Joint* MD5MeshParser::GetJoints() const diff --git a/src/Nazara/Utility/Formats/OBJLoader.cpp b/src/Nazara/Utility/Formats/OBJLoader.cpp index d8282586a..8e5e81184 100644 --- a/src/Nazara/Utility/Formats/OBJLoader.cpp +++ b/src/Nazara/Utility/Formats/OBJLoader.cpp @@ -16,7 +16,7 @@ #include #include -///TODO: N'avoir qu'un seul VertexBuffer communs à tous les meshes +// TODO: Use only one index buffer / vertex buffer for all submeshes namespace Nz { @@ -24,22 +24,7 @@ namespace Nz { bool IsOBJSupported(const std::string_view& extension) { - return (extension == "obj"); - } - - Ternary CheckOBJ(Stream& stream, const MeshParams& parameters) - { - NazaraUnused(stream); - - bool skip; - if (parameters.custom.GetBooleanParameter("SkipBuiltinOBJLoader", &skip) && skip) - return Ternary::False; - - OBJParser parser; - if (!parser.Check(stream)) - return Ternary::False; - - return Ternary::Unknown; + return (extension == ".obj"); } bool ParseMTL(Mesh& mesh, const std::filesystem::path& filePath, const std::string* materials, const OBJParser::Mesh* meshes, std::size_t meshCount) @@ -157,17 +142,25 @@ namespace Nz return true; } - std::shared_ptr LoadOBJ(Stream& stream, const MeshParams& parameters) + Result, ResourceLoadingError> LoadOBJ(Stream& stream, const MeshParams& parameters) { long long reservedVertexCount; if (!parameters.custom.GetIntegerParameter("NativeOBJLoader_VertexCount", &reservedVertexCount)) reservedVertexCount = 100; OBJParser parser; + + UInt64 streamPos = stream.GetCursorPos(); + + if (!parser.Check(stream)) + return Err(ResourceLoadingError::Unrecognized); + + stream.SetCursorPos(streamPos); + if (!parser.Parse(stream, reservedVertexCount)) { NazaraError("OBJ parser failed"); - return nullptr; + return Err(ResourceLoadingError::DecodingError); } std::shared_ptr mesh = std::make_shared(); @@ -181,8 +174,7 @@ namespace Nz const OBJParser::Mesh* meshes = parser.GetMeshes(); std::size_t meshCount = parser.GetMeshCount(); - NazaraAssert(materials != nullptr && positions != nullptr && normals != nullptr && - texCoords != nullptr && meshes != nullptr && meshCount > 0, + NazaraAssert(materials != nullptr && positions != nullptr && normals != nullptr && texCoords != nullptr && meshes != nullptr && meshCount > 0, "Invalid OBJParser output"); // Triangulation temporary vector @@ -365,8 +357,15 @@ namespace Nz { MeshLoader::Entry loader; loader.extensionSupport = IsOBJSupported; - loader.streamChecker = CheckOBJ; loader.streamLoader = LoadOBJ; + loader.parameterFilter = [](const MeshParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipBuiltinOBJLoader", &skip) && skip) + return false; + + return true; + }; return loader; } diff --git a/src/Nazara/Utility/Formats/OBJSaver.cpp b/src/Nazara/Utility/Formats/OBJSaver.cpp index a858b707e..cb2fa7b9a 100644 --- a/src/Nazara/Utility/Formats/OBJSaver.cpp +++ b/src/Nazara/Utility/Formats/OBJSaver.cpp @@ -54,7 +54,7 @@ namespace Nz bool IsOBJSupportedSave(const std::string_view& extension) { - return (extension == "obj"); + return (extension == ".obj"); } bool SaveOBJToStream(const Mesh& mesh, const std::string& format, Stream& stream, const MeshParams& parameters) diff --git a/src/Nazara/Utility/Formats/PCXLoader.cpp b/src/Nazara/Utility/Formats/PCXLoader.cpp index 316d9539f..07ce34093 100644 --- a/src/Nazara/Utility/Formats/PCXLoader.cpp +++ b/src/Nazara/Utility/Formats/PCXLoader.cpp @@ -42,35 +42,17 @@ namespace Nz bool IsPCXSupported(const std::string_view& extension) { - return (extension == "pcx"); + return (extension == ".pcx"); } - Ternary CheckPCX(Stream& stream, const ImageParams& parameters) + Result, ResourceLoadingError> LoadPCX(Stream& stream, const ImageParams& parameters) { - bool skip; - if (parameters.custom.GetBooleanParameter("SkipBuiltinPCXLoader", &skip) && skip) - return Ternary::False; - - UInt8 manufacturer; - if (stream.Read(&manufacturer, 1) == 1) - { - if (manufacturer == 0x0a) - return Ternary::True; - } - - return Ternary::False; - } - - std::shared_ptr LoadPCX(Stream& stream, const ImageParams& parameters) - { - NazaraUnused(parameters); - PCXHeader header; if (stream.Read(&header, sizeof(PCXHeader)) != sizeof(PCXHeader)) - { - NazaraError("Failed to read header"); - return nullptr; - } + return Err(ResourceLoadingError::Unrecognized); + + if (header.manufacturer != 0x0a) + return Err(ResourceLoadingError::Unrecognized); #ifdef NAZARA_BIG_ENDIAN // Les fichiers PCX sont en little endian @@ -95,7 +77,7 @@ namespace Nz if (!image->Create(ImageType::E2D, PixelFormat::RGB8, width, height, 1, (parameters.levelCount > 0) ? parameters.levelCount : 1)) { NazaraError("Failed to create image"); - return nullptr; + return Err(ResourceLoadingError::Internal); } UInt8* pixels = image->GetPixels(); @@ -120,7 +102,7 @@ namespace Nz if (!stream.Read(&rleValue, 1)) { NazaraError("Failed to read stream (byte " + NumberToString(stream.GetCursorPos()) + ')'); - return nullptr; + return Err(ResourceLoadingError::DecodingError); } if (rleValue < 0xc0) @@ -131,7 +113,7 @@ namespace Nz if (!stream.Read(&rleValue, 1)) { NazaraError("Failed to read stream (byte " + NumberToString(stream.GetCursorPos()) + ')'); - return nullptr; + return Err(ResourceLoadingError::DecodingError); } } } @@ -175,7 +157,7 @@ namespace Nz if (!stream.Read(&rleValue, 1)) { NazaraError("Failed to read stream (byte " + NumberToString(stream.GetCursorPos()) + ')'); - return nullptr; + return Err(ResourceLoadingError::DecodingError); } if (rleValue < 0xc0) @@ -186,7 +168,7 @@ namespace Nz if (!stream.Read(&rleValue, 1)) { NazaraError("Failed to read stream (byte " + NumberToString(stream.GetCursorPos()) + ')'); - return nullptr; + return Err(ResourceLoadingError::DecodingError); } } } @@ -226,21 +208,21 @@ namespace Nz if (!stream.Read(&magic, 1)) { NazaraError("Failed to read stream (byte " + NumberToString(stream.GetCursorPos()) + ')'); - return nullptr; + return Err(ResourceLoadingError::DecodingError); } /* first byte must be equal to 0x0c (12) */ if (magic != 0x0c) { NazaraError("Colormap's first byte must be 0x0c (0x" + NumberToString(magic, 16) + ')'); - return nullptr; + return Err(ResourceLoadingError::DecodingError); } /* read palette */ if (stream.Read(palette, 768) != 768) { NazaraError("Failed to read palette"); - return nullptr; + return Err(ResourceLoadingError::DecodingError); } stream.SetCursorPos(curPos); @@ -259,7 +241,7 @@ namespace Nz if (!stream.Read(&rleValue, 1)) { NazaraError("Failed to read stream (byte " + NumberToString(stream.GetCursorPos()) + ')'); - return nullptr; + return Err(ResourceLoadingError::DecodingError); } if (rleValue < 0xc0) @@ -270,7 +252,7 @@ namespace Nz if (!stream.Read(&rleValue, 1)) { NazaraError("Failed to read stream (byte " + NumberToString(stream.GetCursorPos()) + ')'); - return nullptr; + return Err(ResourceLoadingError::DecodingError); } } } @@ -303,7 +285,7 @@ namespace Nz if (!stream.Read(&rleValue, 1)) { NazaraError("Failed to read stream (byte " + NumberToString(stream.GetCursorPos()) + ')'); - return nullptr; + return Err(ResourceLoadingError::DecodingError); } if (rleValue < 0xc0) @@ -314,7 +296,7 @@ namespace Nz if (!stream.Read(&rleValue, 1)) { NazaraError("Failed to read stream (byte " + NumberToString(stream.GetCursorPos()) + ')'); - return nullptr; + return Err(ResourceLoadingError::DecodingError); } } } @@ -330,7 +312,7 @@ namespace Nz default: NazaraError("Unsupported " + NumberToString(bitCount) + " bitcount for pcx files"); - return nullptr; + return Err(ResourceLoadingError::DecodingError); } if (parameters.loadFormat != PixelFormat::Undefined) @@ -346,8 +328,15 @@ namespace Nz { ImageLoader::Entry loaderEntry; loaderEntry.extensionSupport = IsPCXSupported; - loaderEntry.streamChecker = CheckPCX; loaderEntry.streamLoader = LoadPCX; + loaderEntry.parameterFilter = [](const ImageParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipBuiltinPCXLoader", &skip) && skip) + return false; + + return true; + }; return loaderEntry; } diff --git a/src/Nazara/Utility/Formats/STBLoader.cpp b/src/Nazara/Utility/Formats/STBLoader.cpp index 4240d2eda..62cb888a1 100644 --- a/src/Nazara/Utility/Formats/STBLoader.cpp +++ b/src/Nazara/Utility/Formats/STBLoader.cpp @@ -38,37 +38,27 @@ namespace Nz static stbi_io_callbacks s_stbiCallbacks = { StbiRead, StbiSkip, StbiEof }; - constexpr auto s_supportedExtensions = frozen::make_unordered_set({ "bmp", "gif", "hdr", "jpg", "jpeg", "pic", "png", "ppm", "pgm", "psd", "tga" }); + constexpr auto s_supportedExtensions = frozen::make_unordered_set({ ".bmp", ".gif", ".hdr", ".jpg", ".jpeg", ".pic", ".png", ".ppm", ".pgm", ".psd", ".tga" }); bool IsSTBSupported(const std::string_view& extension) { return s_supportedExtensions.find(extension) != s_supportedExtensions.end(); } - Ternary CheckSTB(Stream& stream, const ImageParams& parameters) + Result, ResourceLoadingError> LoadSTB(Stream& stream, const ImageParams& parameters) { - bool skip; - if (parameters.custom.GetBooleanParameter("SkipBuiltinSTBLoader", &skip) && skip) - return Ternary::False; - int width, height, bpp; - if (stbi_info_from_callbacks(&s_stbiCallbacks, &stream, &width, &height, &bpp)) - return Ternary::True; - else - return Ternary::False; - } + if (!stbi_info_from_callbacks(&s_stbiCallbacks, &stream, &width, &height, &bpp)) + return Err(ResourceLoadingError::Unrecognized); - std::shared_ptr LoadSTB(Stream& stream, const ImageParams& parameters) - { - // Je charge tout en RGBA8 et je converti ensuite via la méthode Convert - // Ceci à cause d'un bug de STB lorsqu'il s'agit de charger certaines images (ex: JPG) en "default" + // Load everything as RGBA8 and then convert using the Image::Convert method + // This is because of a STB bug when loading some JPG images with default settings - int width, height, bpp; UInt8* ptr = stbi_load_from_callbacks(&s_stbiCallbacks, &stream, &width, &height, &bpp, STBI_rgb_alpha); if (!ptr) { NazaraError("Failed to load image: " + std::string(stbi_failure_reason())); - return {}; + return Err(ResourceLoadingError::Unrecognized); } CallOnExit freeStbiImage([ptr]() @@ -80,7 +70,7 @@ namespace Nz if (!image->Create(ImageType::E2D, PixelFormat::RGBA8, width, height, 1, (parameters.levelCount > 0) ? parameters.levelCount : 1)) { NazaraError("Failed to create image"); - return {}; + return Err(ResourceLoadingError::Internal); } image->Update(ptr); @@ -92,7 +82,7 @@ namespace Nz if (!image->Convert(parameters.loadFormat)) { NazaraError("Failed to convert image to required format"); - return {}; + return Err(ResourceLoadingError::Internal); } } @@ -106,8 +96,15 @@ namespace Nz { ImageLoader::Entry loaderEntry; loaderEntry.extensionSupport = IsSTBSupported; - loaderEntry.streamChecker = CheckSTB; loaderEntry.streamLoader = LoadSTB; + loaderEntry.parameterFilter = [](const ImageParams& parameters) + { + bool skip; + if (parameters.custom.GetBooleanParameter("SkipBuiltinSTBLoader", &skip) && skip) + return false; + + return true; + }; return loaderEntry; } diff --git a/xmake.lua b/xmake.lua index 39034b52b..4ba81f471 100644 --- a/xmake.lua +++ b/xmake.lua @@ -41,9 +41,9 @@ NazaraRendererBackends = rendererBackends local modules = { Audio = { Deps = {"NazaraCore"}, - Packages = {"dr_wav", "libflac", "libvorbis", "minimp3"}, + Packages = {"dr_wav", "frozen", "libflac", "libvorbis", "minimp3"}, Custom = function () - add_packages("openal-soft", {links = {}}) -- Don't link OpenAL (it will be loaded dynamically) + add_packages("openal-soft", { links = {} }) -- Don't link OpenAL (it will be loaded dynamically) end }, Core = {