/* * 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 "../common/FileReader.h" #include "../common/mptCRC.h" #include "../common/mptIO.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 *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(); 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.GetPinnedRawDataView(); 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 &pageData) { return ReadPage(file, pageInfo, &pageData); } bool ReadPage(FileReader &file) { PageInfo pageInfo; return ReadPage(file, pageInfo); } bool ReadPageAndSkipJunk(FileReader &file, PageInfo &pageInfo, std::vector &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 &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