mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-26 01:20:25 +00:00
64598473cc
There's now two namespaces, RIFF (little-endian) and IFF (big-endian) which (for the most part) contain the same kinds of structures for handling similar data in both formats. (They also share almost all of their implementation) The main types are ChunkHeader and (Owned)Chunk. While Chunk has no ownership over the data it accesses (and can only be constructed from a byte view), OwnedChunk has ownership over this data and is aimed at reading from streams. OwnedList, implementing the standard RIFF LIST type, is currently only implemented for RIFF due to its only user being WAV, but it may be generalized in the future for use by IFF. Co-authored-by: Timothy Flynn <trflynn89@pm.me>
125 lines
3.6 KiB
C++
125 lines
3.6 KiB
C++
/*
|
|
* Copyright (c) 2023, kleines Filmröllchen <filmroellchen@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include "Details.h"
|
|
#include <AK/TypeCasts.h>
|
|
#include <LibRIFF/IFF.h>
|
|
#include <LibRIFF/RIFF.h>
|
|
|
|
namespace RIFF {
|
|
|
|
StringView ChunkID::as_ascii_string() const
|
|
{
|
|
return StringView { id_data.span() };
|
|
}
|
|
|
|
bool ChunkID::operator==(StringView other_string) const
|
|
{
|
|
return as_ascii_string() == other_string;
|
|
}
|
|
|
|
namespace Detail {
|
|
|
|
template<typename WordType>
|
|
auto ChunkHeader<WordType>::read_from_stream(Stream& stream) -> ErrorOr<ChunkHeader>
|
|
{
|
|
auto id = TRY(stream.read_value<RIFF::ChunkID>());
|
|
u32 size = TRY(stream.read_value<WordType>());
|
|
return ChunkHeader { id, size };
|
|
}
|
|
|
|
template<typename HeaderType>
|
|
auto FileHeader<HeaderType>::read_from_stream(Stream& stream) -> ErrorOr<FileHeader>
|
|
{
|
|
auto header = TRY(stream.read_value<HeaderType>());
|
|
auto subformat = TRY(stream.read_value<RIFF::ChunkID>());
|
|
return FileHeader { header, subformat };
|
|
}
|
|
|
|
template<typename HeaderType>
|
|
Chunk<HeaderType>::Chunk(HeaderType header, ReadonlyBytes data)
|
|
: m_header(header)
|
|
, m_data(data)
|
|
{
|
|
VERIFY(data.size() == header.size);
|
|
}
|
|
|
|
template<typename HeaderType>
|
|
FixedMemoryStream Chunk<HeaderType>::data_stream() const
|
|
{
|
|
return FixedMemoryStream { m_data };
|
|
}
|
|
|
|
template<typename HeaderType>
|
|
auto Chunk<HeaderType>::decode(ReadonlyBytes data) -> ErrorOr<Chunk>
|
|
{
|
|
auto data_stream = FixedMemoryStream { data };
|
|
auto header = TRY(HeaderType::read_from_stream(data_stream));
|
|
|
|
if (data.size() < sizeof(HeaderType) + header.size)
|
|
return Error::from_string_literal("Not enough data for IFF/RIFF chunk");
|
|
|
|
return Chunk { header, data.slice(sizeof(HeaderType), header.size) };
|
|
}
|
|
|
|
template<typename HeaderType>
|
|
auto Chunk<HeaderType>::decode_and_advance(ReadonlyBytes& data) -> ErrorOr<Chunk>
|
|
{
|
|
auto chunk = TRY(decode(data));
|
|
data = data.slice(sizeof(HeaderType) + chunk.size());
|
|
// add padding if needed
|
|
if (chunk.size() % 2 != 0) {
|
|
if (data.is_empty())
|
|
return Error::from_string_literal("Missing data for padding byte");
|
|
if (*data.data() != 0)
|
|
return Error::from_string_literal("Padding byte is not 0");
|
|
data = data.slice(1);
|
|
}
|
|
|
|
return chunk;
|
|
}
|
|
|
|
template<typename HeaderType>
|
|
OwnedChunk<HeaderType>::OwnedChunk(HeaderType header, Buffer backing_data)
|
|
: Chunk<HeaderType>(header, backing_data.span())
|
|
, m_backing_data(move(backing_data))
|
|
{
|
|
}
|
|
|
|
template<typename HeaderType>
|
|
auto OwnedChunk<HeaderType>::read_from_stream(Stream& stream) -> ErrorOr<OwnedChunk>
|
|
{
|
|
auto header = TRY(stream.read_value<HeaderType>());
|
|
|
|
auto data = TRY(Buffer::create_uninitialized(header.size));
|
|
TRY(stream.read_until_filled(data.span()));
|
|
|
|
// RIFF chunks may have trailing padding to align to x86 "words" (i.e. 2 bytes).
|
|
if (is<SeekableStream>(stream)) {
|
|
if (!stream.is_eof()) {
|
|
auto stream_position = TRY(static_cast<SeekableStream&>(stream).tell());
|
|
if (stream_position % 2 != 0)
|
|
TRY(static_cast<SeekableStream&>(stream).seek(1, SeekMode::FromCurrentPosition));
|
|
}
|
|
} else {
|
|
dbgln("RIFF Warning: Cannot align stream to 2-byte boundary, next chunk may be bogus!");
|
|
}
|
|
|
|
return OwnedChunk { header, data };
|
|
}
|
|
|
|
template class Chunk<IFF::ChunkHeader>;
|
|
template class Chunk<RIFF::ChunkHeader>;
|
|
template class OwnedChunk<IFF::ChunkHeader>;
|
|
template class OwnedChunk<RIFF::ChunkHeader>;
|
|
template struct ChunkHeader<IFF::WordType>;
|
|
template struct ChunkHeader<RIFF::WordType>;
|
|
template struct FileHeader<IFF::ChunkHeader>;
|
|
template struct FileHeader<RIFF::ChunkHeader>;
|
|
|
|
}
|
|
|
|
}
|