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
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace IoUtilities;
|
|
|
|
using namespace ConversionUtilities;
|
|
|
|
|
2018-03-06 23:09:15 +01:00
|
|
|
namespace TagParser {
|
2015-04-22 19:22:01 +02:00
|
|
|
|
|
|
|
/*!
|
2018-06-03 20:38:32 +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).
|
|
|
|
*/
|
|
|
|
void OggPage::parseHeader(istream &stream, uint64 startOffset, int32 maxSize)
|
|
|
|
{
|
|
|
|
// prepare reading
|
2018-06-02 22:56:08 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
// read segment size tabe
|
|
|
|
m_segmentSizes.push_back(0);
|
2018-03-07 01:17:50 +01:00
|
|
|
for (byte i = 0; i < m_segmentCount;) {
|
2015-04-22 19:22:01 +02:00
|
|
|
byte entry = reader.readByte();
|
|
|
|
maxSize -= entry;
|
|
|
|
m_segmentSizes.back() += entry;
|
2018-03-07 01:17:50 +01:00
|
|
|
if (++i < m_segmentCount && entry < 0xff) {
|
2015-04-22 19:22:01 +02:00
|
|
|
m_segmentSizes.push_back(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 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.
|
|
|
|
*/
|
|
|
|
uint32 OggPage::computeChecksum(istream &stream, uint64 startOffset)
|
|
|
|
{
|
2018-06-02 22:56:08 +02:00
|
|
|
stream.seekg(static_cast<streamoff>(startOffset));
|
2015-04-22 19:22:01 +02:00
|
|
|
uint32 crc = 0x0;
|
2015-08-16 23:39:42 +02:00
|
|
|
byte value, segmentTableSize = 0, segmentTableIndex = 0;
|
2018-03-07 01:17:50 +01:00
|
|
|
for (uint32 i = 0, segmentLength = 27; i != segmentLength; ++i) {
|
|
|
|
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);
|
2018-06-02 22:56:08 +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
|
2018-06-02 22:56:08 +02:00
|
|
|
segmentLength += (segmentTableSize = (value = static_cast<byte>(stream.get())));
|
2015-04-22 19:22:01 +02:00
|
|
|
break;
|
|
|
|
default:
|
2018-06-02 22:56:08 +02:00
|
|
|
value = static_cast<byte>(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.
|
|
|
|
*/
|
|
|
|
void OggPage::updateChecksum(iostream &stream, uint64 startOffset)
|
|
|
|
{
|
|
|
|
char buff[4];
|
|
|
|
LE::getBytes(computeChecksum(stream, startOffset), buff);
|
2018-06-02 22:56:08 +02:00
|
|
|
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.
|
|
|
|
*/
|
|
|
|
uint32 OggPage::makeSegmentSizeDenotation(ostream &stream, uint32 size)
|
|
|
|
{
|
|
|
|
uint32 bytesWritten = 1;
|
2018-03-07 01:17:50 +01:00
|
|
|
while (size >= 0xff) {
|
2018-06-02 22:56:08 +02:00
|
|
|
stream.put(static_cast<char>(0xff));
|
2015-04-22 19:22:01 +02:00
|
|
|
size -= 0xff;
|
|
|
|
++bytesWritten;
|
|
|
|
}
|
2018-06-02 22:56:08 +02:00
|
|
|
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
|