/*
* (C) 2003-2006 Gabest
* (C) 2006-2013 see Authors.txt
*
* This file is part of MPC-HC.
*
* MPC-HC is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* MPC-HC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "stdafx.h"
#include "MatroskaFile.h"
#include "../../../DSUtil/DSUtil.h"
#include "zlib/zlib.h"
#define DOCTYPE _T("matroska")
#define DOCTYPE_WEBM _T("webm")
#define DOCTYPEVERSION 2
using namespace MatroskaReader;
#define BeginChunk \
CheckPointer(pMN0, E_POINTER); \
CAutoPtr pMN = pMN0->Child(); \
\
if (!pMN) \
return S_FALSE; \
do { \
switch (pMN->m_id) {
#define EndChunk \
} \
} \
while (pMN->Next()); \
\
return S_OK;
static void bswap(BYTE* s, int len)
{
for (BYTE* d = s + len - 1; s < d; s++, d--) {
*s ^= *d, *d ^= *s, *s ^= *d;
}
}
//
// CMatroskaFile
//
CMatroskaFile::CMatroskaFile(IAsyncReader* pAsyncReader, HRESULT& hr)
: CBaseSplitterFile(pAsyncReader, hr, DEFAULT_CACHE_LENGTH, false)
, m_rtOffset(0)
{
if (FAILED(hr)) {
return;
}
hr = Init();
}
HRESULT CMatroskaFile::Init()
{
DWORD dw;
if (FAILED(Read(dw)) || dw != 0x1A45DFA3) {
return E_FAIL;
}
CMatroskaNode Root(this);
if (FAILED(Parse(&Root))) {
return E_FAIL;
}
CAutoPtr pSegment, pCluster;
if ((pSegment = Root.Child(0x18538067))
&& (pCluster = pSegment->Child(0x1F43B675))) {
Cluster c0;
c0.ParseTimeCode(pCluster);
m_rtOffset = m_segment.GetRefTime(c0.TimeCode);
}
return S_OK;
}
template
HRESULT CMatroskaFile::Read(T& var)
{
HRESULT hr = ByteRead((BYTE*)&var, sizeof(var));
if (S_OK == hr) {
bswap((BYTE*)&var, sizeof(var));
}
return hr;
}
HRESULT CMatroskaFile::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0x1A45DFA3:
m_ebml.Parse(pMN);
if ((m_ebml.DocType != DOCTYPE || m_ebml.DocTypeReadVersion > DOCTYPEVERSION) && m_ebml.DocType != DOCTYPE_WEBM) {
return E_FAIL;
}
break;
case 0x18538067:
if (m_segment.SegmentInfo.SegmentUID.IsEmpty()) {
m_segment.ParseMinimal(pMN);
}
break;
EndChunk;
}
//
HRESULT EBML::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0x4286:
EBMLVersion.Parse(pMN);
break;
case 0x42F7:
EBMLReadVersion.Parse(pMN);
break;
case 0x42F2:
EBMLMaxIDLength.Parse(pMN);
break;
case 0x42F3:
EBMLMaxSizeLength.Parse(pMN);
break;
case 0x4282:
DocType.Parse(pMN);
break;
case 0x4287:
DocTypeVersion.Parse(pMN);
break;
case 0x4285:
DocTypeReadVersion.Parse(pMN);
break;
EndChunk;
}
HRESULT Segment::Parse(CMatroskaNode* pMN0)
{
pos = pMN0->GetPos();
BeginChunk;
case 0x1549A966:
SegmentInfo.Parse(pMN);
break;
case 0x114D9B74:
MetaSeekInfo.Parse(pMN);
break;
case 0x1654AE6B:
Tracks.Parse(pMN);
break;
case 0x1F43B675:
Clusters.Parse(pMN);
break;
case 0x1C53BB6B:
Cues.Parse(pMN);
break;
case 0x1941A469:
Attachments.Parse(pMN);
break;
case 0x1043A770:
Chapters.Parse(pMN);
break;
// case 0x1254C367: Tags.Parse(pMN); break;
EndChunk;
}
HRESULT Segment::ParseMinimal(CMatroskaNode* pMN0)
{
CheckPointer(pMN0, E_POINTER);
pos = pMN0->GetPos();
len = pMN0->m_len;
CAutoPtr pMN = pMN0->Child();
if (!pMN) {
return S_FALSE;
}
unsigned int k = 0;
do {
switch (pMN->m_id) {
case 0x1549A966:
SegmentInfo.Parse(pMN);
k |= (1 << 0);
break;
case 0x114D9B74:
MetaSeekInfo.Parse(pMN);
k |= (1 << 1);
break;
case 0x1654AE6B:
Tracks.Parse(pMN);
k |= (1 << 2);
break;
case 0x1C53BB6B:
k |= (1 << 3);
Cues.Parse(pMN);
break;
}
} while (k != 15 && pMN->m_id != 0x1F43B675 && pMN->Next());
if (!pMN->IsRandomAccess()) {
return S_OK;
}
while (MatroskaReader::QWORD pos = pMN->FindPos(0x114D9B74, pMN->GetPos())) {
pMN->SeekTo(pos);
if (FAILED(pMN->Parse())) {
break; // a broken file
}
MetaSeekInfo.Parse(pMN);
}
if (k != 15) {
if (Cues.IsEmpty() && (pMN = pMN0->Child(0x1C53BB6B, false))) {
do {
Cues.Parse(pMN);
} while (pMN->Next(true));
}
if (Chapters.IsEmpty() && (pMN = pMN0->Child(0x1043A770, false))) {
do {
Chapters.Parse(pMN); /*BIG UGLY HACK:*/
break;
} while (pMN->Next(true));
}
if (Attachments.IsEmpty() && (pMN = pMN0->Child(0x1941A469, false))) {
do {
Attachments.Parse(pMN); /*BIG UGLY HACK:*/
break;
} while (pMN->Next(true));
}
}
return S_OK;
}
UINT64 Segment::GetMasterTrack()
{
UINT64 TrackNumber = 0, AltTrackNumber = 0;
POSITION pos1 = Tracks.GetHeadPosition();
while (pos1 && TrackNumber == 0) {
Track* pT = Tracks.GetNext(pos1);
POSITION pos2 = pT->TrackEntries.GetHeadPosition();
while (pos2 && TrackNumber == 0) {
TrackEntry* pTE = pT->TrackEntries.GetNext(pos2);
if (pTE->TrackType == TrackEntry::TypeVideo) {
TrackNumber = pTE->TrackNumber;
break;
} else if (pTE->TrackType == TrackEntry::TypeAudio && AltTrackNumber == 0) {
AltTrackNumber = pTE->TrackNumber;
}
}
}
if (TrackNumber == 0) {
TrackNumber = AltTrackNumber;
}
if (TrackNumber == 0) {
TrackNumber = 1;
}
return TrackNumber;
}
ChapterAtom* ChapterAtom::FindChapterAtom(UINT64 id)
{
if (ChapterUID == id) {
return this;
}
POSITION pos = ChapterAtoms.GetHeadPosition();
while (pos) {
ChapterAtom* ca = ChapterAtoms.GetNext(pos)->FindChapterAtom(id);
if (ca) {
return ca;
}
}
return nullptr;
}
ChapterAtom* Segment::FindChapterAtom(UINT64 id, int nEditionEntry)
{
POSITION pos1 = Chapters.GetHeadPosition();
while (pos1) {
Chapter* c = Chapters.GetNext(pos1);
POSITION pos2 = c->EditionEntries.GetHeadPosition();
while (pos2) {
EditionEntry* ee = c->EditionEntries.GetNext(pos2);
if (nEditionEntry-- == 0) {
return id == 0 ? ee : ee->FindChapterAtom(id);
}
}
}
return nullptr;
}
HRESULT Info::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0x73A4:
SegmentUID.Parse(pMN);
break;
case 0x7384:
SegmentFilename.Parse(pMN);
break;
case 0x3CB923:
PrevUID.Parse(pMN);
break;
case 0x3C83AB:
PrevFilename.Parse(pMN);
break;
case 0x3EB923:
NextUID.Parse(pMN);
break;
case 0x3E83BB:
NextFilename.Parse(pMN);
break;
case 0x2AD7B1:
TimeCodeScale.Parse(pMN);
break;
case 0x4489:
Duration.Parse(pMN);
break;
case 0x4461:
DateUTC.Parse(pMN);
break;
case 0x7BA9:
Title.Parse(pMN);
break;
case 0x4D80:
MuxingApp.Parse(pMN);
break;
case 0x5741:
WritingApp.Parse(pMN);
break;
EndChunk;
}
HRESULT Seek::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0x4DBB:
SeekHeads.Parse(pMN);
break;
EndChunk;
}
HRESULT SeekHead::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0x53AB:
SeekID.Parse(pMN);
break;
case 0x53AC:
SeekPosition.Parse(pMN);
break;
EndChunk;
}
HRESULT Track::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0xAE:
TrackEntries.Parse(pMN);
break;
EndChunk;
}
HRESULT TrackEntry::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0xD7:
TrackNumber.Parse(pMN);
break;
case 0x73C5:
TrackUID.Parse(pMN);
break;
case 0x83:
TrackType.Parse(pMN);
break;
case 0xB9:
FlagEnabled.Parse(pMN);
break;
case 0x88:
FlagDefault.Parse(pMN);
break;
case 0x9C:
FlagLacing.Parse(pMN);
break;
case 0x55AA:
FlagForced.Parse(pMN);
break;
case 0x6DE7:
MinCache.Parse(pMN);
break;
case 0x6DF8:
MaxCache.Parse(pMN);
break;
case 0x536E:
Name.Parse(pMN);
break;
case 0x22B59C:
Language.Parse(pMN);
break;
case 0x86:
CodecID.Parse(pMN);
break;
case 0x63A2:
CodecPrivate.Parse(pMN);
break;
case 0x258688:
CodecName.Parse(pMN);
break;
case 0x3A9697:
CodecSettings.Parse(pMN);
break;
case 0x3B4040:
CodecInfoURL.Parse(pMN);
break;
case 0x26B240:
CodecDownloadURL.Parse(pMN);
break;
case 0xAA:
CodecDecodeAll.Parse(pMN);
break;
case 0x6FAB:
TrackOverlay.Parse(pMN);
break;
case 0x23E383:
case 0x2383E3:
DefaultDuration.Parse(pMN);
break;
case 0x23314F:
TrackTimecodeScale.Parse(pMN);
break;
case 0xE0:
if (S_OK == v.Parse(pMN)) {
DescType |= DescVideo;
}
break;
case 0xE1:
if (S_OK == a.Parse(pMN)) {
DescType |= DescAudio;
}
break;
case 0x6D80:
ces.Parse(pMN);
break;
EndChunk;
}
static int cesort(const void* a, const void* b)
{
UINT64 ce1 = (static_cast(const_cast(a)))->ContentEncodingOrder;
UINT64 ce2 = (static_cast(const_cast(b)))->ContentEncodingOrder;
return (int)ce1 - (int)ce2;
}
bool TrackEntry::Expand(CBinary& data, UINT64 Scope)
{
if (ces.ce.IsEmpty()) {
return true;
}
CAtlArray cearray;
POSITION pos = ces.ce.GetHeadPosition();
while (pos) {
cearray.Add(ces.ce.GetNext(pos));
}
qsort(cearray.GetData(), cearray.GetCount(), sizeof(ContentEncoding*), cesort);
for (int i = (int)cearray.GetCount() - 1; i >= 0; i--) {
ContentEncoding* ce = cearray[i];
if (!(ce->ContentEncodingScope & Scope)) {
continue;
}
if (ce->ContentEncodingType == ContentEncoding::Compression) {
if (!data.Decompress(ce->cc)) {
return false;
}
} else if (ce->ContentEncodingType == ContentEncoding::Encryption) {
// TODO
return false;
}
}
return true;
}
HRESULT Video::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0x9A:
FlagInterlaced.Parse(pMN);
break;
case 0x53B8:
StereoMode.Parse(pMN);
break;
case 0xB0:
PixelWidth.Parse(pMN);
if (!DisplayWidth) {
DisplayWidth.Set(PixelWidth);
}
break;
case 0xBA:
PixelHeight.Parse(pMN);
if (!DisplayHeight) {
DisplayHeight.Set(PixelHeight);
}
break;
case 0x54B0:
DisplayWidth.Parse(pMN);
break;
case 0x54BA:
DisplayHeight.Parse(pMN);
break;
case 0x54B2:
DisplayUnit.Parse(pMN);
break;
case 0x54B3:
AspectRatioType.Parse(pMN);
break;
case 0x54AA:
VideoPixelCropBottom.Parse(pMN);
if ((INT64)VideoPixelCropBottom < 0) {
VideoPixelCropBottom.Set(0); // fix bad value
}
break;
case 0x54BB:
VideoPixelCropTop.Parse(pMN);
if ((INT64)VideoPixelCropTop < 0) {
VideoPixelCropTop.Set(0); // fix bad value
}
break;
case 0x54CC:
VideoPixelCropLeft.Parse(pMN);
if ((INT64)VideoPixelCropLeft < 0) {
VideoPixelCropLeft.Set(0); // fix bad value
}
break;
case 0x54DD:
VideoPixelCropRight.Parse(pMN);
if ((INT64)VideoPixelCropRight < 0) {
VideoPixelCropRight.Set(0); // fix bad value
}
break;
case 0x2EB524:
ColourSpace.Parse(pMN);
break;
case 0x2FB523:
GammaValue.Parse(pMN);
break;
case 0x2383E3:
FramePerSec.Parse(pMN);
break;
EndChunk;
}
HRESULT Audio::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0xB5:
SamplingFrequency.Parse(pMN);
if (!OutputSamplingFrequency) {
OutputSamplingFrequency.Set(SamplingFrequency);
}
break;
case 0x78B5:
OutputSamplingFrequency.Parse(pMN);
break;
case 0x9F:
Channels.Parse(pMN);
break;
case 0x7D7B:
ChannelPositions.Parse(pMN);
break;
case 0x6264:
BitDepth.Parse(pMN);
break;
EndChunk;
}
HRESULT ContentEncodings::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0x6240:
ce.Parse(pMN);
break;
EndChunk;
}
HRESULT ContentEncoding::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0x5031:
ContentEncodingOrder.Parse(pMN);
break;
case 0x5032:
ContentEncodingScope.Parse(pMN);
break;
case 0x5033:
ContentEncodingType.Parse(pMN);
break;
case 0x5034:
cc.Parse(pMN);
break;
case 0x5035:
ce.Parse(pMN);
break;
EndChunk;
}
HRESULT ContentCompression::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0x4254:
ContentCompAlgo.Parse(pMN);
break;
case 0x4255:
ContentCompSettings.Parse(pMN);
break;
EndChunk;
}
HRESULT ContentEncryption::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0x47e1:
ContentEncAlgo.Parse(pMN);
break;
case 0x47e2:
ContentEncKeyID.Parse(pMN);
break;
case 0x47e3:
ContentSignature.Parse(pMN);
break;
case 0x47e4:
ContentSigKeyID.Parse(pMN);
break;
case 0x47e5:
ContentSigAlgo.Parse(pMN);
break;
case 0x47e6:
ContentSigHashAlgo.Parse(pMN);
break;
EndChunk;
}
HRESULT Cluster::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0xE7:
TimeCode.Parse(pMN);
break;
case 0xA7:
Position.Parse(pMN);
break;
case 0xAB:
PrevSize.Parse(pMN);
break;
case 0xA0:
BlockGroups.Parse(pMN, true);
break;
case 0xA3:
SimpleBlocks.Parse(pMN, true);
break;
EndChunk;
}
HRESULT Cluster::ParseTimeCode(CMatroskaNode* pMN0)
{
BeginChunk;
case 0xE7:
TimeCode.Parse(pMN);
return S_OK;
EndChunk;
}
HRESULT BlockGroup::Parse(CMatroskaNode* pMN0, bool fFull)
{
BeginChunk;
case 0xA1:
Block.Parse(pMN, fFull);
break;
case 0xA2: /* TODO: multiple virt blocks? */
;
break;
case 0x9B:
BlockDuration.Parse(pMN);
break;
case 0xFA:
ReferencePriority.Parse(pMN);
break;
case 0xFB:
ReferenceBlock.Parse(pMN);
break;
case 0xFD:
ReferenceVirtual.Parse(pMN);
break;
case 0xA4:
CodecState.Parse(pMN);
break;
case 0xE8:
TimeSlices.Parse(pMN);
break;
case 0x75A1:
if (fFull) {
ba.Parse(pMN);
}
break;
EndChunk;
}
HRESULT SimpleBlock::Parse(CMatroskaNode* pMN, bool fFull)
{
pMN->SeekTo(pMN->m_start);
TrackNumber.Parse(pMN);
CShort s;
s.Parse(pMN);
TimeCode.Set(s);
Lacing.Parse(pMN);
if (!fFull) {
return S_OK;
}
CAtlList lens;
MatroskaReader::QWORD tlen = 0;
MatroskaReader::QWORD FrameSize;
BYTE FramesInLaceLessOne;
switch ((Lacing & 0x06) >> 1) {
case 0:
// No lacing
lens.AddTail((pMN->m_start + pMN->m_len) - (pMN->GetPos() + tlen));
break;
case 1:
// Xiph lacing
BYTE n;
pMN->Read(n);
while (n-- > 0) {
BYTE b;
MatroskaReader::QWORD len = 0;
do {
pMN->Read(b);
len += b;
} while (b == 0xff);
lens.AddTail(len);
tlen += len;
}
lens.AddTail((pMN->m_start + pMN->m_len) - (pMN->GetPos() + tlen));
break;
case 2:
// Fixed-size lacing
pMN->Read(FramesInLaceLessOne);
FramesInLaceLessOne++;
FrameSize = ((pMN->m_start + pMN->m_len) - (pMN->GetPos() + tlen)) / FramesInLaceLessOne;
while (FramesInLaceLessOne-- > 0) {
lens.AddTail(FrameSize);
}
break;
case 3:
// EBML lacing
pMN->Read(FramesInLaceLessOne);
CLength FirstFrameSize;
FirstFrameSize.Parse(pMN);
lens.AddTail(FirstFrameSize);
FramesInLaceLessOne--;
tlen = FirstFrameSize;
CSignedLength DiffSize;
FrameSize = FirstFrameSize;
while (FramesInLaceLessOne--) {
DiffSize.Parse(pMN);
FrameSize += DiffSize;
lens.AddTail(FrameSize);
tlen += FrameSize;
}
lens.AddTail((pMN->m_start + pMN->m_len) - (pMN->GetPos() + tlen));
break;
}
POSITION pos = lens.GetHeadPosition();
while (pos) {
MatroskaReader::QWORD len = lens.GetNext(pos);
CAutoPtr p(DEBUG_NEW CBinary());
p->SetCount((INT_PTR)len);
pMN->Read(p->GetData(), len);
BlockData.AddTail(p);
}
return S_OK;
}
HRESULT BlockAdditions::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0xA6:
bm.Parse(pMN);
break;
EndChunk;
}
HRESULT BlockMore::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0xEE:
BlockAddID.Parse(pMN);
break;
case 0xA5:
BlockAdditional.Parse(pMN);
break;
EndChunk;
}
HRESULT TimeSlice::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0xCC:
LaceNumber.Parse(pMN);
break;
case 0xCD:
FrameNumber.Parse(pMN);
break;
case 0xCE:
Delay.Parse(pMN);
break;
case 0xCF:
Duration.Parse(pMN);
break;
EndChunk;
}
HRESULT Cue::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0xBB:
CuePoints.Parse(pMN);
break;
EndChunk;
}
HRESULT CuePoint::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0xB3:
CueTime.Parse(pMN);
break;
case 0xB7:
CueTrackPositions.Parse(pMN);
break;
EndChunk;
}
HRESULT CueTrackPosition::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0xF7:
CueTrack.Parse(pMN);
break;
case 0xF1:
CueClusterPosition.Parse(pMN);
break;
case 0x5387:
CueBlockNumber.Parse(pMN);
break;
case 0xEA:
CueCodecState.Parse(pMN);
break;
case 0xDB:
CueReferences.Parse(pMN);
break;
EndChunk;
}
HRESULT CueReference::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0x96:
CueRefTime.Parse(pMN);
break;
case 0x97:
CueRefCluster.Parse(pMN);
break;
case 0x535F:
CueRefNumber.Parse(pMN);
break;
case 0xEB:
CueRefCodecState.Parse(pMN);
break;
EndChunk;
}
HRESULT Attachment::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0x61A7:
AttachedFiles.Parse(pMN);
break;
EndChunk;
}
HRESULT AttachedFile::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0x467E:
FileDescription.Parse(pMN);
break;
case 0x466E:
FileName.Parse(pMN);
break;
case 0x4660:
FileMimeType.Parse(pMN);
break;
case 0x465C: // binary
FileDataLen = (INT_PTR)pMN->m_len;
FileDataPos = pMN->m_start;
break;
case 0x46AE:
FileUID.Parse(pMN);
break;
EndChunk;
}
HRESULT Chapter::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0x45B9:
EditionEntries.Parse(pMN);
break;
EndChunk;
}
HRESULT EditionEntry::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0xB6:
ChapterAtoms.Parse(pMN);
break;
EndChunk;
}
HRESULT ChapterAtom::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0x73C4:
ChapterUID.Parse(pMN);
break;
case 0x91:
ChapterTimeStart.Parse(pMN);
break;
case 0x92:
ChapterTimeEnd.Parse(pMN);
break;
// case 0x8F: // TODO
case 0x80:
ChapterDisplays.Parse(pMN);
break;
case 0xB6:
ChapterAtoms.Parse(pMN);
break;
case 0x98:
ChapterFlagHidden.Parse(pMN);
break;
case 0x4598:
ChapterFlagEnabled.Parse(pMN);
break;
EndChunk;
}
HRESULT ChapterDisplay::Parse(CMatroskaNode* pMN0)
{
BeginChunk;
case 0x85:
ChapString.Parse(pMN);
break;
case 0x437C:
ChapLanguage.Parse(pMN);
break;
case 0x437E:
ChapCountry.Parse(pMN);
break;
EndChunk;
}
//
HRESULT CBinary::Parse(CMatroskaNode* pMN)
{
ASSERT(pMN->m_len <= INT_MAX);
SetCount((INT_PTR)pMN->m_len);
return pMN->Read(GetData(), pMN->m_len);
}
bool CBinary::Compress(ContentCompression& cc)
{
if (cc.ContentCompAlgo == ContentCompression::ZLIB) {
int res;
z_stream c_stream;
c_stream.zalloc = (alloc_func)0;
c_stream.zfree = (free_func)0;
c_stream.opaque = (voidpf)0;
if (Z_OK != (res = deflateInit(&c_stream, 9))) {
return false;
}
c_stream.next_in = GetData();
c_stream.avail_in = (uInt)GetCount();
BYTE* dst = nullptr;
BYTE* newDst = nullptr;
int n = 0;
do {
newDst = (BYTE*)realloc(dst, ++n * 10);
if (newDst) {
dst = newDst;
c_stream.next_out = &dst[(n - 1) * 10];
c_stream.avail_out = 10;
}
if (!newDst || (Z_OK != (res = deflate(&c_stream, Z_FINISH)) && Z_STREAM_END != res)) {
free(dst);
return false;
}
} while (0 == c_stream.avail_out && Z_STREAM_END != res);
deflateEnd(&c_stream);
SetCount(c_stream.total_out);
memcpy(GetData(), dst, GetCount());
free(dst);
return true;
}
return false;
}
bool CBinary::Decompress(ContentCompression& cc)
{
if (cc.ContentCompAlgo == ContentCompression::ZLIB) {
int res;
z_stream d_stream;
d_stream.zalloc = (alloc_func)0;
d_stream.zfree = (free_func)0;
d_stream.opaque = (voidpf)0;
if (Z_OK != (res = inflateInit(&d_stream))) {
return false;
}
d_stream.next_in = GetData();
d_stream.avail_in = (uInt)GetCount();
BYTE* dst = nullptr;
BYTE* newDst = nullptr;
int n = 0;
do {
newDst = (BYTE*)realloc(dst, ++n * 1000);
if (newDst) {
dst = newDst;
d_stream.next_out = &dst[(n - 1) * 1000];
d_stream.avail_out = 1000;
}
if (!newDst || (Z_OK != (res = inflate(&d_stream, Z_NO_FLUSH)) && Z_STREAM_END != res)) {
free(dst);
return false;
}
} while (0 == d_stream.avail_out && 0 != d_stream.avail_in && Z_STREAM_END != res);
inflateEnd(&d_stream);
SetCount(d_stream.total_out);
memcpy(GetData(), dst, GetCount());
free(dst);
return true;
} else if (cc.ContentCompAlgo == ContentCompression::HDRSTRIP) {
InsertArrayAt(0, &cc.ContentCompSettings);
}
return false;
}
HRESULT CANSI::Parse(CMatroskaNode* pMN)
{
Empty();
MatroskaReader::QWORD len = pMN->m_len;
CHAR c;
while (len-- > 0 && SUCCEEDED(pMN->Read(c))) {
*this += c;
}
return (len == -1 ? S_OK : E_FAIL);
}
HRESULT CUTF8::Parse(CMatroskaNode* pMN)
{
Empty();
CAutoVectorPtr buff;
if (!buff.Allocate((UINT)pMN->m_len + 1) || S_OK != pMN->Read(buff, pMN->m_len)) {
return E_FAIL;
}
buff[pMN->m_len] = 0;
CStringW::operator = (UTF8To16((LPCSTR)(BYTE*)buff));
return S_OK;
}
HRESULT CUInt::Parse(CMatroskaNode* pMN)
{
m_val = 0;
for (int i = 0; i < (int)pMN->m_len; i++) {
m_val <<= 8;
HRESULT hr = pMN->Read(*(BYTE*)&m_val);
if (FAILED(hr)) {
return hr;
}
}
m_fValid = true;
return S_OK;
}
HRESULT CInt::Parse(CMatroskaNode* pMN)
{
m_val = 0;
for (int i = 0; i < (int)pMN->m_len; i++) {
HRESULT hr = pMN->Read(*((BYTE*)&m_val + 7 - i));
if (FAILED(hr)) {
return hr;
}
}
m_val >>= (8 - pMN->m_len) * 8;
m_fValid = true;
return S_OK;
}
HRESULT CFloat::Parse(CMatroskaNode* pMN)
{
HRESULT hr = E_FAIL;
m_val = 0;
if (pMN->m_len == 4) {
float val = 0;
hr = pMN->Read(val);
m_val = val;
} else if (pMN->m_len == 8) {
hr = pMN->Read(m_val);
}
if (SUCCEEDED(hr)) {
m_fValid = true;
}
return hr;
}
template
HRESULT CSimpleVar::Parse(CMatroskaNode* pMN)
{
m_val = 0;
m_fValid = true;
return pMN->Read(m_val);
}
HRESULT CID::Parse(CMatroskaNode* pMN)
{
m_val = 0;
BYTE b = 0;
HRESULT hr = pMN->Read(b);
if (FAILED(hr)) {
return hr;
}
int nMoreBytes = 0;
if ((b & 0x80) == 0x80) {
m_val = b & 0xff;
nMoreBytes = 0;
} else if ((b & 0xc0) == 0x40) {
m_val = b & 0x7f;
nMoreBytes = 1;
} else if ((b & 0xe0) == 0x20) {
m_val = b & 0x3f;
nMoreBytes = 2;
} else if ((b & 0xf0) == 0x10) {
m_val = b & 0x1f;
nMoreBytes = 3;
} else {
return E_FAIL;
}
while (nMoreBytes-- > 0) {
m_val <<= 8;
hr = pMN->Read(*(BYTE*)&m_val);
if (FAILED(hr)) {
return hr;
}
}
m_fValid = true;
return S_OK;
}
HRESULT CLength::Parse(CMatroskaNode* pMN)
{
m_val = 0;
BYTE b = 0;
HRESULT hr = pMN->Read(b);
if (FAILED(hr)) {
return hr;
}
int nMoreBytes = 0;
if ((b & 0x80) == 0x80) {
m_val = b & 0x7f;
nMoreBytes = 0;
} else if ((b & 0xc0) == 0x40) {
m_val = b & 0x3f;
nMoreBytes = 1;
} else if ((b & 0xe0) == 0x20) {
m_val = b & 0x1f;
nMoreBytes = 2;
} else if ((b & 0xf0) == 0x10) {
m_val = b & 0x0f;
nMoreBytes = 3;
} else if ((b & 0xf8) == 0x08) {
m_val = b & 0x07;
nMoreBytes = 4;
} else if ((b & 0xfc) == 0x04) {
m_val = b & 0x03;
nMoreBytes = 5;
} else if ((b & 0xfe) == 0x02) {
m_val = b & 0x01;
nMoreBytes = 6;
} else if ((b & 0xff) == 0x01) {
m_val = b & 0x00;
nMoreBytes = 7;
} else {
return E_FAIL;
}
//int nMoreBytesTmp = nMoreBytes;
MatroskaReader::QWORD UnknownSize = (1i64 << (7 * (nMoreBytes + 1))) - 1;
while (nMoreBytes-- > 0) {
m_val <<= 8;
hr = pMN->Read(*(BYTE*)&m_val);
if (FAILED(hr)) {
return hr;
}
}
if (m_val == UnknownSize) {
m_val = pMN->GetLength() - pMN->GetPos();
TRACE(_T("CLength: Unspecified chunk size at %I64d (corrected to %I64d)\n"), pMN->GetPos(), m_val);
}
if (m_fSigned) {
m_val -= (UnknownSize >> 1);
}
m_fValid = true;
return S_OK;
}
/*
HRESULT CSignedLength::Parse(CMatroskaNode* pMN)
{
// HRESULT hr = __super::Parse(pMN);
// if (FAILED(hr)) return hr;
m_val = 0;
BYTE b = 0;
HRESULT hr = pMN->Read(b);
if (FAILED(hr)) return hr;
int nMoreBytes = 0;
if ((b&0x80) == 0x80) {m_val = b&0x7f; nMoreBytes = 0;}
else if ((b&0xc0) == 0x40) {m_val = b&0x3f; nMoreBytes = 1;}
else if ((b&0xe0) == 0x20) {m_val = b&0x1f; nMoreBytes = 2;}
else if ((b&0xf0) == 0x10) {m_val = b&0x0f; nMoreBytes = 3;}
else if ((b&0xf8) == 0x08) {m_val = b&0x07; nMoreBytes = 4;}
else if ((b&0xfc) == 0x04) {m_val = b&0x03; nMoreBytes = 5;}
else if ((b&0xfe) == 0x02) {m_val = b&0x01; nMoreBytes = 6;}
else if ((b&0xff) == 0x01) {m_val = b&0x00; nMoreBytes = 7;}
else return E_FAIL;
//int nMoreBytesTmp = nMoreBytes;
MatroskaReader::QWORD UnknownSize = (1i64<<(7*(nMoreBytes+1)))-1;
while (nMoreBytes-- > 0)
{
m_val <<= 8;
hr = pMN->Read(*(BYTE*)&m_val);
if (FAILED(hr)) return hr;
}
if (m_val == UnknownSize)
{
m_val = pMN->GetLength() - pMN->GetPos();
TRACE(_T("CLength: Unspecified chunk size at %I64d (corrected to %I64d)\n"), pMN->GetPos(), m_val);
}
m_val -= (UnknownSize >> 1);
m_fValid = true;
return S_OK;
}
*/
template
HRESULT CNode::Parse(CMatroskaNode* pMN)
{
CAutoPtr p(DEBUG_NEW T());
HRESULT hr = E_OUTOFMEMORY;
if (!p || FAILED(hr = p->Parse(pMN))) {
return hr;
}
AddTail(p);
return S_OK;
}
HRESULT CBlockGroupNode::Parse(CMatroskaNode* pMN, bool fFull)
{
CAutoPtr p(DEBUG_NEW BlockGroup());
HRESULT hr = E_OUTOFMEMORY;
if (!p || FAILED(hr = p->Parse(pMN, fFull))) {
return hr;
}
AddTail(p);
return S_OK;
}
HRESULT CSimpleBlockNode::Parse(CMatroskaNode* pMN, bool fFull)
{
CAutoPtr p(DEBUG_NEW SimpleBlock());
HRESULT hr = E_OUTOFMEMORY;
if (!p || FAILED(hr = p->Parse(pMN, fFull))) {
return hr;
}
AddTail(p);
return S_OK;
}
///////////////////////////////
CMatroskaNode::CMatroskaNode(CMatroskaFile* pMF)
: m_pMF(pMF)
, m_pParent(nullptr)
{
ASSERT(m_pMF);
m_start = m_filepos = 0;
m_len.Set(m_pMF ? m_pMF->GetLength() : 0);
}
CMatroskaNode::CMatroskaNode(CMatroskaNode* pParent)
: m_pMF(pParent->m_pMF)
, m_pParent(pParent)
{
Parse();
}
HRESULT CMatroskaNode::Parse()
{
m_filepos = GetPos();
if (FAILED(m_id.Parse(this)) || FAILED(m_len.Parse(this))) {
return E_FAIL;
}
m_start = GetPos();
return S_OK;
}
CAutoPtr CMatroskaNode::Child(DWORD id, bool fSearch)
{
if (m_len == 0) {
return CAutoPtr();
}
SeekTo(m_start);
CAutoPtr pMN(DEBUG_NEW CMatroskaNode(this));
if (id && !pMN->Find(id, fSearch)) {
pMN.Free();
}
return pMN;
}
bool CMatroskaNode::Next(bool fSame)
{
if (!m_pParent) {
return false;
}
CID id = m_id;
while (m_start + m_len < m_pParent->m_start + m_pParent->m_len) {
SeekTo(m_start + m_len);
if (FAILED(Parse())) {
if (!Resync()) {
return false;
}
}
if (!fSame || m_id == id) {
return true;
}
}
return false;
}
bool CMatroskaNode::Find(DWORD id, bool fSearch)
{
MatroskaReader::QWORD pos = m_pParent && m_pParent->m_id == 0x18538067 /*segment?*/
? FindPos(id)
: 0;
if (pos) {
SeekTo(pos);
Parse();
} else if (fSearch) {
while (m_id != id && Next()) {
;
}
}
return (m_id == id);
}
void CMatroskaNode::SeekTo(MatroskaReader::QWORD pos)
{
m_pMF->Seek(pos);
}
MatroskaReader::QWORD CMatroskaNode::GetPos()
{
return m_pMF->GetPos();
}
MatroskaReader::QWORD CMatroskaNode::GetLength()
{
return m_pMF->GetLength();
}
template
HRESULT CMatroskaNode::Read(T& var)
{
return m_pMF->Read(var);
}
HRESULT CMatroskaNode::Read(BYTE* pData, MatroskaReader::QWORD len)
{
return m_pMF->ByteRead(pData, len);
}
MatroskaReader::QWORD CMatroskaNode::FindPos(DWORD id, MatroskaReader::QWORD start)
{
Segment& sm = m_pMF->m_segment;
POSITION pos = sm.MetaSeekInfo.GetHeadPosition();
while (pos) {
Seek* s = sm.MetaSeekInfo.GetNext(pos);
POSITION pos2 = s->SeekHeads.GetHeadPosition();
while (pos2) {
SeekHead* sh = s->SeekHeads.GetNext(pos2);
if (sh->SeekID == id && sh->SeekPosition + sm.pos >= start) {
return sh->SeekPosition + sm.pos;
}
}
}
return 0;
}
CAutoPtr CMatroskaNode::Copy()
{
CAutoPtr pNewNode(DEBUG_NEW CMatroskaNode(m_pMF));
pNewNode->m_pParent = m_pParent;
pNewNode->m_id.Set(m_id);
pNewNode->m_len.Set(m_len);
pNewNode->m_filepos = m_filepos;
pNewNode->m_start = m_start;
return pNewNode;
}
CAutoPtr CMatroskaNode::GetFirstBlock()
{
CAutoPtr pNode = Child();
do {
if (pNode->m_id == 0xA0 || pNode->m_id == 0xA3) {
return pNode;
}
} while (pNode->Next());
return CAutoPtr();
}
bool CMatroskaNode::NextBlock()
{
if (!m_pParent) {
return false;
}
CID id = m_id;
while (m_start + m_len < m_pParent->m_start + m_pParent->m_len) {
SeekTo(m_start + m_len);
if (FAILED(Parse())) {
if (!Resync()) {
return false;
}
}
if (m_id == 0xA0 || m_id == 0xA3) {
return true;
}
}
return false;
}
bool CMatroskaNode::Resync()
{
if (m_pParent->m_id == 0x18538067) { /*segment?*/
SeekTo(m_filepos);
for (BYTE b = 0; S_OK == Read(b); b = 0) {
if ((b & 0xf0) != 0x10) {
continue;
}
DWORD dw = b;
Read((BYTE*)&dw + 1, 3);
bswap((BYTE*)&dw, 4);
switch (dw) {
case 0x1549A966: // SegmentInfo
case 0x114D9B74: // MetaSeekInfo
case 0x1654AE6B: // Tracks
case 0x1F43B675: // Clusters
case 0x1C53BB6B: // Cues
case 0x1941A469: // Attachments
case 0x1043A770: // Chapters
case 0x1254C367: // Tags
SeekTo(GetPos() - 4);
return SUCCEEDED(Parse());
default:
SeekTo(GetPos() - 3);
break;
}
}
}
return false;
}