Major ResourceLoader rework (using Nz::Result)

This commit is contained in:
SirLynix
2022-09-07 13:31:04 +02:00
parent 131faa4fbd
commit 47cb878f9d
25 changed files with 587 additions and 800 deletions

View File

@@ -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 soundStream;
return status.Map([&] { return std::move(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 soundStream;
return status.Map([&] { return std::move(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;
}

View File

@@ -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 soundStream;
return status.Map([&] { return std::move(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 soundStream;
return status.Map([&] { return std::move(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.streamLoader = LoadFlacSoundBuffer;
loaderEntry.parameterFilter = [](const SoundBufferParams& parameters)
{
bool skip;
if (parameters.custom.GetBooleanParameter("SkipBuiltinFlacLoader", &skip) && skip)
return false;
return true;
};
return loaderEntry;
}
@@ -575,10 +534,17 @@ namespace Nz
{
SoundStreamLoader::Entry loaderEntry;
loaderEntry.extensionSupport = IsFlacSupported;
loaderEntry.streamChecker = [](Stream& stream, const SoundStreamParams& parameters) { return CheckFlac(stream, parameters); };
loaderEntry.fileLoader = LoadFlacSoundStreamFile;
loaderEntry.memoryLoader = LoadFlacSoundStreamMemory;
loaderEntry.streamLoader = LoadFlacSoundStreamStream;
loaderEntry.fileLoader = LoadFlacSoundStreamFile;
loaderEntry.memoryLoader = LoadFlacSoundStreamMemory;
loaderEntry.streamLoader = LoadFlacSoundStreamStream;
loaderEntry.parameterFilter = [](const SoundStreamParams& parameters)
{
bool skip;
if (parameters.custom.GetBooleanParameter("SkipBuiltinFlacLoader", &skip) && skip)
return false;
return true;
};
return loaderEntry;
}

View File

@@ -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>
@@ -75,16 +76,16 @@ namespace Nz
{
switch (errCode)
{
case 0: return "no error";
case 0: return "no error";
case OV_EBADHEADER: return "invalid Vorbis bitstream header";
case OV_EBADLINK: return "an invalid stream section was supplied to libvorbisfile, or the requested link is corrupt";
case OV_EFAULT: return "internal logic fault";
case OV_EINVAL: return "an invalid stream section was supplied to libvorbisfile, or the requested link is corrupt";
case OV_EBADLINK: return "an invalid stream section was supplied to libvorbisfile, or the requested link is corrupt";
case OV_EFAULT: return "internal logic fault";
case OV_EINVAL: return "an invalid stream section was supplied to libvorbisfile, or the requested link is corrupt";
case OV_ENOTVORBIS: return "bitstream does not contain any Vorbis data";
case OV_EREAD: return "a read from media returned an error";
case OV_EVERSION: return "Vorbis version mismatch";
case OV_HOLE: return "there was an interruption in the data";
default: return "unknown error";
case OV_EREAD: return "a read from media returned an error";
case OV_EVERSION: return "Vorbis version mismatch";
case OV_HOLE: return "there was an interruption in the data";
default: return "unknown error";
}
}
@@ -116,38 +117,19 @@ namespace Nz
return sampleCount - remainingBytes / sizeof(Int16);
}
bool IsVorbisSupported(const std::string_view& extension)
{
static std::set<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 soundStream;
return status.Map([&] { return std::move(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 soundStream;
return status.Map([&] { return std::move(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;
}

View File

@@ -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 soundStream;
return status.Map([&] { return std::move(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 soundStream;
return status.Map([&] { return std::move(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;
}

View File

@@ -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;
}

View File

@@ -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()

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;
}