Major ResourceLoader rework (using Nz::Result)
This commit is contained in:
parent
131faa4fbd
commit
47cb878f9d
|
|
@ -191,6 +191,15 @@ namespace Nz
|
|||
|
||||
constexpr std::size_t ProcessorVendorCount = static_cast<std::size_t>(ProcessorVendor::Max) + 1;
|
||||
|
||||
enum class ResourceLoadingError
|
||||
{
|
||||
DecodingError,
|
||||
FailedToOpenFile,
|
||||
Internal,
|
||||
Unsupported,
|
||||
Unrecognized
|
||||
};
|
||||
|
||||
enum class SphereType
|
||||
{
|
||||
Cubic,
|
||||
|
|
@ -218,15 +227,6 @@ namespace Nz
|
|||
};
|
||||
|
||||
using StreamOptionFlags = Flags<StreamOption>;
|
||||
|
||||
enum class Ternary
|
||||
{
|
||||
False,
|
||||
True,
|
||||
Unknown,
|
||||
|
||||
Max = Unknown
|
||||
};
|
||||
}
|
||||
|
||||
#endif // NAZARA_CORE_ENUMS_HPP
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <Nazara/Core/Enums.hpp>
|
||||
#include <Nazara/Core/Resource.hpp>
|
||||
#include <Nazara/Core/ResourceParameters.hpp>
|
||||
#include <Nazara/Utils/Result.hpp>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
|
@ -29,11 +30,11 @@ namespace Nz
|
|||
|
||||
public:
|
||||
struct Entry;
|
||||
using ExtensionSupport = std::function<bool(const std::string_view& extension)>;
|
||||
using FileLoader = std::function<std::shared_ptr<Type>(const std::filesystem::path& filePath, const Parameters& parameters)>;
|
||||
using MemoryLoader = std::function<std::shared_ptr<Type>(const void* data, std::size_t size, const Parameters& parameters)>;
|
||||
using StreamChecker = std::function<Ternary(Stream& stream, const Parameters& parameters)>;
|
||||
using StreamLoader = std::function<std::shared_ptr<Type>(Stream& stream, const Parameters& parameters)>;
|
||||
using ExtensionSupport = std::function<bool(std::string_view extension)>;
|
||||
using FileLoader = std::function<Result<std::shared_ptr<Type>, ResourceLoadingError>(const std::filesystem::path& filePath, const Parameters& parameters)>;
|
||||
using MemoryLoader = std::function<Result<std::shared_ptr<Type>, ResourceLoadingError>(const void* data, std::size_t size, const Parameters& parameters)>;
|
||||
using ParameterFilter = std::function<bool(const Parameters& parameters)>;
|
||||
using StreamLoader = std::function<Result<std::shared_ptr<Type>, 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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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<typename Type, typename Parameters>
|
||||
bool ResourceLoader<Type, Parameters>::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<typename Type, typename Parameters>
|
||||
std::shared_ptr<Type> ResourceLoader<Type, Parameters>::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 (loader.parameterFilter && !loader.parameterFilter(parameters))
|
||||
continue;
|
||||
|
||||
Result<std::shared_ptr<Type>, ResourceLoadingError> result = Nz::Err(ResourceLoadingError::Unrecognized);
|
||||
|
||||
if (loader.fileLoader)
|
||||
result = loader.fileLoader(filePath, parameters);
|
||||
else if (loader.streamLoader)
|
||||
{
|
||||
if (!file.IsOpen())
|
||||
{
|
||||
if (!file.Open(OpenMode::ReadOnly))
|
||||
{
|
||||
NazaraError("Failed to load file: unable to open \"" + PathToString(filePath) + '"');
|
||||
NazaraError("failed to load resource: unable to open \"" + PathToString(filePath) + '"');
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Ternary recognized = Ternary::Unknown;
|
||||
if (loader.fileLoader)
|
||||
{
|
||||
if (loader.streamChecker)
|
||||
{
|
||||
else
|
||||
file.SetCursorPos(0);
|
||||
|
||||
recognized = loader.streamChecker(file, parameters);
|
||||
if (recognized == Ternary::False)
|
||||
result = loader.streamLoader(file, parameters);
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
ResourceLoadingError error = result.GetError();
|
||||
if (error != ResourceLoadingError::Unrecognized)
|
||||
NazaraError("failed to load resource: loader failed");
|
||||
else
|
||||
found = true;
|
||||
|
||||
continue;
|
||||
else
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
recognized = Ternary::Unknown;
|
||||
found = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<Type> resource = loader.fileLoader(filePath, parameters);
|
||||
if (resource)
|
||||
{
|
||||
std::shared_ptr<Type> resource = std::move(result).GetValue();
|
||||
resource->SetFilePath(filePath);
|
||||
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(loader.streamChecker);
|
||||
|
||||
file.SetCursorPos(0);
|
||||
|
||||
recognized = loader.streamChecker(file, parameters);
|
||||
if (recognized == Ternary::False)
|
||||
continue;
|
||||
else if (recognized == Ternary::True)
|
||||
found = true;
|
||||
|
||||
file.SetCursorPos(0);
|
||||
|
||||
std::shared_ptr<Type> resource = loader.streamLoader(file, parameters);
|
||||
if (resource)
|
||||
{
|
||||
resource->SetFilePath(filePath);
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
|
||||
if (recognized == Ternary::True)
|
||||
NazaraWarning("Loader failed");
|
||||
}
|
||||
|
||||
if (found)
|
||||
NazaraError("Failed to load file: all loaders failed");
|
||||
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<typename Type, typename Parameters>
|
||||
std::shared_ptr<Type> ResourceLoader<Type, Parameters>::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<std::shared_ptr<Type>, 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);
|
||||
|
||||
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;
|
||||
|
||||
recognized = loader.streamChecker(stream, parameters);
|
||||
if (recognized == Ternary::False)
|
||||
continue;
|
||||
else
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
recognized = Ternary::Unknown;
|
||||
found = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<Type> 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<Type> resource = loader.streamLoader(stream, parameters);
|
||||
if (resource)
|
||||
return resource;
|
||||
}
|
||||
|
||||
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<typename Type, typename Parameters>
|
||||
std::shared_ptr<Type> ResourceLoader<Type, Parameters>::LoadFromStream(Stream& stream, const Parameters& parameters) const
|
||||
|
|
@ -265,31 +206,31 @@ namespace Nz
|
|||
{
|
||||
const Entry& loader = *loaderPtr;
|
||||
|
||||
if (loader.parameterFilter && !loader.parameterFilter(parameters))
|
||||
continue;
|
||||
|
||||
stream.SetCursorPos(streamPos);
|
||||
|
||||
// Does the loader support these data ?
|
||||
Ternary recognized = loader.streamChecker(stream, parameters);
|
||||
if (recognized == Ternary::False)
|
||||
continue;
|
||||
else if (recognized == Ternary::True)
|
||||
// Load resource
|
||||
Result<std::shared_ptr<Type>, 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;
|
||||
|
||||
// We move the stream to its old position
|
||||
stream.SetCursorPos(streamPos);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Load of the resource
|
||||
std::shared_ptr<Type> resource = loader.streamLoader(stream, parameters);
|
||||
if (resource)
|
||||
return resource;
|
||||
|
||||
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 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<typename Type, typename Parameters>
|
||||
auto ResourceLoader<Type, Parameters>::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<Entry>(std::move(loader)));
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ namespace Nz
|
|||
template<typename Type, typename Parameters>
|
||||
bool ResourceSaver<Type, Parameters>::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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ namespace Nz
|
|||
MD5AnimParser(Stream& stream);
|
||||
~MD5AnimParser();
|
||||
|
||||
Ternary Check();
|
||||
bool Check();
|
||||
|
||||
UInt32 GetAnimatedComponentCount() const;
|
||||
const Frame* GetFrames() const;
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ namespace Nz
|
|||
MD5MeshParser(Stream& stream);
|
||||
~MD5MeshParser();
|
||||
|
||||
Ternary Check();
|
||||
bool Check();
|
||||
|
||||
const Joint* GetJoints() const;
|
||||
UInt32 GetJointCount() const;
|
||||
|
|
|
|||
|
|
@ -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<Nz::Animation> LoadAnimation(Nz::Stream& stream, const Nz::AnimationParams& parameters)
|
||||
Nz::Result<std::shared_ptr<Nz::Animation>, Nz::ResourceLoadingError> LoadAnimation(Nz::Stream& stream, const Nz::AnimationParams& parameters)
|
||||
{
|
||||
NazaraAssert(parameters.IsValid(), "invalid animation parameters");
|
||||
|
||||
|
|
@ -280,13 +266,13 @@ std::shared_ptr<Nz::Animation> 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<Nz::SubMesh> 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<Nz::Mesh> LoadMesh(Nz::Stream& stream, const Nz::MeshParams& parameters)
|
||||
Nz::Result<std::shared_ptr<Nz::Mesh>, Nz::ResourceLoadingError> LoadMesh(Nz::Stream& stream, const Nz::MeshParams& parameters)
|
||||
{
|
||||
std::string streamPath = Nz::PathToString(stream.GetPath());
|
||||
|
||||
|
|
@ -838,7 +815,7 @@ std::shared_ptr<Nz::Mesh> 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<Nz::Mesh> 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<Nz::Mesh> mesh = std::make_shared<Nz::Mesh>();
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ namespace
|
|||
av_free(&m_ioBuffer);
|
||||
}
|
||||
|
||||
bool Check()
|
||||
Nz::Result<void, Nz::ResourceLoadingError> 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<void, Nz::ResourceLoadingError> 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<Nz::ImageStream> LoadFile(const std::filesystem::path& filePath, const Nz::ImageStreamParams& /*parameters*/)
|
||||
Nz::Result<std::shared_ptr<Nz::ImageStream>, Nz::ResourceLoadingError> LoadFile(const std::filesystem::path& filePath, const Nz::ImageStreamParams& /*parameters*/)
|
||||
{
|
||||
std::shared_ptr<FFmpegStream> ffmpegStream = std::make_shared<FFmpegStream>();
|
||||
ffmpegStream->SetFile(filePath);
|
||||
|
||||
if (!ffmpegStream->Open())
|
||||
return {};
|
||||
|
||||
return ffmpegStream;
|
||||
Nz::Result<void, Nz::ResourceLoadingError> status = ffmpegStream->Open();
|
||||
return status.Map([&] { return std::move(ffmpegStream); });
|
||||
}
|
||||
|
||||
std::shared_ptr<Nz::ImageStream> LoadMemory(const void* ptr, std::size_t size, const Nz::ImageStreamParams& /*parameters*/)
|
||||
Nz::Result<std::shared_ptr<Nz::ImageStream>, Nz::ResourceLoadingError> LoadMemory(const void* ptr, std::size_t size, const Nz::ImageStreamParams& /*parameters*/)
|
||||
{
|
||||
std::shared_ptr<FFmpegStream> ffmpegStream = std::make_shared<FFmpegStream>();
|
||||
ffmpegStream->SetMemory(ptr, size);
|
||||
|
||||
if (!ffmpegStream->Open())
|
||||
return {};
|
||||
|
||||
return ffmpegStream;
|
||||
Nz::Result<void, Nz::ResourceLoadingError> status = ffmpegStream->Open();
|
||||
return status.Map([&] { return std::move(ffmpegStream); });
|
||||
}
|
||||
|
||||
std::shared_ptr<Nz::ImageStream> LoadStream(Nz::Stream& stream, const Nz::ImageStreamParams& /*parameters*/)
|
||||
Nz::Result<std::shared_ptr<Nz::ImageStream>, Nz::ResourceLoadingError> LoadStream(Nz::Stream& stream, const Nz::ImageStreamParams& /*parameters*/)
|
||||
{
|
||||
std::shared_ptr<FFmpegStream> ffmpegStream = std::make_shared<FFmpegStream>();
|
||||
ffmpegStream->SetStream(stream);
|
||||
|
||||
if (!ffmpegStream->Open())
|
||||
return {};
|
||||
|
||||
return ffmpegStream;
|
||||
Nz::Result<void, Nz::ResourceLoadingError> 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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<SoundBuffer> LoadWavSoundBuffer(Stream& stream, const SoundBufferParams& parameters)
|
||||
Result<std::shared_ptr<SoundBuffer>, 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<void, ResourceLoadingError> Open(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::unique_ptr<File> file = std::make_unique<File>();
|
||||
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<void, ResourceLoadingError> Open(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
{
|
||||
m_ownedStream = std::make_unique<MemoryView>(data, size);
|
||||
return Open(*m_ownedStream, forceMono);
|
||||
return Open(*m_ownedStream, parameters);
|
||||
}
|
||||
|
||||
bool Open(Stream& stream, bool forceMono)
|
||||
Result<void, ResourceLoadingError> 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<SoundStream> LoadWavSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadWavSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<drwavStream> soundStream = std::make_shared<drwavStream>();
|
||||
if (!soundStream->Open(filePath, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open sound stream");
|
||||
return {};
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(filePath, parameters);
|
||||
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
|
||||
return soundStream;
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundStream> LoadWavSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadWavSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<drwavStream> soundStream = std::make_shared<drwavStream>();
|
||||
if (!soundStream->Open(data, size, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open music stream");
|
||||
return {};
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(data, size, parameters);
|
||||
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
|
||||
return soundStream;
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundStream> LoadWavSoundStreamStream(Stream& stream, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadWavSoundStreamStream(Stream& stream, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<drwavStream> soundStream = std::make_shared<drwavStream>();
|
||||
if (!soundStream->Open(stream, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open music stream");
|
||||
return {};
|
||||
}
|
||||
Result<void, ResourceLoadingError> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<SoundBuffer> LoadFlacSoundBuffer(Stream& stream, const SoundBufferParams& parameters)
|
||||
Result<std::shared_ptr<SoundBuffer>, 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<AudioFormat> 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<void, ResourceLoadingError> Open(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::unique_ptr<File> file = std::make_unique<File>();
|
||||
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<void, ResourceLoadingError> Open(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
{
|
||||
m_ownedStream = std::make_unique<MemoryView>(data, size);
|
||||
return Open(*m_ownedStream, forceMono);
|
||||
return Open(*m_ownedStream, parameters);
|
||||
}
|
||||
|
||||
bool Open(Stream& stream, bool forceMono)
|
||||
Result<void, ResourceLoadingError> 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<AudioFormat> 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<SoundStream> LoadFlacSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadFlacSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<libflacStream> soundStream = std::make_shared<libflacStream>();
|
||||
if (!soundStream->Open(filePath, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open sound stream");
|
||||
return {};
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(filePath, parameters);
|
||||
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
|
||||
return soundStream;
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundStream> LoadFlacSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadFlacSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<libflacStream> soundStream = std::make_shared<libflacStream>();
|
||||
if (!soundStream->Open(data, size, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open music stream");
|
||||
return {};
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(data, size, parameters);
|
||||
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
|
||||
return soundStream;
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundStream> LoadFlacSoundStreamStream(Stream& stream, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadFlacSoundStreamStream(Stream& stream, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<libflacStream> soundStream = std::make_shared<libflacStream>();
|
||||
if (!soundStream->Open(stream, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open music stream");
|
||||
return {};
|
||||
}
|
||||
Result<void, ResourceLoadingError> 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.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.parameterFilter = [](const SoundStreamParams& parameters)
|
||||
{
|
||||
bool skip;
|
||||
if (parameters.custom.GetBooleanParameter("SkipBuiltinFlacLoader", &skip) && skip)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return loaderEntry;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,9 @@
|
|||
#include <Nazara/Core/Stream.hpp>
|
||||
#include <Nazara/Utils/CallOnExit.hpp>
|
||||
#include <Nazara/Utils/Endianness.hpp>
|
||||
#include <frozen/string.h>
|
||||
#include <frozen/unordered_set.h>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
|
||||
#define OV_EXCLUDE_STATIC_CALLBACKS
|
||||
#include <vorbis/vorbisfile.h>
|
||||
|
|
@ -116,38 +117,19 @@ namespace Nz
|
|||
return sampleCount - remainingBytes / sizeof(Int16);
|
||||
}
|
||||
|
||||
bool IsVorbisSupported(const std::string_view& extension)
|
||||
{
|
||||
static std::set<std::string_view> supportedExtensions = {
|
||||
"oga", "ogg", "ogm", "ogv", "ogx", "opus", "spx"
|
||||
};
|
||||
constexpr auto s_supportedExtensions = frozen::make_unordered_set<frozen::string>({ ".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<SoundBuffer> LoadVorbisSoundBuffer(Stream& stream, const SoundBufferParams& parameters)
|
||||
Result<std::shared_ptr<SoundBuffer>, 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<void, ResourceLoadingError> Open(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::unique_ptr<File> file = std::make_unique<File>();
|
||||
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<void, ResourceLoadingError> Open(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
{
|
||||
m_ownedStream = std::make_unique<MemoryView>(data, size);
|
||||
return Open(*m_ownedStream, forceMono);
|
||||
return Open(*m_ownedStream, parameters);
|
||||
}
|
||||
|
||||
bool Open(Stream& stream, bool forceMono)
|
||||
Result<void, ResourceLoadingError> 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<SoundStream> LoadVorbisSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadVorbisSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<libvorbisStream> soundStream = std::make_shared<libvorbisStream>();
|
||||
if (!soundStream->Open(filePath, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open sound stream");
|
||||
return {};
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(filePath, parameters);
|
||||
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
|
||||
return soundStream;
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundStream> LoadVorbisSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadVorbisSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<libvorbisStream> soundStream = std::make_shared<libvorbisStream>();
|
||||
if (!soundStream->Open(data, size, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open music stream");
|
||||
return {};
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(data, size, parameters);
|
||||
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
|
||||
return soundStream;
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundStream> LoadVorbisSoundStreamStream(Stream& stream, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadVorbisSoundStreamStream(Stream& stream, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<libvorbisStream> soundStream = std::make_shared<libvorbisStream>();
|
||||
if (!soundStream->Open(stream, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open music stream");
|
||||
return {};
|
||||
}
|
||||
Result<void, ResourceLoadingError> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<UInt8> buffer(MINIMP3_BUF_SIZE);
|
||||
return (mp3dec_detect_cb(&io, buffer.data(), buffer.size()) == 0) ? Ternary::True : Ternary::False;
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundBuffer> LoadMP3SoundBuffer(Stream& stream, const SoundBufferParams& parameters)
|
||||
Result<std::shared_ptr<SoundBuffer>, ResourceLoadingError> LoadMP3SoundBuffer(Stream& stream, const SoundBufferParams& parameters)
|
||||
{
|
||||
static_assert(std::is_same_v<mp3d_sample_t, Int16>);
|
||||
|
||||
|
|
@ -91,12 +75,20 @@ namespace Nz
|
|||
|
||||
mp3dec_t dec;
|
||||
mp3dec_file_info_t info;
|
||||
std::vector<UInt8> 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<UInt8[]> buffer = std::make_unique<UInt8[]>(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<void, ResourceLoadingError> Open(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::unique_ptr<File> file = std::make_unique<File>();
|
||||
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<void, ResourceLoadingError> Open(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
{
|
||||
m_ownedStream = std::make_unique<MemoryView>(data, size);
|
||||
return Open(*m_ownedStream, forceMono);
|
||||
return Open(*m_ownedStream, parameters);
|
||||
}
|
||||
|
||||
bool Open(Stream& stream, bool forceMono)
|
||||
Result<void, ResourceLoadingError> 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<UInt8[]> buffer = std::make_unique<UInt8[]>(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<UInt32>(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<SoundStream> MP3LoadSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadMP3SoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<minimp3Stream> soundStream = std::make_shared<minimp3Stream>();
|
||||
if (!soundStream->Open(filePath, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open sound stream");
|
||||
return {};
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(filePath, parameters);
|
||||
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
|
||||
return soundStream;
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundStream> MP3LoadSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadMP3SoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<minimp3Stream> soundStream = std::make_shared<minimp3Stream>();
|
||||
if (!soundStream->Open(data, size, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open music stream");
|
||||
return {};
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(data, size, parameters);
|
||||
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
|
||||
return soundStream;
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundStream> MP3LoadSoundStreamStream(Stream& stream, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadMP3SoundStreamStream(Stream& stream, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<minimp3Stream> soundStream = std::make_shared<minimp3Stream>();
|
||||
if (!soundStream->Open(stream, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open music stream");
|
||||
return {};
|
||||
}
|
||||
Result<void, ResourceLoadingError> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<std::shared_ptr<Image>, 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<Image> 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> image = std::make_shared<Image>(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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,8 +15,9 @@
|
|||
#include <Nazara/Utility/Font.hpp>
|
||||
#include <Nazara/Utility/FontData.hpp>
|
||||
#include <Nazara/Utility/FontGlyph.hpp>
|
||||
#include <frozen/string.h>
|
||||
#include <frozen/unordered_set.h>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
|
|
@ -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<frozen::string>({ ".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<std::string_view> 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<Font> LoadFreetypeFile(const std::filesystem::path& filePath, const FontParams& parameters)
|
||||
Result<std::shared_ptr<Font>, 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> font = std::make_shared<Font>();
|
||||
if (!font->Create(std::move(face)))
|
||||
return nullptr;
|
||||
return Err(ResourceLoadingError::Internal);
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
std::shared_ptr<Font> LoadFreetypeMemory(const void* data, std::size_t size, const FontParams& parameters)
|
||||
Result<std::shared_ptr<Font>, 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> font = std::make_shared<Font>();
|
||||
if (!font->Create(std::move(face)))
|
||||
return nullptr;
|
||||
return Err(ResourceLoadingError::Internal);
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
std::shared_ptr<Font> LoadFreetypeStream(Stream& stream, const FontParams& parameters)
|
||||
Result<std::shared_ptr<Font>, ResourceLoadingError> LoadFreetypeStream(Stream& stream, const FontParams& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
std::unique_ptr<FreeTypeStream> face(new FreeTypeStream);
|
||||
std::unique_ptr<FreeTypeStream> face = std::make_unique<FreeTypeStream>();
|
||||
face->SetStream(stream);
|
||||
|
||||
if (!face->Open())
|
||||
{
|
||||
NazaraError("Failed to open face");
|
||||
return nullptr;
|
||||
}
|
||||
return Err(ResourceLoadingError::Unrecognized);
|
||||
|
||||
std::shared_ptr<Font> font = std::make_shared<Font>();
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -262,13 +262,10 @@ namespace Nz
|
|||
return m_currentFrame;
|
||||
}
|
||||
|
||||
bool Open()
|
||||
Result<void, ResourceLoadingError> 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<ImageStream> LoadGIFFile(const std::filesystem::path& filePath, const ImageStreamParams& /*parameters*/)
|
||||
Result<std::shared_ptr<ImageStream>, ResourceLoadingError> LoadGIFFile(const std::filesystem::path& filePath, const ImageStreamParams& /*parameters*/)
|
||||
{
|
||||
std::shared_ptr<GIFImageStream> gifStream = std::make_shared<GIFImageStream>();
|
||||
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<ImageStream> LoadGIFMemory(const void* ptr, std::size_t size, const ImageStreamParams& /*parameters*/)
|
||||
Result<std::shared_ptr<ImageStream>, ResourceLoadingError> LoadGIFMemory(const void* ptr, std::size_t size, const ImageStreamParams& /*parameters*/)
|
||||
{
|
||||
std::shared_ptr<GIFImageStream> gifStream = std::make_shared<GIFImageStream>();
|
||||
gifStream->SetMemory(ptr, size);
|
||||
|
||||
if (!gifStream->Open())
|
||||
return {};
|
||||
|
||||
return gifStream;
|
||||
Result status = gifStream->Open();
|
||||
return status.Map([&] { return std::move(gifStream); });
|
||||
}
|
||||
|
||||
std::shared_ptr<ImageStream> LoadGIFStream(Stream& stream, const ImageStreamParams& /*parameters*/)
|
||||
Result<std::shared_ptr<ImageStream>, ResourceLoadingError> LoadGIFStream(Stream& stream, const ImageStreamParams& /*parameters*/)
|
||||
{
|
||||
std::shared_ptr<GIFImageStream> gifStream = std::make_shared<GIFImageStream>();
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Mesh> LoadMD2(Stream& stream, const MeshParams& parameters)
|
||||
Result<std::shared_ptr<Mesh>, 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<std::shared_ptr<Animation>, 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<Animation> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<Mesh> LoadMD5Mesh(Stream& stream, const MeshParams& parameters)
|
||||
Result<std::shared_ptr<Mesh>, 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
#include <unordered_map>
|
||||
#include <Nazara/Utility/Debug.hpp>
|
||||
|
||||
///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<Mesh> LoadOBJ(Stream& stream, const MeshParams& parameters)
|
||||
Result<std::shared_ptr<Mesh>, 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> mesh = std::make_shared<Mesh>();
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<std::shared_ptr<Image>, 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<Image> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,37 +38,27 @@ namespace Nz
|
|||
|
||||
static stbi_io_callbacks s_stbiCallbacks = { StbiRead, StbiSkip, StbiEof };
|
||||
|
||||
constexpr auto s_supportedExtensions = frozen::make_unordered_set<frozen::string>({ "bmp", "gif", "hdr", "jpg", "jpeg", "pic", "png", "ppm", "pgm", "psd", "tga" });
|
||||
constexpr auto s_supportedExtensions = frozen::make_unordered_set<frozen::string>({ ".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<std::shared_ptr<Image>, 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<Image> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue