winamp/Src/external_dependencies/openmpt-trunk/soundlib/OggStream.cpp

214 lines
4.4 KiB
C++

/*
* OggStream.cpp
* -------------
* Purpose: Basic Ogg stream parsing functionality
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "OggStream.h"
#include "mpt/crc/crc.hpp"
#include "../common/FileReader.h"
OPENMPT_NAMESPACE_BEGIN
namespace Ogg
{
uint16 PageInfo::GetPagePhysicalSize() const
{
uint16 size = 0;
size += sizeof(PageHeader);
size += header.page_segments;
for(uint8 segment = 0; segment < header.page_segments; ++segment)
{
size += segment_table[segment];
}
return size;
}
uint16 PageInfo::GetPageHeaderSize() const
{
uint16 size = 0;
size += sizeof(PageHeader);
size += header.page_segments;
return size;
}
uint16 PageInfo::GetPageDataSize() const
{
uint16 size = 0;
for(uint8 segment = 0; segment < header.page_segments; ++segment)
{
size += segment_table[segment];
}
return size;
}
bool AdvanceToPageMagic(FileReader &file)
{
#if MPT_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable:4127) // conditional expression is constant
#endif // MPT_COMPILER_MSVC
while(true)
#if MPT_COMPILER_MSVC
#pragma warning(pop)
#endif // MPT_COMPILER_MSVC
{
if(!file.CanRead(4))
{
return false;
}
if(file.ReadMagic("OggS"))
{
file.SkipBack(4);
return true;
}
file.Skip(1);
}
}
bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector<uint8> *pageData)
{
pageInfo = PageInfo();
if(pageData)
{
(*pageData).clear();
}
if(!file.ReadMagic("OggS"))
{
return false;
}
file.SkipBack(4);
FileReader filePageReader = file; // do not modify original file read position
if(!filePageReader.ReadStruct(pageInfo.header))
{
return false;
}
if(!filePageReader.CanRead(pageInfo.header.page_segments))
{
return false;
}
uint16 pageDataSize = 0;
for(uint8 segment = 0; segment < pageInfo.header.page_segments; ++segment)
{
pageInfo.segment_table[segment] = filePageReader.ReadIntLE<uint8>();
pageDataSize += pageInfo.segment_table[segment];
}
if(!filePageReader.CanRead(pageDataSize))
{
return false;
}
if(pageData)
{
filePageReader.ReadVector(*pageData, pageDataSize);
} else
{
filePageReader.Skip(pageDataSize);
}
filePageReader.SkipBack(pageInfo.GetPagePhysicalSize());
{
mpt::crc32_ogg calculatedCRC;
uint8 rawHeader[sizeof(PageHeader)];
MemsetZero(rawHeader);
filePageReader.ReadArray(rawHeader);
std::memset(rawHeader + 22, 0, 4); // clear out old crc
calculatedCRC.process(rawHeader, rawHeader + sizeof(rawHeader));
filePageReader.Skip(pageInfo.header.page_segments);
calculatedCRC.process(pageInfo.segment_table, pageInfo.segment_table + pageInfo.header.page_segments);
if(pageData)
{
filePageReader.Skip(pageDataSize);
calculatedCRC.process(*pageData);
} else
{
FileReader pageDataReader = filePageReader.ReadChunk(pageDataSize);
auto pageDataView = pageDataReader.GetPinnedView();
calculatedCRC.process(pageDataView.GetSpan());
}
if(calculatedCRC != pageInfo.header.CRC_checksum)
{
return false;
}
}
file.Skip(pageInfo.GetPagePhysicalSize());
return true;
}
bool ReadPage(FileReader &file, PageInfo &pageInfo, std::vector<uint8> &pageData)
{
return ReadPage(file, pageInfo, &pageData);
}
bool ReadPage(FileReader &file)
{
PageInfo pageInfo;
return ReadPage(file, pageInfo);
}
bool ReadPageAndSkipJunk(FileReader &file, PageInfo &pageInfo, std::vector<uint8> &pageData)
{
pageInfo = PageInfo();
pageData.clear();
#if MPT_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable:4127) // conditional expression is constant
#endif // MPT_COMPILER_MSVC
while(true)
#if MPT_COMPILER_MSVC
#pragma warning(pop)
#endif // MPT_COMPILER_MSVC
{
if(!AdvanceToPageMagic(file))
{
return false;
}
if(ReadPage(file, pageInfo, pageData))
{
return true;
} else
{
pageInfo = PageInfo();
pageData.clear();
}
file.Skip(1);
}
}
bool UpdatePageCRC(PageInfo &pageInfo, const std::vector<uint8> &pageData)
{
if(pageData.size() != pageInfo.GetPageDataSize())
{
return false;
}
mpt::crc32_ogg crc;
pageInfo.header.CRC_checksum = 0;
char rawHeader[sizeof(PageHeader)];
std::memcpy(rawHeader, &pageInfo.header, sizeof(PageHeader));
crc.process(rawHeader, rawHeader + sizeof(PageHeader));
crc.process(pageInfo.segment_table, pageInfo.segment_table + pageInfo.header.page_segments);
crc.process(pageData);
pageInfo.header.CRC_checksum = crc;
return true;
}
} // namespace Ogg
OPENMPT_NAMESPACE_END