tagparser/ogg/oggpage.cpp

140 lines
4.5 KiB
C++
Raw Normal View History

2015-09-06 19:57:33 +02:00
#include "./oggpage.h"
2015-04-22 19:22:01 +02:00
2015-09-06 19:57:33 +02:00
#include "../exceptions.h"
2015-04-22 19:22:01 +02:00
#include <c++utilities/conversion/binaryconversion.h>
2018-03-07 01:17:50 +01:00
#include <c++utilities/io/binaryreader.h>
2015-04-22 19:22:01 +02:00
#include <limits>
2015-04-22 19:22:01 +02:00
using namespace std;
2019-06-10 22:49:11 +02:00
using namespace CppUtilities;
2015-04-22 19:22:01 +02:00
namespace TagParser {
2015-04-22 19:22:01 +02:00
/*!
* \class TagParser::OggPage
2015-04-22 19:22:01 +02:00
* \brief The OggPage class is used to parse OGG pages.
* \sa http://www.xiph.org/ogg/doc/framing.html
*/
/*!
* \brief Parses the header read from the specified \a stream at the specified \a startOffset.
* \throws Throws InvalidDataException if the capture pattern is not present.
* \throws Throws TruncatedDataException if the header is truncated (according to \a maxSize).
*/
2019-03-13 19:06:42 +01:00
void OggPage::parseHeader(istream &stream, std::uint64_t startOffset, std::int32_t maxSize)
2015-04-22 19:22:01 +02:00
{
// prepare reading
stream.seekg(static_cast<streamoff>(startOffset));
2015-04-22 19:22:01 +02:00
BinaryReader reader(&stream);
2018-03-07 01:17:50 +01:00
if (maxSize < 27) {
2015-04-22 19:22:01 +02:00
throw TruncatedDataException();
} else {
maxSize -= 27;
}
// read header values
2018-03-07 01:17:50 +01:00
if (reader.readUInt32LE() != 0x5367674f) {
2015-04-22 19:22:01 +02:00
throw InvalidDataException();
}
m_startOffset = startOffset;
m_streamStructureVersion = reader.readByte();
m_headerTypeFlag = reader.readByte();
m_absoluteGranulePosition = reader.readUInt64LE();
m_streamSerialNumber = reader.readUInt32LE();
m_sequenceNumber = reader.readUInt32LE();
m_checksum = reader.readUInt32LE();
m_segmentCount = reader.readByte();
m_segmentSizes.clear();
2018-03-07 01:17:50 +01:00
if (m_segmentCount > 0) {
if (maxSize < m_segmentCount) {
2015-04-22 19:22:01 +02:00
throw TruncatedDataException();
} else {
maxSize -= m_segmentCount;
}
2021-07-02 03:00:50 +02:00
// read segment size table
m_segmentSizes.emplace_back(0);
2019-03-13 19:06:42 +01:00
for (std::uint8_t i = 0; i < m_segmentCount;) {
std::uint8_t entry = reader.readByte();
2015-04-22 19:22:01 +02:00
maxSize -= entry;
m_segmentSizes.back() += entry;
if (++i < m_segmentCount && entry < 0xFF) {
m_segmentSizes.emplace_back(0);
} else if (i == m_segmentCount && entry == 0xFF) {
m_headerTypeFlag |= 0x80; // FIXME v11: don't abuse header type flags
2015-04-22 19:22:01 +02:00
}
}
// check whether the maximum size is exceeded
2018-03-07 01:17:50 +01:00
if (maxSize < 0) {
2015-04-22 19:22:01 +02:00
throw TruncatedDataException();
}
}
}
/*!
* \brief Computes the actual checksum of the page read from the specified \a stream
* at the specified \a startOffset.
*/
2019-03-13 19:06:42 +01:00
std::uint32_t OggPage::computeChecksum(istream &stream, std::uint64_t startOffset)
2015-04-22 19:22:01 +02:00
{
stream.seekg(static_cast<streamoff>(startOffset));
2019-03-13 19:06:42 +01:00
std::uint32_t crc = 0x0;
std::uint8_t value, segmentTableSize = 0, segmentTableIndex = 0;
for (std::uint32_t i = 0, segmentLength = 27; i != segmentLength; ++i) {
2018-03-07 01:17:50 +01:00
switch (i) {
2015-04-22 19:22:01 +02:00
case 22:
// bytes 22, 23, 24, 25 hold denoted checksum and must be set to zero
stream.seekg(4, ios_base::cur);
2019-06-12 20:40:45 +02:00
[[fallthrough]];
2018-03-07 01:17:50 +01:00
case 23:
case 24:
case 25:
2015-08-16 23:39:42 +02:00
value = 0;
2015-04-22 19:22:01 +02:00
break;
case 26:
// byte 26 holds the number of segment sizes
2019-03-13 19:06:42 +01:00
segmentLength += (segmentTableSize = (value = static_cast<std::uint8_t>(stream.get())));
2015-04-22 19:22:01 +02:00
break;
default:
2019-03-13 19:06:42 +01:00
value = static_cast<std::uint8_t>(stream.get());
2018-03-07 01:17:50 +01:00
if (i > 26 && segmentTableIndex < segmentTableSize) {
2015-04-22 19:22:01 +02:00
// bytes 27 to (27 + segment size count) hold page size
2015-08-16 23:39:42 +02:00
segmentLength += value;
++segmentTableIndex;
2015-04-22 19:22:01 +02:00
}
}
2015-10-16 21:46:53 +02:00
crc = (crc << 8) ^ BinaryReader::crc32Table[((crc >> 24) & 0xFF) ^ value];
2015-04-22 19:22:01 +02:00
}
return crc;
}
/*!
* \brief Updates the checksum of the page read from the specified \a stream
* at the specified \a startOffset.
*/
2019-03-13 19:06:42 +01:00
void OggPage::updateChecksum(iostream &stream, std::uint64_t startOffset)
2015-04-22 19:22:01 +02:00
{
char buff[4];
LE::getBytes(computeChecksum(stream, startOffset), buff);
stream.seekp(static_cast<streamoff>(startOffset + 22));
2015-04-22 19:22:01 +02:00
stream.write(buff, sizeof(buff));
}
/*!
* \brief Writes the segment size denotation for the specified segment \a size to the specified stream.
* \return Returns the number of bytes written.
*/
2019-03-13 19:06:42 +01:00
std::uint32_t OggPage::makeSegmentSizeDenotation(ostream &stream, std::uint32_t size)
2015-04-22 19:22:01 +02:00
{
2019-03-13 19:06:42 +01:00
std::uint32_t bytesWritten = 1;
2018-03-07 01:17:50 +01:00
while (size >= 0xff) {
stream.put(static_cast<char>(0xff));
2015-04-22 19:22:01 +02:00
size -= 0xff;
++bytesWritten;
}
stream.put(static_cast<char>(size));
2015-04-22 19:22:01 +02:00
return bytesWritten;
}
2018-03-07 01:17:50 +01:00
} // namespace TagParser