/* * (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; }