Author: jcsston
Date: 2005-04-09 22:52:52 +0400 (Sat, 09 Apr 2005)
New Revision: 1118
Added:
trunk/MatroskaUtils/GDC/GDCMatroska-Setup/
trunk/MatroskaUtils/GDC/GDCMatroska-Setup/GDCMatroska-Setup.vdproj
trunk/MatroskaUtils/GDC/GDCMatroska/MatroskaParser.c
trunk/MatroskaUtils/GDC/GDCMatroska/MatroskaParser.h
trunk/MatroskaUtils/GDC/GDCMatroska/Win32Stream.c
trunk/MatroskaUtils/GDC/GDCMatroska/Win32Stream.h
Modified:
trunk/MatroskaUtils/GDC/GDCMatroska/GDCMatroska.vcproj
trunk/MatroskaUtils/GDC/GDCMatroska/IndexMatroska.cpp
trunk/MatroskaUtils/GDC/GDCMatroska/IndexMatroska.h
trunk/MatroskaUtils/GDC/GoogleDesktopComponent.sln
trunk/MatroskaUtils/GDC/common/GoogleDesktopComponentCommon.vcproj
Log:
New Version of GDCMatroska
Submitted to Google
Modified: trunk/MatroskaUtils/GDC/GDCMatroska/GDCMatroska.vcproj
===================================================================
--- trunk/MatroskaUtils/GDC/GDCMatroska/GDCMatroska.vcproj 2005-04-09 18:30:20 UTC (rev 1117)
+++ trunk/MatroskaUtils/GDC/GDCMatroska/GDCMatroska.vcproj 2005-04-09 18:52:52 UTC (rev 1118)
@@ -75,7 +75,7 @@
AdditionalIncludeDirectories=".\;..\;..\..\;..\..\..\libebml;..\..\..\libebml\src\platform\win32;..\..\..\libmatroska"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;GDCMATROSKA_EXPORTS"
StringPooling="TRUE"
- RuntimeLibrary="2"
+ RuntimeLibrary="0"
UsePrecompiledHeader="3"
WarningLevel="3"
Detect64BitPortabilityProblems="TRUE"
@@ -84,7 +84,6 @@
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
- AdditionalDependencies="Unicows.lib"
OutputFile="$(OutDir)/GDCMatroska.dll"
LinkIncremental="1"
ModuleDefinitionFile="GDCMatroska.def"
@@ -149,6 +148,21 @@
RelativePath=".\IndexMatroska.cpp">
</File>
<File
+ RelativePath=".\MatroskaParser.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
RelativePath=".\stdafx.cpp">
<FileConfiguration
Name="Debug|Win32">
@@ -163,6 +177,21 @@
UsePrecompiledHeader="1"/>
</FileConfiguration>
</File>
+ <File
+ RelativePath=".\Win32Stream.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
</Filter>
<Filter
Name="Header Files"
@@ -172,8 +201,14 @@
RelativePath=".\IndexMatroska.h">
</File>
<File
+ RelativePath=".\MatroskaParser.h">
+ </File>
+ <File
RelativePath=".\stdafx.h">
</File>
+ <File
+ RelativePath=".\Win32Stream.h">
+ </File>
</Filter>
<Filter
Name="Resource Files"
Modified: trunk/MatroskaUtils/GDC/GDCMatroska/IndexMatroska.cpp
===================================================================
--- trunk/MatroskaUtils/GDC/GDCMatroska/IndexMatroska.cpp 2005-04-09 18:30:20 UTC (rev 1117)
+++ trunk/MatroskaUtils/GDC/GDCMatroska/IndexMatroska.cpp 2005-04-09 18:52:52 UTC (rev 1118)
@@ -17,7 +17,7 @@
CIndexMatroska::CIndexMatroska()
{
- m_hMatroskaPropDLL = NULL;
+
}
CString CIndexMatroska::GetRegistryValue(HKEY hBaseKey, TCHAR *reg_key, TCHAR *value_key, TCHAR *default_value)
@@ -38,42 +38,6 @@
return ret_value;
}
-HRESULT CIndexMatroska::LoadMatroskaPropLibrary()
-{
- m_hMatroskaPropDLL = LoadLibrary(_T("MatroskaProp"));
- if (m_hMatroskaPropDLL == NULL) {
- CString filename = GetRegistryValue(HKEY_CLASSES_ROOT, _T("CLSID\\") _T("{780BCB64-0CAF-473c-A9FC-E08C03D75515}") _T("\\InProcServer32\\"), _T(""), _T(""));
- if (filename.GetLength() > 0) {
- m_hMatroskaPropDLL = LoadLibrary(filename);
- }
- }
- if (m_hMatroskaPropDLL == NULL)
- return E_FAIL;
-
- fnCreateParserObject = (CreateParserObject_def)GetProcAddress(m_hMatroskaPropDLL, "CreateParserObject");
- if (fnCreateParserObject == NULL)
- return E_FAIL;
-
- fnDeleteParserObject = (DeleteParserObject_def)GetProcAddress(m_hMatroskaPropDLL, "DeleteParserObject");
- if (fnDeleteParserObject == NULL)
- return E_FAIL;
-
- /*
- fnFillParserObject = (FillParserObject_def)GetProcAddress(m_hMatroskaPropDLL, "FillParserObject");
- fnGetTrackCount = (GetTrackCount_def)GetProcAddress(m_hMatroskaPropDLL, "GetTrackCount");
- fnGetTrackUID = (GetTrackUID_def)GetProcAddress(m_hMatroskaPropDLL, "GetTrackUID");
- fnGetTrackType = (GetTrackType_def)GetProcAddress(m_hMatroskaPropDLL, "GetTrackType");
- fnGetTrackTypeLen = (GetTrackTypeLen_def)GetProcAddress(m_hMatroskaPropDLL, "GetTrackTypeLen");
- fnGetTrackTypeStr = (GetTrackTypeStr_def)GetProcAddress(m_hMatroskaPropDLL, "GetTrackTypeStr");
- fnGetTrackCodecIDLen = (GetTrackCodecIDLen_def)GetProcAddress(m_hMatroskaPropDLL, "GetTrackCodecIDLen");
- fnGetTrackCodecIDStr = (GetTrackCodecIDStr_def)GetProcAddress(m_hMatroskaPropDLL, "GetTrackCodecIDStr");
- fnGetTrackLength = (GetTrackLength_def)GetProcAddress(m_hMatroskaPropDLL, "GetTrackLength");
- fnGetTrackVideoInfo = (GetTrackVideoInfo_def)GetProcAddress(m_hMatroskaPropDLL, "GetTrackVideoInfo");
- fnGetTrackAudioInfo = (GetTrackAudioInfo_def)GetProcAddress(m_hMatroskaPropDLL, "GetTrackAudioInfo");
- */
- return S_OK;
-}
-
STDMETHODIMP CIndexMatroska::InterfaceSupportsErrorInfo(REFIID riid)
{
static const IID* arr[] =
@@ -134,72 +98,187 @@
CStringW content;
- MatroskaUtilsNamespace::MatroskaInfoParser *pParser = new MatroskaUtilsNamespace::MatroskaInfoParser(strOriginal);
- //hr = fnCreateParserObject((void **)&pParser, W2A(strOriginal));
- pParser->ParseFile();
- MatroskaUtilsNamespace::MatroskaTrackInfo *pVideoTrack = pParser->GetFirstVideoTrack();
- MatroskaUtilsNamespace::MatroskaTrackInfo *pAudioTrack = pParser->GetFirstAudioTrack();
- MatroskaUtilsNamespace::MatroskaTrackInfo *pSubtitleTrack = pParser->GetFirstSubtitleTrack();
-
- content = A2W(pParser->GetTextReport().c_str());
+ Win32Stream st;
+ MatroskaFile *mf;
+ char err_msg[256];
+ unsigned int i;
+
+ Win32Stream_Init(&st);
+ Win32Stream_OpenW(&st, strOriginal);
+
+ /* initialize matroska parser */
+ mf = mkv_OpenEx(&st.base, /* pointer to I/O object */
+ 0, /* starting position in the file */
+ 0, /* flags, you can set MKVF_AVOID_SEEKS if this is a non-seekable stream */
+ err_msg, sizeof(err_msg)); /* error message is returned here */
+ if (mf == NULL) {
+ Win32Stream_Close(&st);
+ //fprintf(stderr,"Can't parse Matroska file: %s\n", err_msg);
+ return E_FAIL;
+ }
+
+ int audio_Channels = -1;
+ int video_Height = -1;
+ int video_Width = -1;
+ double duration = 0.0;
+ CStringA s;
+
+ SegmentInfo *segInfo = mkv_GetFileInfo(mf);
+ if (segInfo != NULL) {
+ duration = (double)segInfo->Duration;
+
+ char szBuffer[256];
+ szBuffer[0] = 0;
+ for (int i = 0; i < sizeof(segInfo->UID); i++) {
+ size_t len = strlen(szBuffer);
+ _snprintf(&szBuffer[len], 255-len, " 0x%02x", (unsigned char)segInfo->UID[i]);
+ }
+
+ s.Format("<ul>"
+ "<li><b>Segment Info</b></li>"
+ "<ul>"
+ "<li>Segment UID:%s</li>"
+ "<li>Muxing App: %s</li>"
+ "<li>Writing App: %s</li>"
+ "<li>Title: %s</li>"
+ "<li>Filename: %s</li>"
+ "</ul>"
+ "</ul>",
+ szBuffer,
+ segInfo->MuxingApp,
+ segInfo->WritingApp,
+ segInfo->Title != NULL ? segInfo->Title : "(null)",
+ segInfo->Filename != NULL ? segInfo->Filename : "(null)");
+ content += CA2W(s);
+ }
+ s.Format("<ul>"
+ "<li><b>Track Info (Count: %u)</b></li>", mkv_GetNumTracks(mf));
+ content += CA2W(s);
+ /* if track_num was not specified, then list tracks */
+ for (i = 0; i < mkv_GetNumTracks(mf); ++i) {
+ TrackInfo *ti = mkv_GetTrackInfo(mf, i);
+ if (ti->Type == TT_VIDEO) {
+ if (video_Width == -1) {
+ video_Height = ti->Video.PixelHeight;
+ video_Width = ti->Video.PixelWidth;
+ }
+ s.Format("<ul>"
+ "<li><b>Track %u</b></li>"
+ "<ul>"
+ "<li>Type: Video</li>"
+ "<li>CodecID: %s</li>"
+ "<li>Language: %s</li>"
+ "<li>Pixel Size: %ix%i</li>"
+ "<li>Display Size: %ix%i</li>"
+ "<li>Enabled: %s</li>"
+ "<li>Default: %s</li>"
+ "<li>Default Duration: %.2fms (%.3f fps)</li>"
+ "</ul>"
+ "</ul>",
+ ti->Number,
+ (ti->CodecID != NULL) ? ti->CodecID : "(null)",
+ (ti->Language != NULL) ? ti->Language : "(null)",
+ ti->Video.PixelWidth, ti->Video.PixelHeight,
+ ti->Video.DisplayWidth, ti->Video.DisplayHeight,
+ ti->Enabled ? "True" : "False",
+ ti->Default ? "True" : "False",
+ (double)ti->DefaultDuration / 1000000.0, 1000000000.0 / (double)ti->DefaultDuration);
+ content += CA2W(s);
+
+ } else if (ti->Type == TT_AUDIO) {
+ if (audio_Channels == -1) {
+ audio_Channels = ti->Audio.Channels;
+ }
+ s.Format("<ul>"
+ "<li><b>Track %u</b></li>"
+ "<ul>"
+ "<li>Type: Audio</li>"
+ "<li>CodecID: %s</li>"
+ "<li>Language: %s</li>"
+ "<li>Channels: %i</li>"
+ "<li>Sampling Rate: %.2f</li>"
+ "<li>Enabled: %s</li>"
+ "<li>Default: %s</li>"
+ "<li>Default Duration: %.2fms (%.3f fps)</li>"
+ "</ul>"
+ "</ul>", ti->Number,
+ (ti->CodecID != NULL) ? ti->CodecID : "(null)",
+ (ti->Language != NULL) ? ti->Language : "(null)",
+ ti->Audio.Channels,
+ ti->Audio.SamplingFreq,
+ ti->Enabled ? "True" : "False",
+ ti->Default ? "True" : "False",
+ (double)ti->DefaultDuration / 1000000.0, 1000000000.0 / (double)ti->DefaultDuration);
+ content += CA2W(s);
+
+ } else if (ti->Type == TT_SUB) {
+ s.Format("<ul>"
+ "<li><b>Track %u</b></li>"
+ "<ul>"
+ "<li>Type: Subtitle</li>"
+ "<li>CodecID: %s</li>"
+ "<li>Language: %s</li>"
+ "<li>Enabled: %s</li>"
+ "<li>Default: %s</li>"
+ "<li>Default Duration: %.2fms (%.3f fps)</li>"
+ "</ul>"
+ "</ul>",
+ ti->Number,
+ (ti->CodecID != NULL) ? ti->CodecID : "(null)",
+ (ti->Language != NULL) ? ti->Language : "(null)",
+ ti->Enabled ? "True" : "False",
+ ti->Default ? "True" : "False",
+ (double)ti->DefaultDuration / 1000000.0, 1000000000.0 / (double)ti->DefaultDuration);
+ content += CA2W(s);
+
+ } else {
+ s.Format("<ul>"
+ "<li><b>Track %u</b></li>"
+ "<ul>"
+ "<li>Type: %i</li>"
+ "<li>CodecID: %s</li>"
+ "<li>Language: %s</li>"
+ "<li>Enabled: %s</li>"
+ "<li>Default: %s</li>"
+ "<li>Default Duration: %.2fms (%.3f fps)</li>"
+ "</ul>"
+ "</ul>",
+ ti->Number,
+ (ti->CodecID != NULL) ? ti->CodecID : "(null)",
+ ti->Type, (ti->Language != NULL) ? ti->Language : "(null)",
+ ti->Enabled ? "True" : "False",
+ ti->Default ? "True" : "False",
+ (double)ti->DefaultDuration / 1000000.0, 1000000000.0 / (double)ti->DefaultDuration);
+ content += CA2W(s);
+ }
+ }
+ s.Format("</ul>");
+ content += CA2W(s);
+
hr = spEvent->AddProperty(CComBSTR(L"content"), CComVariant(content));
- hr = spEvent->AddProperty(CComBSTR(L"format"), CComVariant("text/plain"));
+ hr = spEvent->AddProperty(CComBSTR(L"format"), CComVariant("text/html"));
- double duration = pParser->GetSegmentInfo()->GetDuration();
duration = duration * 1000.0 * 1000.0;
CComVariant llDuration;
llDuration = (ULONG)duration;
llDuration.ChangeType(VT_UI8);
hr = spEvent->AddProperty(CComBSTR(L"length"), llDuration);
- if (pVideoTrack != NULL && pVideoTrack->GetVideoInfo() != NULL) {
- MatroskaUtilsNamespace::MatroskaVideoTrackInfo *info = pVideoTrack->GetVideoInfo();
- hr = spEvent->AddProperty(CComBSTR(L"width"), CComVariant((unsigned int)info->video_Width, VT_UI4));
- hr = spEvent->AddProperty(CComBSTR(L"height"), CComVariant((unsigned int)info->video_Height, VT_UI4));
+ if (video_Width != -1) {
+ hr = spEvent->AddProperty(CComBSTR(L"width"), CComVariant((unsigned int)video_Width, VT_UI4));
+ hr = spEvent->AddProperty(CComBSTR(L"height"), CComVariant((unsigned int)video_Height, VT_UI4));
}
- if (pAudioTrack != NULL && pAudioTrack->GetAudioInfo() != NULL) {
- MatroskaUtilsNamespace::MatroskaAudioTrackInfo *info = pAudioTrack->GetAudioInfo();
- hr = spEvent->AddProperty(CComBSTR(L"channels"), CComVariant((unsigned int)info->audio_Channels, VT_UI4));
+ if (audio_Channels != -1) {
+ hr = spEvent->AddProperty(CComBSTR(L"channels"), CComVariant((unsigned int)audio_Channels, VT_UI4));
}
- UTFstring s;
- std::string s2;
- int t;
+ /* close matroska parser */
+ mkv_Close(mf);
+
+ /* close file */
+ Win32Stream_Close(&st);
- s = pParser->GetTagComment();
- if (s.length() > 0) {
- hr = spEvent->AddProperty(CComBSTR(L"comments"), CComVariant(s.c_str()));
- }
-
- s = pParser->GetTagAlbum();
- if (s.length() > 0) {
- hr = spEvent->AddProperty(CComBSTR(L"album_title"), CComVariant(s.c_str()));
- }
-
- s = pParser->GetTagArtist();
- if (s.length() > 0) {
- hr = spEvent->AddProperty(CComBSTR(L"artist"), CComVariant(s.c_str()));
- }
-
- t = pParser->GetTagDiscTrack();
- if (t > 0) {
- hr = spEvent->AddProperty(CComBSTR(L"track_number"), CComVariant((unsigned int)t, VT_UI4));
- }
-
- t = pParser->GetTagYear();
- if (t > 0) {
- hr = spEvent->AddProperty(CComBSTR(L"year_published"), CComVariant((unsigned int)t, VT_UI4));
- }
-
- s2 = pParser->GetTagGenre();
- if (s2.length() > 0) {
- hr = spEvent->AddProperty(CComBSTR(L"genre"), CComVariant(s2.c_str()));
- }
-
- //hr = fnDeleteParserObject((void **)&pParser);
- delete pParser;
-
if (FAILED(hr))
return Error(L"AddProperty failed", IID_IIndexMatroska, hr);
Modified: trunk/MatroskaUtils/GDC/GDCMatroska/IndexMatroska.h
===================================================================
--- trunk/MatroskaUtils/GDC/GDCMatroska/IndexMatroska.h 2005-04-09 18:30:20 UTC (rev 1117)
+++ trunk/MatroskaUtils/GDC/GDCMatroska/IndexMatroska.h 2005-04-09 18:52:52 UTC (rev 1118)
@@ -4,23 +4,9 @@
#include "resource.h" // main symbols
#include "GDCMatroska_h.h"
-#include "MatroskaUtils.h"
+#include "MatroskaParser.h"
+#include "Win32Stream.h"
-typedef LONG (WINAPI *CreateParserObject_def)(void **, char *);
-typedef LONG (WINAPI *FillParserObject_def)(void **, bool parseTags, bool parseWholeFile);
-typedef LONG (WINAPI *DeleteParserObject_def)(void **);
-typedef LONG (WINAPI *GetTrackCount_def)(void **);
-typedef LONG (WINAPI *GetTrackUID_def)(void **, int);
-typedef LONG (WINAPI *GetTrackType_def)(void **, int);
-typedef LONG (WINAPI *GetTrackTypeLen_def)(void **, int);
-typedef LONG (WINAPI *GetTrackTypeStr_def)(void **, int, char *);
-typedef LONG (WINAPI *GetTrackCodecIDLen_def)(void **, int);
-typedef LONG (WINAPI *GetTrackCodecIDStr_def)(void **, int, char *);
-typedef LONG (WINAPI *GetTrackLength_def)(void **, int);
-typedef LONG (WINAPI *GetTrackVideoInfo_def)(void **, int, void *);
-typedef LONG (WINAPI *GetTrackAudioInfo_def)(void **, int, void *);
-typedef LONG (WINAPI *SetAR_def)(void **, int, int, int);
-
// CIndexShortcut
class ATL_NO_VTABLE CIndexMatroska :
@@ -45,31 +31,14 @@
DECLARE_PROTECT_FINAL_CONSTRUCT()
CString GetRegistryValue(HKEY hBaseKey, TCHAR *reg_key, TCHAR *value_key, TCHAR *default_value);
- HRESULT LoadMatroskaPropLibrary();
HRESULT FinalConstruct() {
- LoadMatroskaPropLibrary();
return S_OK;
}
void FinalRelease() {
- FreeLibrary(m_hMatroskaPropDLL);
}
- CreateParserObject_def fnCreateParserObject;
- FillParserObject_def fnFillParserObject;
- DeleteParserObject_def fnDeleteParserObject;
- GetTrackCount_def fnGetTrackCount;
- GetTrackUID_def fnGetTrackUID;
- GetTrackType_def fnGetTrackType;
- GetTrackTypeLen_def fnGetTrackTypeLen;
- GetTrackTypeStr_def fnGetTrackTypeStr;
- GetTrackCodecIDLen_def fnGetTrackCodecIDLen;
- GetTrackCodecIDStr_def fnGetTrackCodecIDStr;
- GetTrackLength_def fnGetTrackLength;
- GetTrackVideoInfo_def fnGetTrackVideoInfo;
- GetTrackAudioInfo_def fnGetTrackAudioInfo;
- HMODULE m_hMatroskaPropDLL;
public:
// IIndexShortcut implementation
STDMETHOD(HandleFile)(BSTR full_path_to_file, IDispatch *event_factory);
Added: trunk/MatroskaUtils/GDC/GDCMatroska/MatroskaParser.c
===================================================================
--- trunk/MatroskaUtils/GDC/GDCMatroska/MatroskaParser.c 2005-04-09 18:30:20 UTC (rev 1117)
+++ trunk/MatroskaUtils/GDC/GDCMatroska/MatroskaParser.c 2005-04-09 18:52:52 UTC (rev 1118)
@@ -0,0 +1,3099 @@
+/*
+ * Copyright (c) 2004-2005 Mike Matsnev. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Absolutely no warranty of function or purpose is made by the author
+ * Mike Matsnev.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: MatroskaParser.c,v 1.31 2005/03/07 11:22:16 mike Exp $
+ *
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <setjmp.h>
+
+#ifdef _WIN32
+// MS names some functions differently
+#define alloca _alloca
+#define inline __inline
+
+#include <tchar.h>
+#endif
+
+#ifndef EVCBUG
+#define EVCBUG
+#endif
+
+#include "MatroskaParser.h"
+
+#ifdef MATROSKA_COMPRESSION_SUPPORT
+#include <zlib.h>
+#endif
+
+#define EBML_VERSION 1
+#define EBML_MAX_ID_LENGTH 4
+#define EBML_MAX_SIZE_LENGTH 8
+#define MATROSKA_VERSION 1
+#define MATROSKA_DOCTYPE "matroska"
+
+#define MAX_STRING_LEN 1023
+#define QSEGSIZE 512
+#define MAX_TRACKS 32
+#define MAX_READAHEAD (512*1024)
+
+#define DEFAULT_PAGE_SIZE 65536
+#define MIN_PAGE_SIZE 4096
+#define MIN_PAGES 2
+#define MAXCLUSTER (64*1048576)
+#define MAXFRAME (4*1048576)
+
+#ifdef WIN32
+#define LL(x) x##i64
+#define ULL(x) x##ui64
+#else
+#define LL(x) x##ll
+#define ULL(x) x##ull
+#endif
+
+#define MAXU64 ULL(0xffffffffffffffff)
+#define ONE ULL(1)
+
+// compatibility
+static char *mystrdup(struct InputStream *is,const char *src) {
+ size_t len;
+ char *dst;
+
+ if (src==NULL)
+ return NULL;
+
+ len = strlen(src);
+ dst = is->memalloc(is,len+1);
+ if (dst==NULL)
+ return NULL;
+
+ memcpy(dst,src,len+1);
+
+ return dst;
+}
+
+#ifdef _WIN32
+static void strlcpy(char *dst,const char *src,unsigned size) {
+ unsigned i;
+
+ for (i=0;i+1<size && src[i];++i)
+ dst[i] = src[i];
+ if (i<size)
+ dst[i] = 0;
+}
+#endif
+
+struct Cue {
+ ulonglong Time;
+ ulonglong Position;
+ ulonglong Block;
+ unsigned char Track;
+};
+
+struct QueueEntry {
+ struct QueueEntry *next;
+ unsigned int Length;
+
+ ulonglong Start;
+ ulonglong End;
+ ulonglong Position;
+
+ unsigned int flags;
+};
+
+struct Queue {
+ struct QueueEntry *head;
+ struct QueueEntry *tail;
+};
+
+#define MPF_ERROR 0x10000
+#define IBSZ 1024
+
+#define RBRESYNC 1
+
+struct MatroskaFile {
+ // parser config
+ unsigned flags;
+
+ // input
+ InputStream *cache;
+
+ // internal buffering
+ char inbuf[IBSZ];
+ ulonglong bufbase; // file offset of the first byte in buffer
+ int bufpos; // current read position in buffer
+ int buflen; // valid bytes in buffer
+
+ // error reporting
+ char errmsg[128];
+ jmp_buf jb;
+
+ // pointers to key elements
+ ulonglong pSegment;
+ ulonglong pSeekHead;
+ ulonglong pSegmentInfo;
+ ulonglong pCluster;
+ ulonglong pTracks;
+ ulonglong pCues;
+ ulonglong pAttachments;
+ ulonglong pChapters;
+ ulonglong pTags;
+
+ // flags for key elements
+ struct {
+ unsigned int SegmentInfo:1;
+ unsigned int Cluster:1;
+ unsigned int Tracks:1;
+ unsigned int Cues:1;
+ unsigned int Attachments:1;
+ unsigned int Chapters:1;
+ unsigned int Tags:1;
+ } seen;
+
+ // file info
+ ulonglong firstTimecode;
+
+ // SegmentInfo
+ struct SegmentInfo Seg;
+
+ // Tracks
+ unsigned int nTracks,nTracksSize;
+ struct TrackInfo **Tracks;
+
+ // Queues
+ struct QueueEntry *QFreeList;
+ unsigned int nQBlocks,nQBlocksSize;
+ struct QueueEntry **QBlocks;
+ struct Queue *Queues;
+ ulonglong readPosition;
+ unsigned int trackMask;
+ ulonglong pSegmentTop; // offset of next byte after the segment
+ ulonglong tcCluster; // current cluster timecode
+
+ // Cues
+ unsigned int nCues,nCuesSize;
+ struct Cue *Cues;
+
+ // Attachments
+ unsigned int nAttachments,nAttachmentsSize;
+ struct Attachment *Attachments;
+
+ // Chapters
+ unsigned int nChapters,nChaptersSize;
+ struct Chapter *Chapters;
+
+ // Tags
+ unsigned int nTags,nTagsSize;
+ struct Tag *Tags;
+};
+
+///////////////////////////////////////////////////////////////////////////
+// error reporting
+static void myvsnprintf_string(char **pdest,char *de,const char *str) {
+ char *dest = *pdest;
+
+ while (dest < de && *str)
+ *dest++ = *str++;
+
+ *pdest = dest;
+}
+
+static void myvsnprintf_uint_impl(char **pdest,char *de,int width,int zero,
+ int neg,unsigned base,int letter,
+ int ms,ulonglong val)
+{
+ char *dest = *pdest;
+ char tmp[21]; /* enough for 64 bit ints */
+ char *np = tmp + sizeof(tmp);
+ int rw,pad,trail;
+ char pc = zero ? '0' : ' ';
+
+ *--np = '\0';
+ while (val != 0) {
+ int rem = (int)(val % base);
+ val = val / base;
+
+ *--np = rem < 10 ? rem + '0' : rem - 10 + letter;
+ }
+
+ rw = (int)(tmp - np + sizeof(tmp) - 1);
+ if (ms)
+ ++rw;
+
+ pad = trail = 0;
+
+ if (rw < width)
+ pad = width - rw;
+
+ if (neg)
+ trail = pad, pad = 0;
+
+ if (dest < de && ms)
+ *dest++ = '-';
+
+ while (dest < de && pad--)
+ *dest++ = pc;
+
+ while (dest < de && *np)
+ *dest++ = *np++;
+
+ while (dest < de && trail--)
+ *dest++ = ' ';
+
+ *pdest = dest;
+}
+
+static void myvsnprintf_uint(char **pdest,char *de,int width,int zero,
+ int neg,unsigned base,int letter,
+ ulonglong val)
+{
+ myvsnprintf_uint_impl(pdest,de,width,zero,neg,base,letter,0,val);
+}
+
+static void myvsnprintf_int(char **pdest,char *de,int width,int zero,
+ int neg,unsigned base,int letter,
+ longlong val)
+{
+ if (val < 0)
+ myvsnprintf_uint_impl(pdest,de,width,zero,neg,base,letter,1,-val);
+ else
+ myvsnprintf_uint_impl(pdest,de,width,zero,neg,base,letter,0,val);
+}
+
+static void myvsnprintf(char *dest,unsigned dsize,const char *fmt,va_list ap) {
+ // s,d,x,u,ll
+ char *de = dest + dsize - 1;
+ int state = 0, width, zero, neg, ll;
+
+ if (dsize <= 1) {
+ if (dsize > 0)
+ *dest = '\0';
+ return;
+ }
+
+ while (*fmt && dest < de)
+ switch (state) {
+ case 0:
+ if (*fmt == '%') {
+ ++fmt;
+ state = 1;
+ width = zero = neg = ll = 0;
+ } else
+ *dest++ = *fmt++;
+ break;
+ case 1:
+ if (*fmt == '-') {
+ neg = 1;
+ ++fmt;
+ state = 2;
+ break;
+ }
+ state = 2;
+ case 2:
+ if (*fmt >= '0' && *fmt <= '9') {
+ width = width * 10 + *fmt++ - '0';
+ break;
+ }
+ state = 3;
+ case 3:
+ if (*fmt == 'l') {
+ ++ll;
+ ++fmt;
+ break;
+ }
+ state = 4;
+ case 4:
+ switch (*fmt) {
+ case 's':
+ myvsnprintf_string(&dest,de,va_arg(ap,const char *));
+ break;
+ case 'd':
+ switch (ll) {
+ case 0:
+ myvsnprintf_int(&dest,de,width,zero,neg,10,'a',va_arg(ap,int));
+ break;
+ case 1:
+ myvsnprintf_int(&dest,de,width,zero,neg,10,'a',va_arg(ap,long));
+ break;
+ case 2:
+ myvsnprintf_int(&dest,de,width,zero,neg,10,'a',va_arg(ap,longlong));
+ break;
+ }
+ break;
+ case 'u':
+ switch (ll) {
+ case 0:
+ myvsnprintf_uint(&dest,de,width,zero,neg,10,'a',va_arg(ap,unsigned int));
+ break;
+ case 1:
+ myvsnprintf_uint(&dest,de,width,zero,neg,10,'a',va_arg(ap,unsigned long));
+ break;
+ case 2:
+ myvsnprintf_uint(&dest,de,width,zero,neg,10,'a',va_arg(ap,ulonglong));
+ break;
+ }
+ break;
+ case 'x':
+ switch (ll) {
+ case 0:
+ myvsnprintf_uint(&dest,de,width,zero,neg,16,'a',va_arg(ap,unsigned int));
+ break;
+ case 1:
+ myvsnprintf_uint(&dest,de,width,zero,neg,16,'a',va_arg(ap,unsigned long));
+ break;
+ case 2:
+ myvsnprintf_uint(&dest,de,width,zero,neg,16,'a',va_arg(ap,ulonglong));
+ break;
+ }
+ break;
+ case 'X':
+ switch (ll) {
+ case 0:
+ myvsnprintf_uint(&dest,de,width,zero,neg,16,'A',va_arg(ap,unsigned int));
+ break;
+ case 1:
+ myvsnprintf_uint(&dest,de,width,zero,neg,16,'A',va_arg(ap,unsigned long));
+ break;
+ case 2:
+ myvsnprintf_uint(&dest,de,width,zero,neg,16,'A',va_arg(ap,ulonglong));
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ ++fmt;
+ state = 0;
+ break;
+ default:
+ state = 0;
+ break;
+ }
+ *dest = '\0';
+}
+
+static void errorjmp(MatroskaFile *mf,const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ myvsnprintf(mf->errmsg,sizeof(mf->errmsg),fmt,ap);
+ va_end(ap);
+
+ mf->flags |= MPF_ERROR;
+
+ longjmp(mf->jb,1);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// arrays
+static void *ArrayAlloc(MatroskaFile *mf,void **base,
+ unsigned *cur,unsigned *max,unsigned elem_size)
+{
+ if (*cur>=*max) {
+ void *np;
+ unsigned newsize = *max * 2;
+ if (newsize==0)
+ newsize = 1;
+
+ np = mf->cache->memrealloc(mf->cache,*base,newsize*elem_size);
+ if (np==NULL)
+ errorjmp(mf,"Out of memory in ArrayAlloc");
+
+ *base = np;
+ *max = newsize;
+ }
+
+ return (char*)*base + elem_size * (*cur)++;
+}
+
+static void ArrayReleaseMemory(MatroskaFile *mf,void **base,
+ unsigned cur,unsigned *max,unsigned elem_size)
+{
+ if (cur<*max) {
+ void *np = mf->cache->memrealloc(mf->cache,*base,cur*elem_size);
+ *base = np;
+ *max = cur;
+ }
+}
+
+
+#define ASGET(f,s,name) ArrayAlloc((f),(void**)&(s)->name,&(s)->n##name,&(s)->n##name##Size,sizeof(*((s)->name)))
+#define AGET(f,name) ArrayAlloc((f),(void**)&(f)->name,&(f)->n##name,&(f)->n##name##Size,sizeof(*((f)->name)))
+#define ARELEASE(f,s,name) ArrayReleaseMemory((f),(void**)&(s)->name,(s)->n##name,&(s)->n##name##Size,sizeof(*((s)->name)))
+
+///////////////////////////////////////////////////////////////////////////
+// queues
+static struct QueueEntry *QPut(struct Queue *q,struct QueueEntry *qe) {
+ if (q->tail)
+ q->tail->next = qe;
+ qe->next = NULL;
+ q->tail = qe;
+ if (q->head==NULL)
+ q->head = qe;
+
+ return qe;
+}
+
+static struct QueueEntry *QGet(struct Queue *q) {
+ struct QueueEntry *qe = q->head;
+ if (qe == NULL)
+ return NULL;
+ q->head = qe->next;
+ if (q->tail == qe)
+ q->tail = NULL;
+ return qe;
+}
+
+static struct QueueEntry *QAlloc(MatroskaFile *mf) {
+ struct QueueEntry *qe,**qep;
+ if (mf->QFreeList == NULL) {
+ unsigned i;
+
+ qep = AGET(mf,QBlocks);
+
+ *qep = mf->cache->memalloc(mf->cache,QSEGSIZE * sizeof(*qe));
+ if (*qep == NULL)
+ errorjmp(mf,"Ouf of memory");
+
+ qe = *qep;
+
+ for (i=0;i<QSEGSIZE-1;++i)
+ qe[i].next = qe+i+1;
+ qe[QSEGSIZE-1].next = NULL;
+
+ mf->QFreeList = qe;
+ }
+
+ qe = mf->QFreeList;
+ mf->QFreeList = qe->next;
+
+ return qe;
+}
+
+static inline void QFree(MatroskaFile *mf,struct QueueEntry *qe) {
+ qe->next = mf->QFreeList;
+ mf->QFreeList = qe;
+}
+
+// fill the buffer at current position
+static void fillbuf(MatroskaFile *mf) {
+ int rd;
+
+ // advance buffer pointers
+ mf->bufbase += mf->buflen;
+ mf->buflen = mf->bufpos = 0;
+
+ // get the relevant page
+ rd = mf->cache->read(mf->cache, mf->bufbase, mf->inbuf, IBSZ);
+ if (rd<0)
+ errorjmp(mf,"I/O Error: %s",mf->cache->geterror(mf->cache));
+
+ mf->buflen = rd;
+}
+
+// fill the buffer and return next char
+static int nextbuf(MatroskaFile *mf) {
+ fillbuf(mf);
+
+ if (mf->bufpos < mf->buflen)
+ return (unsigned char)(mf->inbuf[mf->bufpos++]);
+
+ return EOF;
+}
+
+static inline int readch(MatroskaFile *mf) {
+ return mf->bufpos < mf->buflen ? (unsigned char)(mf->inbuf[mf->bufpos++]) : nextbuf(mf);
+}
+
+static inline ulonglong filepos(MatroskaFile *mf) {
+ return mf->bufbase + mf->bufpos;
+}
+
+static void readbytes(MatroskaFile *mf,void *buffer,int len) {
+ char *cp = buffer;
+ int nb = mf->buflen - mf->bufpos;
+
+ if (nb > len)
+ nb = len;
+
+ memcpy(cp, mf->inbuf + mf->bufpos, nb);
+ mf->bufpos += nb;
+ len -= nb;
+ cp += nb;
+
+ if (len>0) {
+ mf->bufbase += mf->buflen;
+ mf->bufpos = mf->buflen = 0;
+
+ nb = mf->cache->read(mf->cache, mf->bufbase, cp, len);
+ if (nb<0)
+ errorjmp(mf,"I/O Error: %s",mf->cache->geterror(mf->cache));
+ if (nb != len)
+ errorjmp(mf,"Short read: got %d bytes of %d",nb,len);
+ mf->bufbase += len;
+ }
+}
+
+static void skipbytes(MatroskaFile *mf,ulonglong len) {
+ int nb = mf->buflen - mf->bufpos;
+
+ if (nb > len)
+ nb = (int)len;
+
+ mf->bufpos += nb;
+ len -= nb;
+
+ if (len>0) {
+ mf->bufbase += mf->buflen;
+ mf->bufpos = mf->buflen = 0;
+
+ mf->bufbase += len;
+ }
+}
+
+static void seek(MatroskaFile *mf,ulonglong pos) {
+ // see if pos is inside buffer
+ if (pos>=mf->bufbase && pos<mf->bufbase+mf->buflen)
+ mf->bufpos = (unsigned)(pos - mf->bufbase);
+ else {
+ // invalidate buffer and set pointer
+ mf->bufbase = pos;
+ mf->buflen = mf->bufpos = 0;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// floating point
+static inline MKFLOAT mkfi(int i) {
+#ifdef MATROSKA_INTEGER_ONLY
+ MKFLOAT f;
+ f.v = (longlong)i << 32;
+ return f;
+#else
+ return i;
+#endif
+}
+
+static inline longlong mul3(MKFLOAT scale,longlong tc) {
+#ifdef MATROSKA_INTEGER_ONLY
+ // x1 x0
+ // y1 y0
+ // --------------
+ // x0*y0
+ // x1*y0
+ // x0*y1
+ // x1*y1
+ // --------------
+ // .. r1 r0 ..
+ //
+ // r = ((x0*y0) >> 32) + (x1*y0) + (x0*y1) + ((x1*y1) << 32)
+ unsigned x0,x1,y0,y1;
+ ulonglong p;
+ char sign = 0;
+
+ if (scale.v < 0)
+ sign = !sign, scale.v = -scale.v;
+ if (tc < 0)
+ sign = !sign, tc = -tc;
+
+ x0 = (unsigned)scale.v;
+ x1 = (unsigned)((ulonglong)scale.v >> 32);
+ y0 = (unsigned)tc;
+ y1 = (unsigned)((ulonglong)tc >> 32);
+
+ p = (ulonglong)x0*y0 >> 32;
+ p += (ulonglong)x0*y1;
+ p += (ulonglong)x1*y0;
+ p += (ulonglong)(x1*y1) << 32;
+
+ return p;
+#else
+ return (longlong)(scale * tc);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////
+// EBML support
+static int readID(MatroskaFile *mf) {
+ int c1,c2,c3,c4;
+
+ c1 = readch(mf);
+ if (c1 == EOF)
+ return EOF;
+
+ if (c1 & 0x80)
+ return c1;
+
+ if ((c1 & 0xf0) == 0)
+ errorjmp(mf,"Invalid first byte of EBML ID: %02X",c1);
+
+ c2 = readch(mf);
+ if (c2 == EOF)
+fail:
+ errorjmp(mf,"Got EOF while reading EBML ID");
+
+ if ((c1 & 0xc0) == 0x40)
+ return (c1<<8) | c2;
+
+ c3 = readch(mf);
+ if (c3 == EOF)
+ goto fail;
+
+ if ((c1 & 0xe0) == 0x20)
+ return (c1<<16) | (c2<<8) | c3;
+
+ c4 = readch(mf);
+ if (c4 == EOF)
+ goto fail;
+
+ if ((c1 & 0xf0) == 0x10)
+ return (c1<<24) | (c2<<16) | (c3<<8) | c4;
+
+ return 0; // NOT REACHED
+}
+
+static ulonglong readVLUIntImp(MatroskaFile *mf,int *mask) {
+ int c,d,m;
+ ulonglong v = 0;
+
+ c = readch(mf);
+ if (c == EOF)
+ return EOF;
+
+ if (c == 0)
+ errorjmp(mf,"Invalid first byte of EBML integer: 0");
+
+ for (m=0;;++m) {
+ if (c & (0x80 >> m)) {
+ c &= 0x7f >> m;
+ if (mask)
+ *mask = m;
+ return v | ((ulonglong)c << m*8);
+ }
+ d = readch(mf);
+ if (d == EOF)
+ errorjmp(mf,"Got EOF while reading EBML unsigned integer");
+ v = (v<<8) | d;
+ }
+ // NOT REACHED
+}
+
+static inline ulonglong readVLUInt(MatroskaFile *mf) {
+ return readVLUIntImp(mf,NULL);
+}
+
+static ulonglong readSize(MatroskaFile *mf) {
+ int m;
+ ulonglong v = readVLUIntImp(mf,&m);
+
+ // see if it's unspecified
+ if (v == (MAXU64 >> (57-m*7)))
+ errorjmp(mf,"Unspecified element size is not supported here.");
+
+ return v;
+}
+
+static inline longlong readVLSInt(MatroskaFile *mf) {
+ static longlong bias[8] = { (ONE<<6)-1, (ONE<<13)-1, (ONE<<20)-1, (ONE<<27)-1,
+ (ONE<<34)-1, (ONE<<41)-1, (ONE<<48)-1, (ONE<<55)-1 };
+
+ int m;
+ longlong v = readVLUIntImp(mf,&m);
+
+ return v - bias[m];
+}
+
+static ulonglong readUInt(MatroskaFile *mf,unsigned int len) {
+ int c;
+ unsigned int m = len;
+ ulonglong v = 0;
+
+ if (len==0)
+ return v;
+ if (len>8)
+ errorjmp(mf,"Unsupported integer size in readUInt: %u",len);
+
+ do {
+ c = readch(mf);
+ if (c == EOF)
+ errorjmp(mf,"Got EOF while reading EBML unsigned integer");
+ v = (v<<8) | c;
+ } while (--m);
+
+ return v;
+}
+
+static inline longlong readSInt(MatroskaFile *mf,unsigned int len) {
+ longlong v = readUInt(mf,(unsigned)len);
+ int s = 64 - (len<<3);
+ return (v << s) >> s;
+}
+
+static MKFLOAT readFloat(MatroskaFile *mf,unsigned int len) {
+#ifdef MATROSKA_INTEGER_ONLY
+ MKFLOAT f;
+ int shift;
+#else
+ union {
+ unsigned int ui;
+ ulonglong ull;
+ float f;
+ double d;
+ } u;
+#endif
+
+ if (len!=4 && len!=8)
+ errorjmp(mf,"Invalid float size in readFloat: %u",len);
+
+#ifdef MATROSKA_INTEGER_ONLY
+ if (len == 4) {
+ unsigned ui = (unsigned)readUInt(mf,(unsigned)len);
+ f.v = (ui & 0x7fffff) | 0x800000;
+ if (ui & 0x80000000)
+ f.v = -f.v;
+ shift = (ui >> 23) & 0xff;
+ if (shift == 0) // assume 0
+zero:
+ shift = 0, f.v = 0;
+ else if (shift == 255)
+inf:
+ if (ui & 0x80000000)
+ f.v = LL(0x8000000000000000);
+ else
+ f.v = LL(0x7fffffffffffffff);
+ else {
+ shift += -127 + 9;
+ if (shift > 39)
+ goto inf;
+shift:
+ if (shift < 0)
+ f.v = f.v >> -shift;
+ else if (shift > 0)
+ f.v = f.v << shift;
+ }
+ } else if (len == 8) {
+ ulonglong ui = readUInt(mf,(unsigned)len);
+ f.v = (ui & LL(0xfffffffffffff)) | LL(0x10000000000000);
+ if (ui & 0x80000000)
+ f.v = -f.v;
+ shift = (int)((ui >> 52) & 0x7ff);
+ if (shift == 0) // assume 0
+ goto zero;
+ else if (shift == 2047)
+ goto inf;
+ else {
+ shift += -1023 - 20;
+ if (shift > 10)
+ goto inf;
+ goto shift;
+ }
+ }
+
+ return f;
+#else
+ if (len==4) {
+ u.ui = (unsigned int)readUInt(mf,(unsigned)len);
+ return u.f;
+ }
+
+ if (len==8) {
+ u.ull = readUInt(mf,(unsigned)len);
+ return u.d;
+ }
+
+ return 0;
+#endif
+}
+
+static void readString(MatroskaFile *mf,ulonglong len,char *buffer,int buflen) {
+ int nread;
+
+ if (buflen<1)
+ errorjmp(mf,"Invalid buffer size in readString: %d",buflen);
+
+ nread = buflen - 1;
+
+ if (nread > len)
+ nread = (int)len;
+
+ readbytes(mf,buffer,nread);
+ len -= nread;
+
+ if (len>0)
+ skipbytes(mf,len);
+
+ buffer[nread] = '\0';
+}
+
+///////////////////////////////////////////////////////////////////////////
+// file parser
+#define FOREACH(f,tl) \
+ { \
+ ulonglong tmplen = (tl); \
+ { \
+ ulonglong start = filepos(f); \
+ ulonglong cur,len; \
+ int id; \
+ for (;;) { \
+ cur = filepos(mf); \
+ if (cur == start + tmplen) \
+ break; \
+ id = readID(f); \
+ if (id==EOF) \
+ errorjmp(mf,"Unexpected EOF while reading EBML container"); \
+ len = readSize(mf); \
+ switch (id) {
+
+#define ENDFOR1(f) \
+ default: \
+ skipbytes(f,len); \
+ break; \
+ }
+#define ENDFOR2() \
+ } \
+ } \
+ }
+
+#define ENDFOR(f) ENDFOR1(f) ENDFOR2()
+
+#define myalloca(f,c) alloca(c)
+#define STRGETF(f,v,len,func) \
+ { \
+ char *TmpVal; \
+ unsigned TmpLen = (len)>MAX_STRING_LEN ? MAX_STRING_LEN : (unsigned)(len); \
+ TmpVal = func(f->cache,TmpLen+1); \
+ if (TmpVal == NULL) \
+ errorjmp(mf,"Out of memory"); \
+ readString(f,len,TmpVal,TmpLen+1); \
+ (v) = TmpVal; \
+ }
+
+#define STRGETA(f,v,len) STRGETF(f,v,len,myalloca)
+#define STRGETM(f,v,len) STRGETF(f,v,len,f->cache->memalloc)
+
+static int IsWritingApp(MatroskaFile *mf,const char *str) {
+ const char *cp = mf->Seg.WritingApp;
+ if (!cp)
+ return 0;
+
+ while (*str && *str++==*cp++) ;
+
+ return !*str;
+}
+
+static void parseEBML(MatroskaFile *mf,ulonglong toplen) {
+ ulonglong v;
+ char buf[32];
+
+ FOREACH(mf,toplen)
+ case 0x4286: // Version
+ v = readUInt(mf,(unsigned)len);
+ break;
+ case 0x42f7: // ReadVersion
+ v = readUInt(mf,(unsigned)len);
+ if (v > EBML_VERSION)
+ errorjmp(mf,"File requires version %d EBML parser",(int)v);
+ break;
+ case 0x42f2: // MaxIDLength
+ v = readUInt(mf,(unsigned)len);
+ if (v > EBML_MAX_ID_LENGTH)
+ errorjmp(mf,"File has identifiers longer than %d",(int)v);
+ break;
+ case 0x42f3: // MaxSizeLength
+ v = readUInt(mf,(unsigned)len);
+ if (v > EBML_MAX_SIZE_LENGTH)
+ errorjmp(mf,"File has integers longer than %d",(int)v);
+ break;
+ case 0x4282: // DocType
+ readString(mf,len,buf,sizeof(buf));
+ if (strcmp(buf,MATROSKA_DOCTYPE))
+ errorjmp(mf,"Unsupported DocType: %s",buf);
+ break;
+ case 0x4287: // DocTypeVersion
+ v = readUInt(mf,(unsigned)len);
+ break;
+ case 0x4285: // DocTypeReadVersion
+ v = readUInt(mf,(unsigned)len);
+ if (v > MATROSKA_VERSION)
+ errorjmp(mf,"File requires version %d Matroska parser",(int)v);
+ break;
+ ENDFOR(mf);
+}
+
+static void parseSeekEntry(MatroskaFile *mf,ulonglong toplen) {
+ int seekid = 0;
+ ulonglong pos = (ulonglong)-1;
+
+ FOREACH(mf,toplen)
+ case 0x53ab: // SeekID
+ if (len>EBML_MAX_ID_LENGTH)
+ errorjmp(mf,"Invalid ID size in parseSeekEntry: %d\n",(int)len);
+ seekid = (int)readUInt(mf,(unsigned)len);
+ break;
+ case 0x53ac: // SeekPos
+ pos = readUInt(mf,(unsigned)len);
+ break;
+ ENDFOR(mf);
+
+ if (pos == (ulonglong)-1)
+ errorjmp(mf,"Invalid element position in parseSeekEntry");
+
+ pos += mf->pSegment;
+ switch (seekid) {
+ case 0x114d9b74: // next SeekHead
+ if (mf->pSeekHead)
+ errorjmp(mf,"SeekHead contains more than one SeekHead pointer");
+ mf->pSeekHead = pos;
+ break;
+ case 0x1549a966: // SegmentInfo
+ mf->pSegmentInfo = pos;
+ break;
+ case 0x1f43b675: // Cluster
+ if (!mf->pCluster)
+ mf->pCluster = pos;
+ break;
+ case 0x1654ae6b: // Tracks
+ mf->pTracks = pos;
+ break;
+ case 0x1c53bb6b: // Cues
+ mf->pCues = pos;
+ break;
+ case 0x1941a469: // Attachments
+ mf->pAttachments = pos;
+ break;
+ case 0x1043a770: // Chapters
+ mf->pChapters = pos;
+ break;
+ case 0x1254c367: // tags
+ mf->pTags = pos;
+ break;
+ }
+}
+
+static void parseSeekHead(MatroskaFile *mf,ulonglong toplen) {
+ FOREACH(mf,toplen)
+ case 0x4dbb:
+ parseSeekEntry(mf,len);
+ break;
+ ENDFOR(mf);
+}
+
+static void parseSegmentInfo(MatroskaFile *mf,ulonglong toplen) {
+ MKFLOAT duration;
+
+ if (mf->seen.SegmentInfo) {
+ skipbytes(mf,toplen);
+ return;
+ }
+
+ mf->seen.SegmentInfo = 1;
+ mf->Seg.TimecodeScale = 1000000; // Default value
+
+ FOREACH(mf,toplen)
+ case 0x73a4: // SegmentUID
+ if (len!=sizeof(mf->Seg.UID))
+ errorjmp(mf,"SegmentUID size is not %d bytes",mf->Seg.UID);
+ readbytes(mf,mf->Seg.UID,sizeof(mf->Seg.UID));
+ break;
+ case 0x7384: // SegmentFilename
+ STRGETM(mf,mf->Seg.Filename,len);
+ break;
+ case 0x3cb923: // PrevUID
+ if (len!=sizeof(mf->Seg.PrevUID))
+ errorjmp(mf,"PrevUID size is not %d bytes",mf->Seg.PrevUID);
+ readbytes(mf,mf->Seg.PrevUID,sizeof(mf->Seg.PrevUID));
+ break;
+ case 0x3c83ab: // PrevFilename
+ STRGETM(mf,mf->Seg.PrevFilename,len);
+ break;
+ case 0x3eb923: // NextUID
+ if (len!=sizeof(mf->Seg.NextUID))
+ errorjmp(mf,"NextUID size is not %d bytes",mf->Seg.NextUID);
+ readbytes(mf,mf->Seg.NextUID,sizeof(mf->Seg.NextUID));
+ break;
+ case 0x3e83bb: // NextFilename
+ STRGETM(mf,mf->Seg.NextFilename,len);
+ break;
+ case 0x2ad7b1: // TimecodeScale
+ mf->Seg.TimecodeScale = readUInt(mf,(unsigned)len);
+ if (mf->Seg.TimecodeScale == 0)
+ errorjmp(mf,"Segment timecode scale is zero");
+ break;
+ case 0x4489: // Duration
+ duration = readFloat(mf,(unsigned)len);
+ break;
+ case 0x4461: // DateUTC
+ mf->Seg.DateUTC = readUInt(mf,(unsigned)len);
+ break;
+ case 0x7ba9: // Title
+ STRGETM(mf,mf->Seg.Title,len);
+ break;
+ case 0x4d80: // MuxingApp
+ STRGETM(mf,mf->Seg.MuxingApp,len);
+ break;
+ case 0x5741: // WritingApp
+ STRGETM(mf,mf->Seg.WritingApp,len);
+ break;
+ ENDFOR(mf);
+
+ mf->Seg.Duration = mul3(duration,mf->Seg.TimecodeScale);
+}
+
+static void parseFirstCluster(MatroskaFile *mf,ulonglong toplen) {
+ mf->seen.Cluster = 1;
+ mf->firstTimecode = 0;
+
+ FOREACH(mf,toplen)
+ case 0xe7: // Timecode
+ mf->firstTimecode += readUInt(mf,(unsigned)len);
+ break;
+ case 0xa0: // BlockGroup
+ FOREACH(mf,len)
+ case 0xa1: // Block
+ readVLUInt(mf); // track number
+ mf->firstTimecode += readSInt(mf,2);
+
+ skipbytes(mf,start + toplen - filepos(mf));
+ return;
+ ENDFOR(mf);
+ break;
+ ENDFOR(mf);
+}
+
+static void parseVideoInfo(MatroskaFile *mf,ulonglong toplen,struct TrackInfo *ti) {
+ ulonglong v;
+ char dW = 0, dH = 0;
+
+ FOREACH(mf,toplen)
+ case 0x9a: // FlagInterlaced
+ ti->Video.Interlaced = readUInt(mf,(unsigned)len)!=0;
+ break;
+ case 0x53b8: // StereoMode
+ v = readUInt(mf,(unsigned)len);
+ if (v>3)
+ errorjmp(mf,"Invalid stereo mode");
+ ti->Video.StereoMode = (unsigned char)v;
+ break;
+ case 0xb0: // PixelWidth
+ v = readUInt(mf,(unsigned)len);
+ if (v>0xffffffff)
+ errorjmp(mf,"PixelWidth is too large");
+ ti->Video.PixelWidth = (unsigned)v;
+ if (!dW)
+ ti->Video.DisplayWidth = ti->Video.PixelWidth;
+ break;
+ case 0xba: // PixelHeight
+ v = readUInt(mf,(unsigned)len);
+ if (v>0xffffffff)
+ errorjmp(mf,"PixelHeight is too large");
+ ti->Video.PixelHeight = (unsigned)v;
+ if (!dH)
+ ti->Video.DisplayHeight = ti->Video.PixelHeight;
+ break;
+ case 0x54b0: // DisplayWidth
+ v = readUInt(mf,(unsigned)len);
+ if (v>0xffffffff)
+ errorjmp(mf,"DisplayWidth is too large");
+ ti->Video.DisplayWidth = (unsigned)v;
+ dW = 1;
+ break;
+ case 0x54ba: // DisplayHeight
+ v = readUInt(mf,(unsigned)len);
+ if (v>0xffffffff)
+ errorjmp(mf,"DisplayHeight is too large");
+ ti->Video.DisplayHeight = (unsigned)v;
+ dH = 1;
+ break;
+ case 0x54b2: // DisplayUnit
+ v = readUInt(mf,(unsigned)len);
+ if (v>2)
+ errorjmp(mf,"Invalid DisplayUnit: %d",(int)v);
+ ti->Video.DisplayUnit = (unsigned char)v;
+ break;
+ case 0x54b3: // AspectRatioType
+ v = readUInt(mf,(unsigned)len);
+ if (v>2)
+ errorjmp(mf,"Invalid AspectRatioType: %d",(int)v);
+ ti->Video.AspectRatioType = (unsigned char)v;
+ break;
+ case 0x2eb524: // ColourSpace
+ ti->Video.ColourSpace = (unsigned)readUInt(mf,4);
+ break;
+ case 0x2fb523: // GammaValue
+ ti->Video.GammaValue = readFloat(mf,(unsigned)len);
+ break;
+ ENDFOR(mf);
+}
+
+static void parseAudioInfo(MatroskaFile *mf,ulonglong toplen,struct TrackInfo *ti) {
+ ulonglong v;
+
+ FOREACH(mf,toplen)
+ case 0xb5: // SamplingFrequency
+ ti->Audio.SamplingFreq = readFloat(mf,(unsigned)len);
+ break;
+ case 0x78b5: // OutputSamplingFrequency
+ ti->Audio.OutputSamplingFreq = readFloat(mf,(unsigned)len);
+ break;
+ case 0x9f: // Channels
+ v = readUInt(mf,(unsigned)len);
+ if (v<1 || v>255)
+ errorjmp(mf,"Invalid Channels value");
+ ti->Audio.Channels = (unsigned char)v;
+ break;
+ case 0x7d7b: // ChannelPositions
+ skipbytes(mf,len);
+ break;
+ case 0x6264: // BitDepth
+ v = readUInt(mf,(unsigned)len);
+ if ((v<1 || v>255) && !IsWritingApp(mf,"AVI-Mux GUI"))
+ errorjmp(mf,"Invalid BitDepth: %d",(int)v);
+ ti->Audio.BitDepth = (unsigned char)v;
+ break;
+ ENDFOR(mf);
+
+ if (mkv_TruncFloat(ti->Audio.OutputSamplingFreq)==0)
+ ti->Audio.OutputSamplingFreq = ti->Audio.SamplingFreq;
+}
+
+static void CopyStr(char **src,char **dst) {
+ size_t l;
+
+ if (!*src)
+ return;
+
+ l = strlen(*src)+1;
+ memcpy(*dst,*src,l);
+ *src = *dst;
+ *dst += l;
+}
+
+static void parseTrackEntry(MatroskaFile *mf,ulonglong toplen) {
+ struct TrackInfo t,*tp,**tpp;
+ ulonglong v;
+ char *cp = NULL;
+ size_t cplen = 0, cpadd = 0;
+
+ if (mf->nTracks >= MAX_TRACKS)
+ errorjmp(mf,"Too many tracks.");
+
+ // clear track info
+ memset(&t,0,sizeof(t));
+
+ // fill default values
+ t.Enabled = 1;
+ t.Default = 1;
+ t.Lacing = 1;
+ t.TimecodeScale = mkfi(1);
+ t.DecodeAll = 1;
+
+ FOREACH(mf,toplen)
+ case 0xd7: // TrackNumber
+ v = readUInt(mf,(unsigned)len);
+ if (v>255)
+ errorjmp(mf,"Track number is >255 (%d)",(int)v);
+ t.Number = (unsigned char)v;
+ break;
+ case 0x73c5: // TrackUID
+ t.UID = readUInt(mf,(unsigned)len);
+ break;
+ case 0x83: // TrackType
+ v = readUInt(mf,(unsigned)len);
+ if (v<1 || v>254)
+ errorjmp(mf,"Invalid track type: %d",(int)v);
+ t.Type = (unsigned char)v;
+ if (t.Type == 2) { // fill defaults for audio tracks
+ t.Audio.Channels = 1;
+ t.Audio.SamplingFreq = mkfi(8000);
+ }
+ break;
+ case 0xb9: // Enabled
+ t.Enabled = readUInt(mf,(unsigned)len)!=0;
+ break;
+ case 0x88: // Default
+ t.Default = readUInt(mf,(unsigned)len)!=0;
+ break;
+ case 0x9c: // Lacing
+ t.Lacing = readUInt(mf,(unsigned)len)!=0;
+ break;
+ case 0x6de7: // MinCache
+ v = readUInt(mf,(unsigned)len);
+ if (v > 0xffffffff)
+ errorjmp(mf,"MinCache is too large");
+ t.MinCache = (unsigned)v;
+ break;
+ case 0x6df8: // MaxCache
+ v = readUInt(mf,(unsigned)len);
+ if (v > 0xffffffff)
+ errorjmp(mf,"MaxCache is too large");
+ t.MaxCache = (unsigned)v;
+ break;
+ case 0x23e383: // DefaultDuration
+ t.DefaultDuration = readUInt(mf,(unsigned)len);
+ break;
+ case 0x23314f: // TrackTimecodeScale
+ t.TimecodeScale = readFloat(mf,(unsigned)len);
+ break;
+ case 0x536e: // Name
+ if (t.Name)
+ errorjmp(mf,"Duplicate Track Name");
+ STRGETA(mf,t.Name,len);
+ break;
+ case 0x22b59c: // Language
+ if (t.Language)
+ errorjmp(mf,"Duplicate Track Language");
+ STRGETA(mf,t.Language,len);
+ break;
+ case 0x86: // CodecID
+ if (t.CodecID)
+ errorjmp(mf,"Duplicate CodecID");
+ STRGETA(mf,t.CodecID,len);
+ break;
+ case 0x63a2: // CodecPrivate
+ if (cp)
+ errorjmp(mf,"Duplicate CodecPrivate");
+ if (len>262144) // 256KB
+ errorjmp(mf,"CodecPrivate is too large: %d",(int)len);
+ cplen = (unsigned)len;
+ cp = alloca(cplen);
+ readbytes(mf,cp,(int)cplen);
+ break;
+ case 0x258688: // CodecName
+ skipbytes(mf,len);
+ break;
+ case 0x3a9697: // CodecSettings
+ skipbytes(mf,len);
+ break;
+ case 0x3b4040: // CodecInfoURL
+ skipbytes(mf,len);
+ break;
+ case 0x26b240: // CodecDownloadURL
+ skipbytes(mf,len);
+ break;
+ case 0xaa: // CodecDecodeAll
+ t.DecodeAll = readUInt(mf,(unsigned)len)!=0;
+ break;
+ case 0x6fab: // TrackOverlay
+ v = readUInt(mf,(unsigned)len);
+ if (v>255)
+ errorjmp(mf,"Track number in TrackOverlay is too large: %d",(int)v);
+ t.TrackOverlay = (unsigned char)v;
+ break;
+ case 0xe0: // VideoInfo
+ parseVideoInfo(mf,len,&t);
+ break;
+ case 0xe1: // AudioInfo
+ parseAudioInfo(mf,len,&t);
+ break;
+ case 0x6d80: // ContentEncodings
+ FOREACH(mf,len)
+ case 0x6240: // ContentEncoding
+ FOREACH(mf,len)
+ case 0x5031: // ContentEncodingOrder
+ readUInt(mf,(unsigned)len);
+ break;
+ case 0x5032: // ContentEncodingScope
+ readUInt(mf,(unsigned)len);
+ break;
+ case 0x5033: // ContentEncodingType
+ readUInt(mf,(unsigned)len);
+ break;
+ case 0x5034: // ContentCompression
+ // fill in defaults
+ t.CompEnabled = 1;
+ t.CompMethod = COMP_ZLIB;
+ FOREACH(mf,len)
+ case 0x4254: // ContentCompAlgo
+ v = readUInt(mf,(unsigned)len);
+ if (v != 0)
+ errorjmp(mf,"Unsupported compression algorithm: %d",(int)v);
+ t.CompEnabled = 1;
+ t.CompMethod = COMP_ZLIB;
+ break;
+ case 0x4255: // ContentCompSettings
+ skipbytes(mf,len);
+ break;
+ ENDFOR(mf);
+ break;
+ // TODO Implement Encryption/Signatures
+ ENDFOR(mf);
+ break;
+ ENDFOR(mf);
+ break;
+ ENDFOR(mf);
+
+ // validate track info
+ if (!t.CodecID)
+ errorjmp(mf,"Track has no Codec ID");
+
+ // allocate new track
+ tpp = AGET(mf,Tracks);
+
+ // copy strings
+ if (t.Name)
+ cpadd += strlen(t.Name)+1;
+ if (t.Language)
+ cpadd += strlen(t.Language)+1;
+ if (t.CodecID)
+ cpadd += strlen(t.CodecID)+1;
+
+ tp = mf->cache->memalloc(mf->cache,sizeof(*tp) + cplen + cpadd);
+ if (tp == NULL)
+ errorjmp(mf,"Out of memory");
+
+ memcpy(tp,&t,sizeof(*tp));
+ memcpy(tp+1,cp,cplen);
+ if (cplen) {
+ tp->CodecPrivate = tp+1;
+ tp->CodecPrivateSize = (unsigned)cplen;
+ }
+
+ cp = (char*)(tp+1) + cplen;
+ CopyStr(&tp->Name,&cp);
+ CopyStr(&tp->Language,&cp);
+ CopyStr(&tp->CodecID,&cp);
+
+ // set default language
+ if (!tp->Language)
+ tp->Language="eng";
+
+ *tpp = tp;
+}
+
+static void parseTracks(MatroskaFile *mf,ulonglong toplen) {
+ mf->seen.Tracks = 1;
+ FOREACH(mf,toplen)
+ case 0xae: // TrackEntry
+ parseTrackEntry(mf,len);
+ break;
+ ENDFOR(mf);
+}
+
+static void addCue(MatroskaFile *mf,ulonglong pos,ulonglong timecode) {
+ struct Cue *cc = AGET(mf,Cues);
+ cc->Time = timecode;
+ cc->Position = pos;
+ cc->Track = 0;
+ cc->Block = 0;
+}
+
+static void fixupCues(MatroskaFile *mf) {
+ // adjust cues, shift cues if file does not start at 0
+ unsigned i;
+ longlong adjust = mf->firstTimecode * mf->Seg.TimecodeScale;
+
+ for (i=0;i<mf->nCues;++i) {
+ mf->Cues[i].Time *= mf->Seg.TimecodeScale;
+ mf->Cues[i].Time -= adjust;
+ }
+}
+
+static void parseCues(MatroskaFile *mf,ulonglong toplen) {
+ jmp_buf jb;
+ ulonglong v;
+ struct Cue cc;
+ unsigned i,j,k;
+
+ mf->seen.Cues = 1;
+ mf->nCues = 0;
+ cc.Block = 0;
+
+ memcpy(&jb,&mf->jb,sizeof(jb));
+
+ if (setjmp(mf->jb)) {
+ memcpy(&mf->jb,&jb,sizeof(jb));
+ mf->nCues = 0;
+ mf->seen.Cues = 0;
+ return;
+ }
+
+ FOREACH(mf,toplen)
+ case 0xbb: // CuePoint
+ FOREACH(mf,len)
+ case 0xb3: // CueTime
+ cc.Time = readUInt(mf,(unsigned)len);
+ break;
+ case 0xb7: // CueTrackPositions
+ FOREACH(mf,len)
+ case 0xf7: // CueTrack
+ v = readUInt(mf,(unsigned)len);
+ if (v>255)
+ errorjmp(mf,"CueTrack points to an invalid track: %d",(int)v);
+ cc.Track = (unsigned char)v;
+ break;
+ case 0xf1: // CueClusterPosition
+ cc.Position = readUInt(mf,(unsigned)len);
+ break;
+ case 0x5378: // CueBlockNumber
+ cc.Block = readUInt(mf,(unsigned)len);
+ break;
+ case 0xea: // CodecState
+ readUInt(mf,(unsigned)len);
+ break;
+ case 0xdb: // CueReference
+ FOREACH(mf,len)
+ case 0x96: // CueRefTime
+ readUInt(mf,(unsigned)len);
+ break;
+ case 0x97: // CueRefCluster
+ readUInt(mf,(unsigned)len);
+ break;
+ case 0x535f: // CueRefNumber
+ readUInt(mf,(unsigned)len);
+ break;
+ case 0xeb: // CueRefCodecState
+ readUInt(mf,(unsigned)len);
+ break;
+ ENDFOR(mf);
+ break;
+ ENDFOR(mf);
+ break;
+ ENDFOR(mf);
+
+ if (mf->nCues == 0 && mf->pCluster - mf->pSegment != cc.Position)
+ addCue(mf,mf->pCluster - mf->pSegment,mf->firstTimecode);
+
+ memcpy(AGET(mf,Cues),&cc,sizeof(cc));
+ break;
+ ENDFOR(mf);
+
+ memcpy(&mf->jb,&jb,sizeof(jb));
+
+ ARELEASE(mf,mf,Cues);
+
+ // bubble sort the cues and fuck the losers that write unordered cues
+ if (mf->nCues > 0)
+ for (i = mf->nCues - 1, k = 1; i > 0 && k > 0; --i)
+ for (j = k = 0; j < i; ++j)
+ if (mf->Cues[j].Time > mf->Cues[j+1].Time) {
+ struct Cue tmp = mf->Cues[j+1];
+ mf->Cues[j+1] = mf->Cues[j];
+ mf->Cues[j] = tmp;
+ ++k;
+ }
+
+ fixupCues(mf);
+}
+
+static void parseAttachment(MatroskaFile *mf,ulonglong toplen) {
+ struct Attachment a,*pa;
+
+ memset(&a,0,sizeof(a));
+ FOREACH(mf,toplen)
+ case 0x467e: // Description
+ STRGETA(mf,a.Description,len);
+ break;
+ case 0x466e: // Name
+ STRGETA(mf,a.Name,len);
+ break;
+ case 0x4660: // MimeType
+ STRGETA(mf,a.MimeType,len);
+ break;
+ case 0x46ae: // UID
+ a.UID = readUInt(mf,(unsigned)len);
+ break;
+ case 0x465c: // Data
+ a.Position = filepos(mf);
+ a.Length = len;
+ skipbytes(mf,len);
+ break;
+ ENDFOR(mf);
+
+ if (!a.Position)
+ return;
+
+ pa = AGET(mf,Attachments);
+ memcpy(pa,&a,sizeof(a));
+
+ if (a.Description)
+ pa->Description = mystrdup(mf->cache,a.Description);
+ if (a.Name)
+ pa->Name = mystrdup(mf->cache,a.Name);
+ if (a.MimeType)
+ pa->MimeType = mystrdup(mf->cache,a.MimeType);
+}
+
+static void parseAttachments(MatroskaFile *mf,ulonglong toplen) {
+ mf->seen.Attachments = 1;
+
+ FOREACH(mf,toplen)
+ case 0x61a7: // AttachedFile
+ parseAttachment(mf,len);
+ break;
+ ENDFOR(mf);
+}
+
+static void parseChapter(MatroskaFile *mf,ulonglong toplen,struct Chapter *parent) {
+ struct ChapterDisplay *disp;
+ struct ChapterProcess *proc;
+ struct ChapterCommand *cmd;
+ struct Chapter *ch = ASGET(mf,parent,Children);
+
+ memset(ch,0,sizeof(*ch));
+
+ ch->Enabled = 1;
+
+ FOREACH(mf,toplen)
+ case 0x73c4: // ChapterUID
+ ch->UID = readUInt(mf,(unsigned)len);
+ break;
+ case 0x91: // ChapterTimeStart
+ ch->Start = readUInt(mf,(unsigned)len);
+ break;
+ case 0x92: // ChapterTimeEnd
+ ch->End = readUInt(mf,(unsigned)len);
+ break;
+ case 0x98: // ChapterFlagHidden
+ ch->Hidden = readUInt(mf,(unsigned)len)!=0;
+ break;
+ case 0x4598: // ChapterFlagEnabled
+ ch->Enabled = readUInt(mf,(unsigned)len)!=0;
+ break;
+ case 0x8f: // ChapterTrack
+ FOREACH(mf,len)
+ case 0x89: // ChapterTrackNumber
+ *(ulonglong*)(ASGET(mf,ch,Tracks)) = readUInt(mf,(unsigned)len);
+ break;
+ ENDFOR(mf);
+ break;
+ case 0x80: // ChapterDisplay
+ disp = NULL;
+
+ FOREACH(mf,len)
+ case 0x85: // ChapterString
+ if (disp==NULL) {
+ disp = ASGET(mf,ch,Display);
+ memset(disp, 0, sizeof(*disp));
+ }
+ if (disp->String)
+ skipbytes(mf,len); // Ignore duplicate string
+ else
+ STRGETM(mf,disp->String,len);
+ break;
+ case 0x437c: // ChapterLanguage
+ if (disp==NULL) {
+ disp = ASGET(mf,ch,Display);
+ memset(disp, 0, sizeof(*disp));
+ }
+ if (disp->Language)
+ skipbytes(mf,len);
+ else
+ STRGETM(mf,disp->Language,len);
+ break;
+ case 0x437e: // ChapterCountry
+ if (disp==NULL) {
+ disp = ASGET(mf,ch,Display);
+ memset(disp, 0, sizeof(*disp));
+ }
+ if (disp->Country)
+ skipbytes(mf,len);
+ else
+ STRGETM(mf,disp->Country,len);
+ break;
+ ENDFOR(mf);
+
+ if (disp && !disp->String) {
+ mf->cache->memfree(mf->cache,disp->Language);
+ mf->cache->memfree(mf->cache,disp->Country);
+ --ch->nDisplay;
+ }
+ break;
+ case 0x6944: // ChapProcess
+ proc = NULL;
+
+ FOREACH(mf,len)
+ case 0x6955: // ChapProcessCodecID
+ if (proc == NULL) {
+ proc = ASGET(mf, ch, Process);
+ memset(proc, 0, sizeof(*proc));
+ }
+ proc->CodecID = (unsigned)readUInt(mf,(unsigned)len);
+ break;
+ case 0x450d: // ChapProcessPrivate
+ if (proc == NULL) {
+ proc = ASGET(mf, ch, Process);
+ memset(proc, 0, sizeof(*proc));
+ }
+ if (proc->CodecPrivate)
+ skipbytes(mf, len);
+ else {
+ proc->CodecPrivateLength = (unsigned)len;
+ STRGETM(mf,proc->CodecPrivate,len);
+ }
+ break;
+ case 0x6911: // ChapProcessCommand
+ if (proc == NULL) {
+ proc = ASGET(mf, ch, Process);
+ memset(proc, 0, sizeof(*proc));
+ }
+
+ cmd = NULL;
+
+ FOREACH(mf,len)
+ case 0x6922: // ChapterCommandTime
+ if (cmd == NULL) {
+ cmd = ASGET(mf,proc,Commands);
+ memset(cmd, 0, sizeof(*cmd));
+ }
+ cmd->Time = (unsigned)readUInt(mf,(unsigned)len);
+ break;
+ case 0x6933: // ChapterCommandString
+ if (cmd == NULL) {
+ cmd = ASGET(mf,proc,Commands);
+ memset(cmd, 0, sizeof(*cmd));
+ }
+ if (cmd->Command)
+ skipbytes(mf,len);
+ else {
+ cmd->CommandLength = (unsigned)len;
+ STRGETM(mf,cmd->Command,len);
+ }
+ break;
+ ENDFOR(mf);
+
+ if (cmd && !cmd->Command)
+ --proc->nCommands;
+ break;
+ ENDFOR(mf);
+
+ if (proc && !proc->nCommands)
+ --ch->nProcess;
+ break;
+ case 0xb6: // Nested ChapterAtom
+ parseChapter(mf,len,ch);
+ break;
+ ENDFOR(mf);
+
+ ARELEASE(mf,ch,Tracks);
+ ARELEASE(mf,ch,Display);
+ ARELEASE(mf,ch,Children);
+}
+
+static void parseChapters(MatroskaFile *mf,ulonglong toplen) {
+ struct Chapter *ch;
+
+ mf->seen.Chapters = 1;
+
+ FOREACH(mf,toplen)
+ case 0x45b9: // EditionEntry
+ ch = AGET(mf,Chapters);
+ memset(ch, 0, sizeof(*ch));
+ FOREACH(mf,len)
+ case 0x45bc: // EditionUID
+ ch->UID = readUInt(mf,(unsigned)len);
+ break;
+ case 0x45bd: // EditionFlagHidden
+ ch->Hidden = readUInt(mf,(unsigned)len)!=0;
+ break;
+ case 0x45db: // EditionFlagDefault
+ ch->Default = readUInt(mf,(unsigned)len)!=0;
+ break;
+ case 0x45dd: // EditionFlagOrdered
+ ch->Ordered = readUInt(mf,(unsigned)len)!=0;
+ break;
+ case 0xb6: // ChapterAtom
+ parseChapter(mf,len,ch);
+ break;
+ ENDFOR(mf);
+ break;
+ ENDFOR(mf);
+}
+
+static void parseTags(MatroskaFile *mf,ulonglong toplen) {
+ struct Tag *tag;
+ struct Target *target;
+ struct SimpleTag *st;
+
+ mf->seen.Tags = 1;
+
+ FOREACH(mf,toplen)
+ case 0x7373: // Tag
+ tag = AGET(mf,Tags);
+ memset(tag,0,sizeof(*tag));
+
+ FOREACH(mf,len)
+ case 0x63c0: // Targets
+ FOREACH(mf,len)
+ case 0x63c5: // TrackUID
+ target = ASGET(mf,tag,Targets);
+ target->UID = readUInt(mf,(unsigned)len);
+ target->Type = TARGET_TRACK;
+ break;
+ case 0x63c4: // ChapterUID
+ target = ASGET(mf,tag,Targets);
+ target->UID = readUInt(mf,(unsigned)len);
+ target->Type = TARGET_CHAPTER;
+ break;
+ case 0x63c6: // AttachmentUID
+ target = ASGET(mf,tag,Targets);
+ target->UID = readUInt(mf,(unsigned)len);
+ target->Type = TARGET_ATTACHMENT;
+ break;
+ case 0x63c9: // EditionUID
+ target = ASGET(mf,tag,Targets);
+ target->UID = readUInt(mf,(unsigned)len);
+ target->Type = TARGET_EDITION;
+ break;
+ ENDFOR(mf);
+ break;
+ case 0x67c8: // SimpleTag
+ st = ASGET(mf,tag,SimpleTags);
+ memset(st,0,sizeof(*st));
+
+ FOREACH(mf,len)
+ case 0x45a3: // TagName
+ if (st->Name)
+ skipbytes(mf,len);
+ else
+ STRGETM(mf,st->Name,len);
+ break;
+ case 0x4487: // TagString
+ if (st->Value)
+ skipbytes(mf,len);
+ else
+ STRGETM(mf,st->Value,len);
+ break;
+ case 0x447a: // TagLanguage
+ if (st->Language)
+ skipbytes(mf,len);
+ else
+ STRGETM(mf,st->Language,len);
+ break;
+ case 0x4484: // TagDefault
+ st->Default = readUInt(mf,(unsigned)len)!=0;
+ break;
+ ENDFOR(mf);
+
+ if (!st->Name || !st->Value) {
+ mf->cache->memfree(mf->cache,st->Name);
+ mf->cache->memfree(mf->cache,st->Value);
+ --tag->nSimpleTags;
+ }
+ break;
+ ENDFOR(mf);
+ break;
+ ENDFOR(mf);
+}
+
+static void parseContainer(MatroskaFile *mf) {
+ ulonglong len;
+ int id = readID(mf);
+ if (id==EOF)
+ errorjmp(mf,"Unexpected EOF in parseContainer");
+
+ len = readSize(mf);
+
+ switch (id) {
+ case 0x1549a966: // SegmentInfo
+ parseSegmentInfo(mf,len);
+ break;
+ case 0x1f43b675: // Cluster
+ parseFirstCluster(mf,len);
+ break;
+ case 0x1654ae6b: // Tracks
+ parseTracks(mf,len);
+ break;
+ case 0x1c53bb6b: // Cues
+ parseCues(mf,len);
+ break;
+ case 0x1941a469: // Attachments
+ parseAttachments(mf,len);
+ break;
+ case 0x1043a770: // Chapters
+ parseChapters(mf,len);
+ break;
+ case 0x1254c367: // Tags
+ parseTags(mf,len);
+ break;
+ }
+}
+
+static void parseContainerPos(MatroskaFile *mf,ulonglong pos) {
+ seek(mf,pos);
+ parseContainer(mf);
+}
+
+static void parsePointers(MatroskaFile *mf) {
+ if (mf->pSegmentInfo && !mf->seen.SegmentInfo)
+ parseContainerPos(mf,mf->pSegmentInfo);
+ if (mf->pCluster && !mf->seen.Cluster)
+ parseContainerPos(mf,mf->pCluster);
+ if (mf->pTracks && !mf->seen.Tracks)
+ parseContainerPos(mf,mf->pTracks);
+ if (mf->pCues && !mf->seen.Cues) {
+ parseContainerPos(mf,mf->pCues);
+
+ // ignore errors
+ mf->flags &= ~MPF_ERROR;
+ }
+ if (mf->pAttachments && !mf->seen.Attachments)
+ parseContainerPos(mf,mf->pAttachments);
+ if (mf->pChapters && !mf->seen.Chapters)
+ parseContainerPos(mf,mf->pChapters);
+ if (mf->pTags && !mf->seen.Tags)
+ parseContainerPos(mf,mf->pTags);
+}
+
+static void parseSegment(MatroskaFile *mf,ulonglong toplen) {
+ ulonglong nextpos;
+ unsigned nSeekHeads = 0, dontstop = 0;
+
+ // we want to read data until we find a seekhead or a trackinfo
+ FOREACH(mf,toplen)
+ case 0x114d9b74: // SeekHead
+ if (mf->flags & MKVF_AVOID_SEEKS) {
+ skipbytes(mf,len);
+ break;
+ }
+
+ nextpos = filepos(mf) + len;
+ do {
+ mf->pSeekHead = 0;
+ parseSeekHead(mf,len);
+ ++nSeekHeads;
+ if (mf->pSeekHead) { // this is possibly a chained SeekHead
+ seek(mf,mf->pSeekHead);
+ id = readID(mf);
+ if (id==EOF) // chained SeekHead points to EOF?
+ goto resume;
+ if (id != 0x114d9b74) // chained SeekHead doesnt point to a SeekHead?
+ goto resume;
+ len = readSize(mf);
+ } else if (mf->pSegmentInfo && mf->pTracks && mf->pCues && mf->pCluster) { // we have pointers to all key elements
+ // XXX EVIL HACK
+ // Some software doesnt index tags via SeekHead, so we continue
+ // reading the segment after the second SeekHead
+ if (mf->pTags || nSeekHeads<2 || filepos(mf)>=start+toplen) {
+ parsePointers(mf);
+ return;
+ }
+ // reset nextpos pointer to current position
+ nextpos = filepos(mf);
+ dontstop = 1;
+ }
+ } while (mf->pSeekHead);
+resume:
+ // XXX workaround for AVI-Mux GUI
+ if (mf->pSegmentInfo && !mf->seen.SegmentInfo)
+ parseContainerPos(mf,mf->pSegmentInfo);
+ seek(mf,nextpos); // resume reading segment
+ break;
+ case 0x1549a966: // SegmentInfo
+ mf->pSegmentInfo = cur;
+ parseSegmentInfo(mf,len);
+ break;
+ case 0x1f43b675: // Cluster
+ if (!mf->pCluster)
+ mf->pCluster = cur;
+ if (mf->seen.Cluster)
+ skipbytes(mf,len);
+ else
+ parseFirstCluster(mf,len);
+ break;
+ case 0x1654ae6b: // Tracks
+ mf->pTracks = cur;
+ parseTracks(mf,len);
+ break;
+ case 0x1c53bb6b: // Cues
+ mf->pCues = cur;
+ parseCues(mf,len);
+ break;
+ case 0x1941a469: // Attachments
+ mf->pAttachments = cur;
+ parseAttachments(mf,len);
+ break;
+ case 0x1043a770: // Chapters
+ mf->pChapters = cur;
+ parseChapters(mf,len);
+ break;
+ case 0x1254c367: // Tags
+ mf->pTags = cur;
+ parseTags(mf,len);
+ break;
+ ENDFOR1(mf);
+ // if we have pointers to all key elements
+ if (!dontstop && mf->pSegmentInfo && mf->pTracks && mf->pCluster)
+ break;
+ ENDFOR2();
+ parsePointers(mf);
+}
+
+static void parseBlockGroup(MatroskaFile *mf,ulonglong toplen,ulonglong timecode) {
+ ulonglong v;
+ ulonglong duration = 0;
+ ulonglong dpos;
+ struct QueueEntry *qe,*qf = NULL;
+ unsigned char have_duration = 0, have_block = 0;
+ unsigned char gap = 0;
+ unsigned char lacing = 0;
+ unsigned char ref = 0;
+ unsigned char trackid;
+ unsigned tracknum = 0;
+ int c;
+ unsigned nframes = 0,i;
+ unsigned *sizes;
+ signed short block_timecode;
+
+ FOREACH(mf,toplen)
+ case 0xfb: // ReferenceBlock
+ readSInt(mf,(unsigned)len);
+ ref = 1;
+ break;
+ case 0xa1: // Block
+ have_block = 1;
+
+ dpos = filepos(mf);
+
+ v = readVLUInt(mf);
+ if (v>255)
+ errorjmp(mf,"Invalid track number in Block: %d",(int)v);
+ trackid = (unsigned char)v;
+
+ for (tracknum=0;tracknum<mf->nTracks;++tracknum)
+ if (mf->Tracks[tracknum]->Number == trackid)
+ goto found;
+ errorjmp(mf,"Invalid track number in Block: %u",trackid);
+found:
+
+ if (mf->trackMask & (1<<tracknum)) { // ignore this block
+ skipbytes(mf,start + tmplen - filepos(mf)); // shortcut
+ return;
+ }
+
+ block_timecode = (signed short)readSInt(mf,2);
+
+ // recalculate this block's timecode to final timecode in ns
+ timecode = mul3(mf->Tracks[tracknum]->TimecodeScale,
+ (timecode - mf->firstTimecode + block_timecode) * mf->Seg.TimecodeScale);
+
+ c = readch(mf);
+ if (c==EOF)
+ errorjmp(mf,"Unexpected EOF while reading Block flags");
+
+ gap = c & 0x1;
+ lacing = (c >> 1) & 3;
+
+ if (lacing) {
+ c = readch(mf);
+ if (c == EOF)
+ errorjmp(mf,"Unexpected EOF while reading lacing data");
+ nframes = c+1;
+ } else
+ nframes = 1;
+ sizes = alloca(nframes*sizeof(*sizes));
+
+ switch (lacing) {
+ case 0: // No lacing
+ sizes[0] = (unsigned)(len - filepos(mf) + dpos);
+ break;
+ case 1: // Xiph lacing
+ sizes[nframes-1] = 0;
+ for (i=0;i<nframes-1;++i) {
+ sizes[i] = 0;
+ do {
+ c = readch(mf);
+ if (c==EOF)
+ errorjmp(mf,"Unexpected EOF while reading lacing data");
+ sizes[i] += c;
+ } while (c==255);
+ sizes[nframes-1] += sizes[i];
+ }
+ sizes[nframes-1] = (unsigned)(len - filepos(mf) + dpos) - sizes[nframes-1];
+ break;
+ case 3: // EBML lacing
+ sizes[nframes-1] = 0;
+ sizes[0] = (unsigned)readVLUInt(mf);
+ for (i=1;i<nframes-1;++i) {
+ sizes[i] = sizes[i-1] + (int)readVLSInt(mf);
+ sizes[nframes-1] += sizes[i];
+ }
+ if (nframes>1)
+ sizes[nframes-1] = (unsigned)(len - filepos(mf) + dpos) - sizes[0] - sizes[nframes-1];
+ break;
+ case 2: // Fixed lacing
+ sizes[0] = (unsigned)(len - filepos(mf) + dpos)/nframes;
+ for (i=1;i<nframes;++i)
+ sizes[i] = sizes[0];
+ break;
+ }
+
+ v = filepos(mf);
+ qf = NULL;
+ for (i=0;i<nframes;++i) {
+ qe = QAlloc(mf);
+ if (!qf)
+ qf = qe;
+
+ qe->Start = timecode;
+ qe->End = timecode;
+ qe->Position = v;
+ qe->Length = sizes[i];
+ qe->flags = FRAME_UNKNOWN_END | FRAME_KF;
+ if (i == nframes-1 && gap)
+ qe->flags |= FRAME_GAP;
+ if (i > 0)
+ qe->flags |= FRAME_UNKNOWN_START;
+
+ QPut(&mf->Queues[tracknum],qe);
+
+ v += sizes[i];
+ }
+
+ // we want to still load these bytes into cache
+ for (v = filepos(mf) & ~0x3fff; v < len + dpos; v += 0x4000)
+ mf->cache->read(mf->cache,v,NULL,0); // touch page
+
+ skipbytes(mf,len - filepos(mf) + dpos);
+ break;
+ case 0x9b: // BlockDuration
+ duration = readUInt(mf,(unsigned)len);
+ have_duration = 1;
+ break;
+ ENDFOR(mf);
+
+ if (!have_block)
+ errorjmp(mf,"Found a BlockGroup without Block");
+
+ if (nframes > 1) {
+ if (have_duration) {
+ duration = mul3(mf->Tracks[tracknum]->TimecodeScale,
+ duration * mf->Seg.TimecodeScale);
+ dpos = duration / nframes;
+
+ v = qf->Start;
+ for (qe = qf; nframes > 1; --nframes, qe = qe->next) {
+ qe->Start = v;
+ v += dpos;
+ duration -= dpos;
+ qe->End = v;
+ qe->flags &= ~(FRAME_UNKNOWN_START|FRAME_UNKNOWN_END);
+ }
+ qe->Start = v;
+ qe->End = v + duration;
+ qe->flags &= ~(FRAME_UNKNOWN_START|FRAME_UNKNOWN_END);
+ } else if (mf->Tracks[tracknum]->DefaultDuration) {
+ dpos = mf->Tracks[tracknum]->DefaultDuration;
+ v = qf->Start;
+ for (qe = qf; nframes > 0; --nframes, qe = qe->next) {
+ qe->Start = v;
+ v += dpos;
+ qe->End = v;
+ qe->flags &= ~(FRAME_UNKNOWN_START|FRAME_UNKNOWN_END);
+ }
+ }
+ } else if (nframes == 1) {
+ if (have_duration) {
+ qf->End = qf->Start + mul3(mf->Tracks[tracknum]->TimecodeScale,
+ duration * mf->Seg.TimecodeScale);
+ qf->flags &= ~FRAME_UNKNOWN_END;
+ } else if (mf->Tracks[tracknum]->DefaultDuration) {
+ qf->End = qf->Start + mf->Tracks[tracknum]->DefaultDuration;
+ qf->flags &= ~FRAME_UNKNOWN_END;
+ }
+ }
+
+ if (ref)
+ while (qf) {
+ qf->flags &= ~FRAME_KF;
+ qf = qf->next;
+ }
+}
+
+static void ClearQueue(MatroskaFile *mf,struct Queue *q) {
+ struct QueueEntry *qe,*qn;
+
+ for (qe=q->head;qe;qe=qn) {
+ qn = qe->next;
+ qe->next = mf->QFreeList;
+ mf->QFreeList = qe;
+ }
+
+ q->head = NULL;
+ q->tail = NULL;
+}
+
+static void EmptyQueues(MatroskaFile *mf) {
+ unsigned i;
+
+ for (i=0;i<mf->nTracks;++i)
+ ClearQueue(mf,&mf->Queues[i]);
+}
+
+static int readMoreBlocks(MatroskaFile *mf) {
+ ulonglong toplen, cstop;
+ longlong cp;
+ int cid, ret = 0;
+ jmp_buf jb;
+ volatile unsigned retries = 0;
+
+ if (mf->readPosition >= mf->pSegmentTop)
+ return EOF;
+
+ memcpy(&jb,&mf->jb,sizeof(jb));
+
+ if (setjmp(mf->jb)) { // something evil happened here, try to resync
+ ret = EOF;
+
+ if (++retries > 3) // don't try too hard
+ goto ex;
+
+ for (;;) {
+ if (filepos(mf) >= mf->pSegmentTop) {
+ mf->readPosition = filepos(mf);
+ goto ex;
+ }
+
+ cp = mf->cache->scan(mf->cache,filepos(mf),0x1f43b675); // cluster
+
+ if (cp < 0 || (ulonglong)cp >= mf->pSegmentTop)
+ goto ex;
+
+ seek(mf,cp);
+
+ cid = readID(mf);
+ if (cid == EOF)
+ goto ex;
+ if (cid == 0x1f43b675) {
+ toplen = readSize(mf);
+ if (toplen < MAXCLUSTER) {
+ // reset error flags
+ mf->flags &= ~MPF_ERROR;
+ ret = RBRESYNC;
+ break;
+ }
+ }
+ }
+
+ mf->readPosition = cp;
+ }
+
+ cstop = mf->cache->getsize(mf->cache)>>1;
+ if (cstop > MAX_READAHEAD)
+ cstop = MAX_READAHEAD;
+ cstop += mf->readPosition;
+
+ seek(mf,mf->readPosition);
+
+ while (filepos(mf) < mf->pSegmentTop) {
+ cid = readID(mf);
+ if (cid == EOF) {
+ ret = EOF;
+ break;
+ }
+ toplen = readSize(mf);
+
+ if (cid == 0x1f43b675) { // Cluster
+ unsigned char have_timecode = 0;
+
+ FOREACH(mf,toplen)
+ case 0xe7: // Timecode
+ mf->tcCluster = readUInt(mf,(unsigned)len);
+ have_timecode = 1;
+ break;
+ case 0xa7: // Position
+ readUInt(mf,(unsigned)len);
+ break;
+ case 0xab: // PrevSize
+ readUInt(mf,(unsigned)len);
+ break;
+ case 0xa0: // BlockGroup
+ if (!have_timecode)
+ errorjmp(mf,"Found BlockGroup before cluster TimeCode");
+ parseBlockGroup(mf,len,mf->tcCluster);
+ goto out;
+ ENDFOR(mf);
+out:;
+ } else {
+ if (toplen > MAXFRAME)
+ errorjmp(mf,"Element in a cluster is too large around %llu, %X [%u]",filepos(mf),cid,(unsigned)toplen);
+ if (cid == 0xa0) // BlockGroup
+ parseBlockGroup(mf,toplen,mf->tcCluster);
+ else
+ skipbytes(mf,toplen);
+ }
+
+ if ((mf->readPosition = filepos(mf)) > cstop)
+ break;
+ }
+
+ mf->readPosition = filepos(mf);
+
+ex:
+ memcpy(&mf->jb,&jb,sizeof(jb));
+
+ return ret;
+}
+
+// this is almost the same as readMoreBlocks, except it ensures
+// there are no partial frames queued, however empty queues are ok
+static int fillQueues(MatroskaFile *mf,unsigned int mask) {
+ unsigned i,j;
+ int ret = 0;
+
+ for (;;) {
+ j = 0;
+
+ for (i=0;i<mf->nTracks;++i)
+ if (mf->Queues[i].head && !(mask & (1<<i)))
+ ++j;
+
+ if (j>0) // have at least some frames
+ return ret;
+
+ if ((ret = readMoreBlocks(mf)) < 0) {
+ j = 0;
+ for (i=0;i<mf->nTracks;++i)
+ if (mf->Queues[i].head && !(mask & (1<<i)))
+ ++j;
+ if (j) // we adjusted some blocks
+ return 0;
+ return EOF;
+ }
+ }
+}
+
+static void reindex(MatroskaFile *mf) {
+ jmp_buf jb;
+ ulonglong pos = mf->pCluster;
+ ulonglong step = 10*1024*1024;
+ ulonglong size, tc, isize;
+ longlong next_cluster;
+ int id, have_tc, bad;
+ struct Cue *cue;
+
+ if (pos >= mf->pSegmentTop)
+ return;
+
+ if (pos + step * 10 > mf->pSegmentTop)
+ step = (mf->pSegmentTop - pos) / 10;
+ if (step == 0)
+ step = 1;
+
+ memcpy(&jb,&mf->jb,sizeof(jb));
+
+ // remove all cues
+ mf->nCues = 0;
+
+ bad = 0;
+
+ while (pos < mf->pSegmentTop) {
+ if (!mf->cache->progress(mf->cache,pos,mf->pSegmentTop))
+ break;
+
+ if (++bad > 50) {
+ pos += step;
+ bad = 0;
+ continue;
+ }
+
+ // find next cluster header
+ next_cluster = mf->cache->scan(mf->cache,pos,0x1f43b675); // cluster
+ if (next_cluster < 0 || (ulonglong)next_cluster >= mf->pSegmentTop)
+ break;
+
+ pos = next_cluster + 4; // prevent endless loops
+
+ if (setjmp(mf->jb)) // something evil happened while reindexing
+ continue;
+
+ seek(mf,next_cluster);
+
+ id = readID(mf);
+ if (id == EOF)
+ break;
+ if (id != 0x1f43b675) // shouldn't happen
+ continue;
+
+ size = readVLUInt(mf);
+ if (size >= MAXCLUSTER || size < 1024)
+ continue;
+
+ have_tc = 0;
+ size += filepos(mf);
+
+ while (filepos(mf) < (ulonglong)next_cluster + 1024) {
+ id = readID(mf);
+ if (id == EOF)
+ break;
+
+ isize = readVLUInt(mf);
+
+ if (id == 0xe7) { // cluster timecode
+ tc = readUInt(mf,(unsigned)isize);
+ have_tc = 1;
+ break;
+ }
+
+ skipbytes(mf,isize);
+ }
+
+ if (!have_tc)
+ continue;
+
+ seek(mf,size);
+ id = readID(mf);
+
+ if (id == EOF)
+ break;
+
+ if (id != 0x1f43b675) // cluster
+ continue;
+
+ // good cluster, remember it
+ cue = AGET(mf,Cues);
+ cue->Time = tc;
+ cue->Position = next_cluster - mf->pSegment;
+ cue->Block = 0;
+ cue->Track = 0;
+
+ // advance to the next point
+ pos = next_cluster + step;
+ if (pos < size)
+ pos = size;
+
+ bad = 0;
+ }
+
+ fixupCues(mf);
+
+ if (mf->nCues == 0) {
+ cue = AGET(mf,Cues);
+ cue->Time = mf->firstTimecode;
+ cue->Position = mf->pCluster - mf->pSegment;
+ cue->Block = 0;
+ cue->Track = 0;
+ }
+
+ mf->cache->progress(mf->cache,0,0);
+
+ memcpy(&mf->jb,&jb,sizeof(jb));
+}
+
+static void fixupChapter(ulonglong adj, struct Chapter *ch) {
+ unsigned i;
+
+ if (ch->Start != 0)
+ ch->Start -= adj;
+ if (ch->End != 0)
+ ch->End -= adj;
+
+ for (i=0;i<ch->nChildren;++i)
+ fixupChapter(adj,&ch->Children[i]);
+}
+
+static longlong findLastTimecode(MatroskaFile *mf) {
+ ulonglong nd = 0;
+ unsigned n,vtrack;
+
+ if (mf->nCues == 0 || mf->nTracks == 0)
+ return -1;
+
+ for (n=vtrack=0;n<mf->nTracks;++n)
+ if (mf->Tracks[n]->Type == TT_VIDEO) {
+ vtrack = n;
+ goto ok;
+ }
+
+ return -1;
+ok:
+
+ mf->trackMask = ~(1 << vtrack);
+ mf->readPosition = mf->Cues[mf->nCues - 1].Position + mf->pSegment;
+ mf->tcCluster = mf->Cues[mf->nCues - 1].Time / mf->Seg.TimecodeScale;
+
+ do
+ while (mf->Queues[vtrack].head)
+ {
+ ulonglong tc = mf->Queues[vtrack].head->flags & FRAME_UNKNOWN_END ?
+ mf->Queues[vtrack].head->Start : mf->Queues[vtrack].head->End;
+ if (nd < tc)
+ nd = tc;
+ QFree(mf,QGet(&mf->Queues[vtrack]));
+ }
+ while (readMoreBlocks(mf) != EOF);
+
+ mf->trackMask = 0;
+
+ return nd;
+}
+
+static void parseFile(MatroskaFile *mf) {
+ ulonglong len = filepos(mf), adjust;
+ unsigned i;
+ int id = readID(mf);
+ int m;
+
+ if (id==EOF)
+ errorjmp(mf,"Unexpected EOF at start of file");
+
+ // files with multiple concatenated segments can have only
+ // one EBML prolog
+ if (len > 0 && id == 0x18538067)
+ goto segment;
+
+ if (id!=0x1a45dfa3)
+ errorjmp(mf,"First element in file is not EBML");
+
+ parseEBML(mf,readSize(mf));
+
+ // next we need to find the first segment
+ for (;;) {
+ id = readID(mf);
+ if (id==EOF)
+ errorjmp(mf,"No segments found in the file");
+segment:
+ len = readVLUIntImp(mf,&m);
+ // see if it's unspecified
+ if (len == (MAXU64 >> (57-m*7)))
+ len = MAXU64;
+ if (id == 0x18538067) // Segment
+ break;
+ skipbytes(mf,len);
+ }
+
+ // found it
+ mf->pSegment = filepos(mf);
+ mf->pSegmentTop = len == MAXU64 ? MAXU64 : mf->pSegment + len;
+ parseSegment(mf,len);
+
+ // check if we got all data
+ if (!mf->seen.SegmentInfo)
+ errorjmp(mf,"Couldn't find SegmentInfo");
+ if (!mf->seen.Tracks)
+ errorjmp(mf,"Couldn't find Tracks");
+ if (!mf->seen.Cluster)
+ errorjmp(mf,"Couldn't find any Clusters");
+
+ adjust = mf->firstTimecode * mf->Seg.TimecodeScale;
+
+ for (i=0;i<mf->nChapters;++i)
+ fixupChapter(adjust, &mf->Chapters[i]);
+
+ // release extra memory
+ ARELEASE(mf,mf,Tracks);
+
+ // initialize reader
+ mf->Queues = mf->cache->memalloc(mf->cache,mf->nTracks * sizeof(*mf->Queues));
+ if (mf->Queues == NULL)
+ errorjmp(mf, "Ouf of memory");
+ memset(mf->Queues, 0, mf->nTracks * sizeof(*mf->Queues));
+
+ // try to detect real duration
+ if (!(mf->flags & MKVF_AVOID_SEEKS)) {
+ longlong nd = findLastTimecode(mf);
+ if (nd > 0)
+ mf->Seg.Duration = nd;
+ }
+
+ // move to first frame
+ mf->readPosition = mf->pCluster;
+ mf->tcCluster = mf->firstTimecode;
+}
+
+static void DeleteChapter(MatroskaFile *mf,struct Chapter *ch) {
+ unsigned i,j;
+
+ for (i=0;i<ch->nDisplay;++i) {
+ mf->cache->memfree(mf->cache,ch->Display[i].String);
+ mf->cache->memfree(mf->cache,ch->Display[i].Language);
+ mf->cache->memfree(mf->cache,ch->Display[i].Country);
+ }
+ mf->cache->memfree(mf->cache,ch->Display);
+ mf->cache->memfree(mf->cache,ch->Tracks);
+
+ for (i=0;i<ch->nProcess;++i) {
+ for (j=0;j<ch->Process[i].nCommands;++j)
+ mf->cache->memfree(mf->cache,ch->Process[i].Commands[j].Command);
+ mf->cache->memfree(mf->cache,ch->Process[i].Commands);
+ mf->cache->memfree(mf->cache,ch->Process[i].CodecPrivate);
+ }
+ mf->cache->memfree(mf->cache,ch->Process);
+
+ for (i=0;i<ch->nChildren;++i)
+ DeleteChapter(mf,&ch->Children[i]);
+ mf->cache->memfree(mf->cache,ch->Children);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// public interface
+MatroskaFile *mkv_OpenEx(InputStream *io,
+ ulonglong base,
+ unsigned flags,
+ char *err_msg,unsigned msgsize)
+{
+ MatroskaFile *mf = io->memalloc(io,sizeof(*mf));
+ if (mf == NULL) {
+ strlcpy(err_msg,"Out of memory",msgsize);
+ return NULL;
+ }
+
+ memset(mf,0,sizeof(*mf));
+
+ mf->cache = io;
+ mf->flags = flags;
+ io->progress(io,0,0);
+
+ if (setjmp(mf->jb)==0) {
+ seek(mf,base);
+ parseFile(mf);
+ } else { // parser error
+ strlcpy(err_msg,mf->errmsg,msgsize);
+ mkv_Close(mf);
+ return NULL;
+ }
+
+ return mf;
+}
+
+MatroskaFile *mkv_Open(InputStream *io,
+ char *err_msg,unsigned msgsize)
+{
+ return mkv_OpenEx(io,0,0,err_msg,msgsize);
+}
+
+void mkv_Close(MatroskaFile *mf) {
+ unsigned i,j;
+
+ if (mf==NULL)
+ return;
+
+ for (i=0;i<mf->nTracks;++i)
+ mf->cache->memfree(mf->cache,mf->Tracks[i]);
+ mf->cache->memfree(mf->cache,mf->Tracks);
+
+ for (i=0;i<mf->nQBlocks;++i)
+ mf->cache->memfree(mf->cache,mf->QBlocks[i]);
+ mf->cache->memfree(mf->cache,mf->QBlocks);
+
+ mf->cache->memfree(mf->cache,mf->Queues);
+
+ mf->cache->memfree(mf->cache,mf->Seg.Title);
+ mf->cache->memfree(mf->cache,mf->Seg.MuxingApp);
+ mf->cache->memfree(mf->cache,mf->Seg.WritingApp);
+ mf->cache->memfree(mf->cache,mf->Seg.Filename);
+ mf->cache->memfree(mf->cache,mf->Seg.NextFilename);
+ mf->cache->memfree(mf->cache,mf->Seg.PrevFilename);
+
+ mf->cache->memfree(mf->cache,mf->Cues);
+
+ for (i=0;i<mf->nAttachments;++i) {
+ mf->cache->memfree(mf->cache,mf->Attachments[i].Description);
+ mf->cache->memfree(mf->cache,mf->Attachments[i].Name);
+ mf->cache->memfree(mf->cache,mf->Attachments[i].MimeType);
+ }
+ mf->cache->memfree(mf->cache,mf->Attachments);
+
+ for (i=0;i<mf->nChapters;++i)
+ DeleteChapter(mf,&mf->Chapters[i]);
+ mf->cache->memfree(mf->cache,mf->Chapters);
+
+ for (i=0;i<mf->nTags;++i) {
+ for (j=0;j<mf->Tags[i].nSimpleTags;++j) {
+ mf->cache->memfree(mf->cache,mf->Tags[i].SimpleTags[j].Name);
+ mf->cache->memfree(mf->cache,mf->Tags[i].SimpleTags[j].Value);
+ }
+ mf->cache->memfree(mf->cache,mf->Tags[i].Targets);
+ mf->cache->memfree(mf->cache,mf->Tags[i].SimpleTags);
+ }
+ mf->cache->memfree(mf->cache,mf->Tags);
+
+ mf->cache->memfree(mf->cache,mf);
+}
+
+const char *mkv_GetLastError(MatroskaFile *mf) {
+ return mf->errmsg[0] ? mf->errmsg : NULL;
+}
+
+SegmentInfo *mkv_GetFileInfo(MatroskaFile *mf) {
+ return &mf->Seg;
+}
+
+unsigned int mkv_GetNumTracks(MatroskaFile *mf) {
+ return mf->nTracks;
+}
+
+TrackInfo *mkv_GetTrackInfo(MatroskaFile *mf,unsigned track) {
+ if (track>mf->nTracks)
+ return NULL;
+
+ return mf->Tracks[track];
+}
+
+void mkv_GetAttachments(MatroskaFile *mf,Attachment **at,unsigned *count) {
+ *at = mf->Attachments;
+ *count = mf->nAttachments;
+}
+
+void mkv_GetChapters(MatroskaFile *mf,Chapter **ch,unsigned *count) {
+ *ch = mf->Chapters;
+ *count = mf->nChapters;
+}
+
+void mkv_GetTags(MatroskaFile *mf,Tag **tag,unsigned *count) {
+ *tag = mf->Tags;
+ *count = mf->nTags;
+}
+
+ulonglong mkv_GetSegmentTop(MatroskaFile *mf) {
+ return mf->pSegmentTop;
+}
+
+#define IS_DELTA(f) (!((f)->flags & FRAME_KF) || ((f)->flags & FRAME_UNKNOWN_START))
+
+void mkv_Seek(MatroskaFile *mf,ulonglong timecode,unsigned flags) {
+ int i,j,m,ret;
+ unsigned n,z,mask;
+ ulonglong m_kftime[MAX_TRACKS];
+ unsigned char m_seendf[MAX_TRACKS];
+
+ if (mf->flags & MKVF_AVOID_SEEKS)
+ return;
+
+ if (timecode == 0) {
+ EmptyQueues(mf);
+ mf->readPosition = mf->pCluster;
+ mf->tcCluster = mf->firstTimecode;
+ mf->flags &= ~MPF_ERROR;
+
+ return;
+ }
+
+ if (mf->nCues==0)
+ reindex(mf);
+
+ if (mf->nCues==0)
+ return;
+
+ mf->flags &= ~MPF_ERROR;
+
+ i = 0;
+ j = mf->nCues - 1;
+
+ for (;;) {
+ if (i>j) {
+ j = j>=0 ? j : 0;
+
+ if (setjmp(mf->jb)!=0)
+ return;
+
+ mkv_SetTrackMask(mf,mf->trackMask);
+
+ if (flags & MKVF_SEEK_TO_PREV_KEYFRAME) {
+ // we do this in two stages
+ // a. find the last keyframes before the require position
+ // b. seek to them
+
+ // pass 1
+ for (;;) {
+ for (n=0;n<mf->nTracks;++n) {
+ m_kftime[n] = MAXU64;
+ m_seendf[n] = 0;
+ }
+
+ EmptyQueues(mf);
+
+ mf->readPosition = mf->Cues[j].Position + mf->pSegment;
+ mf->tcCluster = mf->Cues[j].Time / mf->Seg.TimecodeScale;
+
+ for (;;) {
+ if ((ret = fillQueues(mf,0)) < 0 || ret == RBRESYNC)
+ return;
+
+ // drain queues until we get to the required timecode
+ for (n=0;n<mf->nTracks;++n) {
+ if (mf->Queues[n].head && mf->Queues[n].head->Start<timecode) {
+ if (IS_DELTA(mf->Queues[n].head))
+ m_seendf[n] = 1;
+ else
+ m_kftime[n] = mf->Queues[n].head->Start;
+ }
+
+ while (mf->Queues[n].head && mf->Queues[n].head->Start<timecode)
+ {
+ if (IS_DELTA(mf->Queues[n].head))
+ m_seendf[n] = 1;
+ else
+ m_kftime[n] = mf->Queues[n].head->Start;
+ QFree(mf,QGet(&mf->Queues[n]));
+ }
+
+ if (mf->Queues[n].head)
+ if (!IS_DELTA(mf->Queues[n].head))
+ m_kftime[n] = mf->Queues[n].head->Start;
+ }
+
+ for (n=0;n<mf->nTracks;++n)
+ if (mf->Queues[n].head && mf->Queues[n].head->Start>=timecode)
+ goto found;
+ }
+found:
+
+ for (n=0;n<mf->nTracks;++n)
+ if (!(mf->trackMask & (1<<n)) && m_kftime[n]==MAXU64 &&
+ m_seendf[n] && j>0)
+ {
+ // we need to restart the search from prev cue
+ --j;
+ goto again;
+ }
+
+ break;
+again:;
+ }
+ } else
+ for (n=0;n<mf->nTracks;++n)
+ m_kftime[n] = timecode;
+
+ // now seek to this timecode
+ EmptyQueues(mf);
+
+ mf->readPosition = mf->Cues[j].Position + mf->pSegment;
+ mf->tcCluster = mf->Cues[j].Time / mf->Seg.TimecodeScale;
+
+ for (mask=0;;) {
+ if ((ret = fillQueues(mf,mask)) < 0 || ret == RBRESYNC)
+ return;
+
+ // drain queues until we get to the required timecode
+ for (n=0;n<mf->nTracks;++n) {
+ struct QueueEntry *qe;
+ for (qe = mf->Queues[n].head;qe && qe->Start<m_kftime[n];qe = mf->Queues[n].head)
+ QFree(mf,QGet(&mf->Queues[n]));
+ }
+
+ for (n=z=0;n<mf->nTracks;++n)
+ if (m_kftime[n]==MAXU64 || (mf->Queues[n].head && mf->Queues[n].head->Start>=m_kftime[n])) {
+ ++z;
+ mask |= 1<<n;
+ }
+
+ if (z==mf->nTracks)
+ return;
+ }
+ }
+
+ m = (i+j)>>1;
+
+ if (timecode < mf->Cues[m].Time)
+ j = m-1;
+ else
+ i = m+1;
+ }
+}
+
+void mkv_SkipToKeyframe(MatroskaFile *mf) {
+ unsigned n,wait;
+ ulonglong ht;
+
+ if (setjmp(mf->jb)!=0)
+ return;
+
+ // remove delta frames from queues
+ do {
+ wait = 0;
+
+ if (fillQueues(mf,0)<0)
+ return;
+
+ for (n=0;n<mf->nTracks;++n)
+ if (mf->Queues[n].head && !(mf->Queues[n].head->flags & FRAME_KF)) {
+ ++wait;
+ QFree(mf,QGet(&mf->Queues[n]));
+ }
+ } while (wait);
+
+ // find highest queued time
+ for (n=0,ht=0;n<mf->nTracks;++n)
+ if (mf->Queues[n].head && ht<mf->Queues[n].head->Start)
+ ht = mf->Queues[n].head->Start;
+
+ // ensure the time difference is less than 100ms
+ do {
+ wait = 0;
+
+ if (fillQueues(mf,0)<0)
+ return;
+
+ for (n=0;n<mf->nTracks;++n)
+ while (mf->Queues[n].head && mf->Queues[n].head->next &&
+ (mf->Queues[n].head->next->flags & FRAME_KF) &&
+ ht - mf->Queues[n].head->Start > 100000000)
+ {
+ ++wait;
+ QFree(mf,QGet(&mf->Queues[n]));
+ }
+
+ } while (wait);
+}
+
+ulonglong mkv_GetLowestQTimecode(MatroskaFile *mf) {
+ unsigned n,seen;
+ ulonglong t;
+
+ // find the lowest queued timecode
+ for (n=seen=0,t=0;n<mf->nTracks;++n)
+ if (mf->Queues[n].head && (!seen || t > mf->Queues[n].head->Start))
+ t = mf->Queues[n].head->Start, seen=1;
+
+ return seen ? t : (ulonglong)LL(-1);
+}
+
+int mkv_TruncFloat(MKFLOAT f) {
+#ifdef MATROSKA_INTEGER_ONLY
+ return (int)(f.v >> 32);
+#else
+ return (int)f;
+#endif
+}
+
+#define FTRACK 0xffffffff
+
+void mkv_SetTrackMask(MatroskaFile *mf,unsigned int mask) {
+ unsigned int i;
+
+ if (mf->flags & MPF_ERROR)
+ return;
+
+ mf->trackMask = mask;
+
+ for (i=0;i<mf->nTracks;++i)
+ if (mask & (1<<i))
+ ClearQueue(mf,&mf->Queues[i]);
+}
+
+int mkv_ReadFrame(MatroskaFile *mf,
+ unsigned int mask,unsigned int *track,
+ ulonglong *StartTime,ulonglong *EndTime,
+ ulonglong *FilePos,unsigned int *FrameSize,
+ unsigned int *FrameFlags)
+{
+ unsigned int i,j;
+ struct QueueEntry *qe;
+
+ if (setjmp(mf->jb)!=0)
+ return -1;
+
+ do {
+ // extract required frame, use block with the lowest timecode
+ for (j=FTRACK,i=0;i<mf->nTracks;++i)
+ if (!(mask & (1<<i)) && mf->Queues[i].head) {
+ j = i;
+ ++i;
+ break;
+ }
+
+ for (;i<mf->nTracks;++i)
+ if (!(mask & (1<<i)) && mf->Queues[i].head &&
+ mf->Queues[j].head->Start > mf->Queues[i].head->Start)
+ j = i;
+
+ if (j != FTRACK) {
+ qe = QGet(&mf->Queues[j]);
+
+ *track = j;
+ *StartTime = qe->Start;
+ *EndTime = qe->End;
+ *FilePos = qe->Position;
+ *FrameSize = qe->Length;
+ *FrameFlags = qe->flags;
+
+ QFree(mf,qe);
+
+ return 0;
+ }
+
+ if (mf->flags & MPF_ERROR)
+ return -1;
+
+ } while (fillQueues(mf,mask)>=0);
+
+ return EOF;
+}
+
+#ifdef MATROSKA_COMPRESSION_SUPPORT
+/*************************************************************************
+ * Compressed streams support
+ ************************************************************************/
+struct CompressedStream {
+ MatroskaFile *mf;
+ z_stream zs;
+
+ /* current compressed frame */
+ ulonglong frame_pos;
+ unsigned frame_size;
+ char frame_buffer[2048];
+
+ /* decoded data buffer */
+ char decoded_buffer[2048];
+ unsigned decoded_ptr;
+ unsigned decoded_size;
+
+ /* error handling */
+ char errmsg[128];
+};
+
+CompressedStream *cs_Create(/* in */ MatroskaFile *mf,
+ /* in */ unsigned tracknum,
+ /* out */ char *errormsg,
+ /* in */ unsigned msgsize)
+{
+ CompressedStream *cs;
+ TrackInfo *ti;
+ int code;
+
+ ti = mkv_GetTrackInfo(mf, tracknum);
+ if (ti == NULL) {
+ strlcpy(errormsg, "No such track.", msgsize);
+ return NULL;
+ }
+
+ if (!ti->CompEnabled) {
+ strlcpy(errormsg, "Track is not compressed.", msgsize);
+ return NULL;
+ }
+
+ if (ti->CompMethod != COMP_ZLIB) {
+ strlcpy(errormsg, "Unsupported compression method.", msgsize);
+ return NULL;
+ }
+
+ cs = mf->cache->memalloc(mf->cache,sizeof(*cs));
+ if (cs == NULL) {
+ strlcpy(errormsg, "Ouf of memory.", msgsize);
+ return NULL;
+ }
+
+ memset(&cs->zs,0,sizeof(cs->zs));
+ code = inflateInit(&cs->zs);
+ if (code != Z_OK) {
+ strlcpy(errormsg, "ZLib error.", msgsize);
+ mf->cache->memfree(mf->cache,cs);
+ return NULL;
+ }
+
+ cs->frame_size = 0;
+ cs->decoded_ptr = cs->decoded_size = 0;
+ cs->mf = mf;
+
+ return cs;
+}
+
+void cs_Destroy(/* in */ CompressedStream *cs) {
+ if (cs == NULL)
+ return;
+
+ inflateEnd(&cs->zs);
+ cs->mf->cache->memfree(cs->mf->cache,cs);
+}
+
+/* advance to the next frame in matroska stream, you need to pass values returned
+ * by mkv_ReadFrame */
+void cs_NextFrame(/* in */ CompressedStream *cs,
+ /* in */ ulonglong pos,
+ /* in */ unsigned size)
+{
+ cs->zs.avail_in = 0;
+ inflateReset(&cs->zs);
+ cs->frame_pos = pos;
+ cs->frame_size = size;
+ cs->decoded_ptr = cs->decoded_size = 0;
+}
+
+/* read and decode more data from current frame, return number of bytes decoded,
+ * 0 on end of frame, or -1 on error */
+int cs_ReadData(CompressedStream *cs,char *buffer,unsigned bufsize)
+{
+ char *cp = buffer;
+ unsigned rd = 0;
+ unsigned todo;
+ int code;
+
+ do {
+ /* try to copy data from decoded buffer */
+ if (cs->decoded_ptr < cs->decoded_size) {
+ todo = cs->decoded_size - cs->decoded_ptr;;
+ if (todo > bufsize - rd)
+ todo = bufsize - rd;
+
+ memcpy(cp, cs->decoded_buffer + cs->decoded_ptr, todo);
+
+ rd += todo;
+ cp += todo;
+ cs->decoded_ptr += todo;
+ } else {
+ /* setup output buffer */
+ cs->zs.next_out = cs->decoded_buffer;
+ cs->zs.avail_out = sizeof(cs->decoded_buffer);
+
+ /* try to read more data */
+ if (cs->zs.avail_in == 0 && cs->frame_size > 0) {
+ todo = cs->frame_size;
+ if (todo > sizeof(cs->frame_buffer))
+ todo = sizeof(cs->frame_buffer);
+
+ if (cs->mf->cache->read(cs->mf->cache, cs->frame_pos, cs->frame_buffer, todo) != (int)todo) {
+ strlcpy(cs->errmsg, "File read failed", sizeof(cs->errmsg));
+ return -1;
+ }
+
+ cs->zs.next_in = cs->frame_buffer;
+ cs->zs.avail_in = todo;
+
+ cs->frame_pos += todo;
+ cs->frame_size -= todo;
+ }
+
+ /* try to decode more data */
+ code = inflate(&cs->zs,Z_NO_FLUSH);
+ if (code != Z_OK && code != Z_STREAM_END) {
+ strlcpy(cs->errmsg, "ZLib error.", sizeof(cs->errmsg));
+ return -1;
+ }
+
+ /* handle decoded data */
+ if (cs->zs.avail_out == sizeof(cs->decoded_buffer)) /* EOF */
+ break;
+
+ cs->decoded_ptr = 0;
+ cs->decoded_size = sizeof(cs->decoded_buffer) - cs->zs.avail_out;
+ }
+ } while (rd < bufsize);
+
+ return rd;
+}
+
+/* return error message for the last error */
+const char *cs_GetLastError(CompressedStream *cs)
+{
+ if (!cs->errmsg[0])
+ return NULL;
+ return cs->errmsg;
+}
+#endif
Added: trunk/MatroskaUtils/GDC/GDCMatroska/MatroskaParser.h
===================================================================
--- trunk/MatroskaUtils/GDC/GDCMatroska/MatroskaParser.h 2005-04-09 18:30:20 UTC (rev 1117)
+++ trunk/MatroskaUtils/GDC/GDCMatroska/MatroskaParser.h 2005-04-09 18:52:52 UTC (rev 1118)
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 2004-2005 Mike Matsnev. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Absolutely no warranty of function or purpose is made by the author
+ * Mike Matsnev.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: MatroskaParser.h,v 1.10 2005/01/10 04:52:34 mike Exp $
+ *
+ */
+
+#ifndef MATROSKA_PARSER_H
+#define MATROSKA_PARSER_H
+
+/* Random notes:
+ *
+ * The parser does not process frame data in any way and does not read it into
+ * the queue. The app should read it via mkv_ReadData if it is interested.
+ *
+ * The code here is 64-bit clean and was tested on FreeBSD/sparc 64-bit big endian
+ * system
+ */
+
+#ifdef MPDLLBUILD
+#define X __declspec(dllexport)
+#else
+#ifdef MPDLL
+#define X __declspec(dllimport)
+#pragma comment(lib,"MatroskaParser")
+#else
+#define X
+#endif
+#endif
+
+//#define MATROSKA_COMPRESSION_SUPPORT
+//#define MATROSKA_INTEGER_ONLY
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* 64-bit integers */
+#ifdef _WIN32_WCE
+typedef signed __int64 longlong;
+typedef unsigned __int64 ulonglong;
+#else
+typedef signed long long longlong;
+typedef unsigned long long ulonglong;
+#endif
+
+/* MKFLOATing point */
+#ifdef MATROSKA_INTEGER_ONLY
+typedef struct {
+ longlong v;
+} MKFLOAT;
+#else
+typedef double MKFLOAT;
+#endif
+
+/* generic I/O */
+struct InputStream {
+ /* read bytes from stream */
+ int (*read)(struct InputStream *cc,ulonglong pos,void *buffer,int count);
+ /* scan for a four byte signature, bytes must be nonzero */
+ longlong (*scan)(struct InputStream *cc,ulonglong start,unsigned signature);
+ /* get cache size, this is used to cap readahead */
+ unsigned (*getsize)(struct InputStream *cc);
+ /* fetch last error message */
+ const char *(*geterror)(struct InputStream *cc);
+ /* memory allocation */
+ void *(*memalloc)(struct InputStream *cc,size_t size);
+ void *(*memrealloc)(struct InputStream *cc,void *mem,size_t newsize);
+ void (*memfree)(struct InputStream *cc,void *mem);
+ // zero return causes parser to abort open
+ int (*progress)(struct InputStream *cc,ulonglong cur,ulonglong max);
+};
+
+typedef struct InputStream InputStream;
+
+/* matroska file */
+struct MatroskaFile; /* opaque */
+
+typedef struct MatroskaFile MatroskaFile;
+
+#define COMP_ZLIB 0
+
+#define TT_VIDEO 1
+#define TT_AUDIO 2
+#define TT_SUB 17
+
+struct TrackInfo {
+ unsigned char Number;
+ unsigned char Type;
+ unsigned char TrackOverlay;
+ ulonglong UID;
+ ulonglong MinCache;
+ ulonglong MaxCache;
+ ulonglong DefaultDuration;
+ MKFLOAT TimecodeScale;
+ void *CodecPrivate;
+ unsigned CodecPrivateSize;
+ unsigned CompMethod;
+ struct {
+ unsigned int Enabled:1;
+ unsigned int Default:1;
+ unsigned int Lacing:1;
+ unsigned int DecodeAll:1;
+ unsigned int CompEnabled:1;
+ };
+
+ union {
+ struct {
+ unsigned char StereoMode;
+ unsigned char DisplayUnit;
+ unsigned char AspectRatioType;
+ unsigned int PixelWidth;
+ unsigned int PixelHeight;
+ unsigned int DisplayWidth;
+ unsigned int DisplayHeight;
+ unsigned int ColourSpace;
+ MKFLOAT GammaValue;
+ struct {
+ unsigned int Interlaced:1;
+ };
+ } Video;
+ struct {
+ MKFLOAT SamplingFreq;
+ MKFLOAT OutputSamplingFreq;
+ unsigned char Channels;
+ unsigned char BitDepth;
+ } Audio;
+ };
+
+ /* various strings */
+ char *Name;
+ char *Language;
+ char *CodecID;
+};
+
+typedef struct TrackInfo TrackInfo;
+
+struct SegmentInfo {
+ char UID[16];
+ char PrevUID[16];
+ char NextUID[16];
+ char *Filename;
+ char *PrevFilename;
+ char *NextFilename;
+ char *Title;
+ char *MuxingApp;
+ char *WritingApp;
+ ulonglong TimecodeScale;
+ ulonglong Duration;
+ ulonglong DateUTC;
+};
+
+typedef struct SegmentInfo SegmentInfo;
+
+struct Attachment {
+ ulonglong Position;
+ ulonglong Length;
+ ulonglong UID;
+ char *Name;
+ char *Description;
+ char *MimeType;
+};
+
+typedef struct Attachment Attachment;
+
+struct ChapterDisplay {
+ char *String;
+ char *Language;
+ char *Country;
+};
+
+struct ChapterCommand {
+ unsigned Time;
+ unsigned CommandLength;
+ char *Command;
+};
+
+struct ChapterProcess {
+ unsigned CodecID;
+ unsigned CodecPrivateLength;
+ char *CodecPrivate;
+ unsigned nCommands,nCommandsSize;
+ struct ChapterCommand *Commands;
+};
+
+struct Chapter {
+ ulonglong UID;
+ ulonglong Start;
+ ulonglong End;
+
+ unsigned nTracks,nTracksSize;
+ ulonglong *Tracks;
+ unsigned nDisplay,nDisplaySize;
+ struct ChapterDisplay *Display;
+ unsigned nChildren,nChildrenSize;
+ struct Chapter *Children;
+ unsigned nProcess,nProcessSize;
+ struct ChapterProcess *Process;
+
+ struct {
+ unsigned int Hidden:1;
+ unsigned int Enabled:1;
+
+ // Editions
+ unsigned int Default:1;
+ unsigned int Ordered:1;
+ };
+};
+
+typedef struct Chapter Chapter;
+
+#define TARGET_TRACK 0
+#define TARGET_CHAPTER 1
+#define TARGET_ATTACHMENT 2
+#define TARGET_EDITION 3
+struct Target {
+ ulonglong UID;
+ unsigned Type;
+};
+
+struct SimpleTag {
+ char *Name;
+ char *Value;
+ char *Language;
+ unsigned Default:1;
+};
+
+struct Tag {
+ unsigned nTargets,nTargetsSize;
+ struct Target *Targets;
+
+ unsigned nSimpleTags,nSimpleTagsSize;
+ struct SimpleTag *SimpleTags;
+};
+
+typedef struct Tag Tag;
+
+/* Open a matroska file
+ * io pointer is recorded inside MatroskaFile
+ */
+X MatroskaFile *mkv_Open(/* in */ InputStream *io,
+ /* out */ char *err_msg,
+ /* in */ unsigned msgsize);
+
+#define MKVF_AVOID_SEEKS 1 /* use sequential reading only */
+
+X MatroskaFile *mkv_OpenEx(/* in */ InputStream *io,
+ /* in */ ulonglong base,
+ /* in */ unsigned flags,
+ /* out */ char *err_msg,
+ /* in */ unsigned msgsize);
+
+/* Close and deallocate mf
+ * NULL pointer is ok and is simply ignored
+ */
+X void mkv_Close(/* in */ MatroskaFile *mf);
+
+/* Fetch the error message of the last failed operation */
+X const char *mkv_GetLastError(/* in */ MatroskaFile *mf);
+
+/* Get file information */
+X SegmentInfo *mkv_GetFileInfo(/* in */ MatroskaFile *mf);
+
+/* Get track information */
+X unsigned int mkv_GetNumTracks(/* in */ MatroskaFile *mf);
+X TrackInfo *mkv_GetTrackInfo(/* in */ MatroskaFile *mf,/* in */ unsigned track);
+
+/* chapters, tags and attachments */
+X void mkv_GetAttachments(/* in */ MatroskaFile *mf,
+ /* out */ Attachment **at,
+ /* out */ unsigned *count);
+X void mkv_GetChapters(/* in */ MatroskaFile *mf,
+ /* out */ Chapter **ch,
+ /* out */ unsigned *count);
+X void mkv_GetTags(/* in */ MatroskaFile *mf,
+ /* out */ Tag **tag,
+ /* out */ unsigned *count);
+
+X ulonglong mkv_GetSegmentTop(MatroskaFile *mf);
+
+/* Seek to specified timecode,
+ * if timecode is past end of file,
+ * all tracks are set to return EOF
+ * on next read
+ */
+#define MKVF_SEEK_TO_PREV_KEYFRAME 1
+
+X void mkv_Seek(/* in */ MatroskaFile *mf,
+ /* in */ ulonglong timecode /* in ns */,
+ /* in */ unsigned flags);
+
+X void mkv_SkipToKeyframe(MatroskaFile *mf);
+
+X ulonglong mkv_GetLowestQTimecode(MatroskaFile *mf);
+
+X int mkv_TruncFloat(MKFLOAT f);
+
+/*************************************************************************
+ * reading data, pull model
+ */
+
+/* frame flags */
+#define FRAME_KF 0x00000001
+#define FRAME_UNKNOWN_START 0x00000002
+#define FRAME_UNKNOWN_END 0x00000004
+#define FRAME_GAP 0x00000008
+
+/* This sets the masking flags for the parser,
+ * masked tracks [with 1s in their bit positions]
+ * will be ignored when reading file data.
+ * This call discards all parsed and queued frames
+ */
+X void mkv_SetTrackMask(/* in */ MatroskaFile *mf,/* in */ unsigned int mask);
+
+/* Read one frame from the queue.
+ * mask specifies what tracks to ignore.
+ * Returns -1 if there are no more frames in the specified
+ * set of tracks, 0 on success
+ */
+X int mkv_ReadFrame(/* in */ MatroskaFile *mf,
+ /* in */ unsigned int mask,
+ /* out */ unsigned int *track,
+ /* out */ ulonglong *StartTime /* in ns */,
+ /* out */ ulonglong *EndTime /* in ns */,
+ /* out */ ulonglong *FilePos /* in bytes from start of file */,
+ /* out */ unsigned int *FrameSize /* in bytes */,
+ /* out */ unsigned int *FrameFlags);
+
+/* Read raw frame data, invokes underlying io methods,
+ * but keeps track of the file pointer for internal buffering.
+ * Returns the -1 if error occurred, or 0 on success
+ */
+X int mkv_ReadData(/* in */ MatroskaFile *mf,
+ /* in */ ulonglong FilePos,
+ /* out */ void *Buffer,
+ /* in */ unsigned int Count);
+
+#ifdef MATROSKA_COMPRESSION_SUPPORT
+/* Compressed streams support */
+struct CompressedStream;
+
+typedef struct CompressedStream CompressedStream;
+
+X CompressedStream *cs_Create(/* in */ MatroskaFile *mf,
+ /* in */ unsigned tracknum,
+ /* out */ char *errormsg,
+ /* in */ unsigned msgsize);
+X void cs_Destroy(/* in */ CompressedStream *cs);
+
+/* advance to the next frame in matroska stream, you need to pass values returned
+ * by mkv_ReadFrame */
+X void cs_NextFrame(/* in */ CompressedStream *cs,
+ /* in */ ulonglong pos,
+ /* in */ unsigned size);
+
+/* read and decode more data from current frame, return number of bytes decoded,
+ * 0 on end of frame, or -1 on error */
+X int cs_ReadData(CompressedStream *cs,char *buffer,unsigned bufsize);
+
+/* return error message for the last error */
+X const char *cs_GetLastError(CompressedStream *cs);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#undef X
+
+#endif
Added: trunk/MatroskaUtils/GDC/GDCMatroska/Win32Stream.c
===================================================================
--- trunk/MatroskaUtils/GDC/GDCMatroska/Win32Stream.c 2005-04-09 18:30:20 UTC (rev 1117)
+++ trunk/MatroskaUtils/GDC/GDCMatroska/Win32Stream.c 2005-04-09 18:52:52 UTC (rev 1118)
@@ -0,0 +1,133 @@
+#include "Win32Stream.h"
+
+#define CACHESIZE 65536
+
+/* Win32Stream methods */
+
+/* read count bytes into buffer starting at file position pos
+* return the number of bytes read, -1 on error or 0 on EOF
+*/
+static int Win32Read(Win32Stream *st, ulonglong pos, void *buffer, int count) {
+ DWORD BytesRead;
+ LONG High = (LONG)(pos>>32);
+ SetFilePointer(st->fp, (LONG)(pos & 0xffffffff), &High, FILE_BEGIN);
+
+ if (!ReadFile(st->fp, buffer, count, &BytesRead, NULL)) {
+ return 0;
+ }
+ return (int)BytesRead;
+}
+
+/* scan for a signature sig(big-endian) starting at file position pos
+* return position of the first byte of signature or -1 if error/not found
+*/
+static longlong Win32Scan(Win32Stream *st, ulonglong start, unsigned signature) {
+ char c;
+ unsigned cmp = 0;
+ ulonglong pos = start;
+ LONG High = (LONG)(start>>32);
+
+ SetFilePointer(st->fp, (LONG)(start & 0xffffffff), &High, FILE_BEGIN);
+
+ while (Win32Read(st, pos, &c, 1) != 0) {
+ cmp = ((cmp << 8) | c) & 0xffffffff;
+ if (cmp == signature)
+ return pos - 4;
+ pos++;
+ }
+
+ return -1;
+}
+
+/* return cache size, this is used to limit readahead */
+static unsigned Win32GetCacheSize(Win32Stream *st) {
+ return CACHESIZE;
+}
+
+/* return last error message */
+static const char *Win32GetLastError(Win32Stream *st) {
+ return strerror(st->error);
+}
+
+/* memory allocation, this is done via stdlib */
+static void *Win32Malloc(Win32Stream *st, size_t size) {
+ return malloc(size);
+}
+
+static void *Win32Realloc(Win32Stream *st, void *mem, size_t size) {
+ return realloc(mem,size);
+}
+
+static void Win32Free(Win32Stream *st, void *mem) {
+ free(mem);
+}
+
+/* progress report handler for lengthy operations
+* returns 0 to abort operation, nonzero to continue
+*/
+static int Win32Progress(Win32Stream *st, ulonglong cur, ulonglong max) {
+ return 1;
+}
+
+void Win32Stream_Init(Win32Stream *st)
+{
+ /* fill in I/O object */
+ memset(st, 0, sizeof(Win32Stream));
+ st->base.read = Win32Read;
+ st->base.scan = Win32Scan;
+ st->base.getsize = Win32GetCacheSize;
+ st->base.geterror = Win32GetLastError;
+ st->base.memalloc = Win32Malloc;
+ st->base.memrealloc = Win32Realloc;
+ st->base.memfree = Win32Free;
+ st->base.progress = Win32Progress;
+}
+
+int Win32Stream_Open(Win32Stream *st, const char *filename)
+{
+ return Win32Stream_OpenA(st, filename);
+}
+
+int Win32Stream_OpenA(Win32Stream *st, const char *filename)
+{
+ DWORD AccessMode, ShareMode, Disposition;
+ DWORD dwFlags = 0;
+
+ AccessMode = GENERIC_READ;
+ ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE;
+ Disposition = OPEN_EXISTING;
+
+ st->fp = CreateFileA(filename, AccessMode, ShareMode, NULL, Disposition, dwFlags, NULL);
+ if ((st->fp == INVALID_HANDLE_VALUE) || ((long)st->fp == 0xffffffff))
+ {
+ // File was not opened
+ return (int)GetLastError();
+ }
+
+ return 0;
+}
+
+int Win32Stream_OpenW(Win32Stream *st, const wchar_t *filename)
+{
+ DWORD AccessMode, ShareMode, Disposition;
+ DWORD dwFlags = 0;
+
+ AccessMode = GENERIC_READ;
+ ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE;
+ Disposition = OPEN_EXISTING;
+
+ st->fp = CreateFileW(filename, AccessMode, ShareMode, NULL, Disposition, dwFlags, NULL);
+ if ((st->fp == INVALID_HANDLE_VALUE) || ((long)st->fp == 0xffffffff))
+ {
+ // File was not opened
+ return (int)GetLastError();
+ }
+
+ return 0;
+}
+
+void Win32Stream_Close(Win32Stream *st)
+{
+ CloseHandle(st->fp);
+ st->fp = NULL;
+}
Added: trunk/MatroskaUtils/GDC/GDCMatroska/Win32Stream.h
===================================================================
--- trunk/MatroskaUtils/GDC/GDCMatroska/Win32Stream.h 2005-04-09 18:30:20 UTC (rev 1117)
+++ trunk/MatroskaUtils/GDC/GDCMatroska/Win32Stream.h 2005-04-09 18:52:52 UTC (rev 1118)
@@ -0,0 +1,29 @@
+#ifndef _WIN32_STREAM_H_
+#define _WIN32_STREAM_H_
+
+#include <windows.h>
+#include "MatroskaParser.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Win32Stream {
+ struct InputStream base;
+ HANDLE fp;
+ int error;
+};
+typedef struct Win32Stream Win32Stream;
+
+// Helper methods
+void Win32Stream_Init(Win32Stream *st);
+int Win32Stream_Open(Win32Stream *st, const char *filename);
+int Win32Stream_OpenA(Win32Stream *st, const char *filename);
+int Win32Stream_OpenW(Win32Stream *st, const wchar_t *filename);
+void Win32Stream_Close(Win32Stream *st);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _WIN32_STREAM_H_
\ No newline at end of file
Added: trunk/MatroskaUtils/GDC/GDCMatroska-Setup/GDCMatroska-Setup.vdproj
===================================================================
--- trunk/MatroskaUtils/GDC/GDCMatroska-Setup/GDCMatroska-Setup.vdproj 2005-04-09 18:30:20 UTC (rev 1117)
+++ trunk/MatroskaUtils/GDC/GDCMatroska-Setup/GDCMatroska-Setup.vdproj 2005-04-09 18:52:52 UTC (rev 1118)
@@ -0,0 +1,689 @@
+"DeployProject"
+{
+"VSVersion" = "3:701"
+"ProjectType" = "8:{2C2AF0D9-9B47-4FE5-BEF2-169778172667}"
+"IsWebType" = "8:FALSE"
+"ProjectName" = "8:GDCMatroska-Setup"
+"LanguageId" = "3:1033"
+"CodePage" = "3:1252"
+"UILanguageId" = "3:1033"
+"SccProjectName" = "8:"
+"SccLocalPath" = "8:"
+"SccAuxPath" = "8:"
+"SccProvider" = "8:"
+ "Hierarchy"
+ {
+ "Entry"
+ {
+ "MsmKey" = "8:_66C3065023A74F609C625313053DA8F8"
+ "OwnerKey" = "8:_E4F05E1860E8454A9F39C151AA3D3A86"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_E4F05E1860E8454A9F39C151AA3D3A86"
+ "OwnerKey" = "8:_UNDEFINED"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ }
+ "Configurations"
+ {
+ "Debug"
+ {
+ "DisplayName" = "8:Debug"
+ "IsDebugOnly" = "11:TRUE"
+ "IsReleaseOnly" = "11:FALSE"
+ "OutputFilename" = "8:Debug\\GDCMatroska-Setup.msi"
+ "PackageFilesAs" = "3:2"
+ "PackageFileSize" = "3:-2147483648"
+ "CabType" = "3:1"
+ "Compression" = "3:2"
+ "SignOutput" = "11:FALSE"
+ "CertificateFile" = "8:"
+ "PrivateKeyFile" = "8:"
+ "TimeStampServer" = "8:"
+ "InstallerBootstrapper" = "3:2"
+ }
+ "Release"
+ {
+ "DisplayName" = "8:Release"
+ "IsDebugOnly" = "11:FALSE"
+ "IsReleaseOnly" = "11:TRUE"
+ "OutputFilename" = "8:Release\\GDCMatroska-Setup.msi"
+ "PackageFilesAs" = "3:2"
+ "PackageFileSize" = "3:-2147483648"
+ "CabType" = "3:1"
+ "Compression" = "3:3"
+ "SignOutput" = "11:FALSE"
+ "CertificateFile" = "8:"
+ "PrivateKeyFile" = "8:"
+ "TimeStampServer" = "8:"
+ "InstallerBootstrapper" = "3:2"
+ }
+ }
+ "Deployable"
+ {
+ "CustomAction"
+ {
+ }
+ "DefaultFeature"
+ {
+ "Name" = "8:DefaultFeature"
+ "Title" = "8:"
+ "Description" = "8:"
+ }
+ "ExternalPersistence"
+ {
+ "LaunchCondition"
+ {
+ }
+ }
+ "Feature"
+ {
+ }
+ "File"
+ {
+ }
+ "FileType"
+ {
+ }
+ "Folder"
+ {
+ "{58C0ADA3-3CEA-43BD-A3B3-2EA121BC8217}:_5E96DF27E65D45FB9A81C83FD074A586"
+ {
+ "DefaultLocation" = "8:[ProgramFilesFolder][ProductName]"
+ "Name" = "8:#1925"
+ "AlwaysCreate" = "11:FALSE"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Property" = "8:TARGETDIR"
+ "Folders"
+ {
+ }
+ }
+ "{78BAF5CE-F2E5-45BE-83BC-DB6AF387E941}:_66B776E609CA46389E9E85C159778E30"
+ {
+ "Name" = "8:#1919"
+ "AlwaysCreate" = "11:FALSE"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Property" = "8:ProgramMenuFolder"
+ "Folders"
+ {
+ }
+ }
+ "{78BAF5CE-F2E5-45BE-83BC-DB6AF387E941}:_79C70FF64A3B400C88C3F0CFA73D0246"
+ {
+ "Name" = "8:#1916"
+ "AlwaysCreate" = "11:FALSE"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Property" = "8:DesktopFolder"
+ "Folders"
+ {
+ }
+ }
+ }
+ "LaunchCondition"
+ {
+ }
+ "Locator"
+ {
+ }
+ "MsiBootstrapper"
+ {
+ "LangId" = "3:1033"
+ }
+ "Product"
+ {
+ "Name" = "8:Microsoft Visual Studio"
+ "ProductName" = "8:GDCMatroska"
+ "ProductCode" = "8:{ACAC7295-2780-4B24-9E3D-AE1F703A0268}"
+ "PackageCode" = "8:{8FF50B41-09F3-4415-BDA0-BE37E07D1D27}"
+ "UpgradeCode" = "8:{884E153F-08C8-40A9-86E4-46B1C75596B6}"
+ "RestartWWWService" = "11:FALSE"
+ "RemovePreviousVersions" = "11:TRUE"
+ "DetectNewerInstalledVersion" = "11:TRUE"
+ "ProductVersion" = "8:1.0.0"
+ "Manufacturer" = "8:Jory Stone"
+ "ARPHELPTELEPHONE" = "8:"
+ "ARPHELPLINK" = "8:"
+ "Title" = "8:GDC Matroska"
+ "Subject" = "8:"
+ "ARPCONTACT" = "8:Jory Stone"
+ "Keywords" = "8:"
+ "ARPCOMMENTS" = "8:Google Desktop Search plugin for Matroska (http://www.matroska.org) files"
+ "ARPURLINFOABOUT" = "8:http://www.jory.info/"
+ "ARPPRODUCTICON" = "8:"
+ "ARPIconIndex" = "3:0"
+ "SearchPath" = "8:"
+ "UseSystemSearchPath" = "11:TRUE"
+ }
+ "Registry"
+ {
+ "HKLM"
+ {
+ "Keys"
+ {
+ "{6A471EEF-D31B-40F8-BCF6-C9E8EC783F36}:_91017A2C2F4B4FEA97BDD24019B232E5"
+ {
+ "Name" = "8:Software"
+ "Condition" = "8:"
+ "AlwaysCreate" = "11:FALSE"
+ "DeleteAtUninstall" = "11:FALSE"
+ "Transitive" = "11:FALSE"
+ "Keys"
+ {
+ "{6A471EEF-D31B-40F8-BCF6-C9E8EC783F36}:_11A0764738334FF28E2EA73091721C67"
+ {
+ "Name" = "8:[Manufacturer]"
+ "Condition" = "8:"
+ "AlwaysCreate" = "11:FALSE"
+ "DeleteAtUninstall" = "11:FALSE"
+ "Transitive" = "11:FALSE"
+ "Keys"
+ {
+ }
+ "Values"
+ {
+ }
+ }
+ }
+ "Values"
+ {
+ }
+ }
+ }
+ }
+ "HKCU"
+ {
+ "Keys"
+ {
+ "{6A471EEF-D31B-40F8-BCF6-C9E8EC783F36}:_82D4D49B9C7D43658D6E97AB3F16C20F"
+ {
+ "Name" = "8:Software"
+ "Condition" = "8:"
+ "AlwaysCreate" = "11:FALSE"
+ "DeleteAtUninstall" = "11:FALSE"
+ "Transitive" = "11:FALSE"
+ "Keys"
+ {
+ "{6A471EEF-D31B-40F8-BCF6-C9E8EC783F36}:_3798F9082C44481FA416A0C050DEA771"
+ {
+ "Name" = "8:[Manufacturer]"
+ "Condition" = "8:"
+ "AlwaysCreate" = "11:FALSE"
+ "DeleteAtUninstall" = "11:FALSE"
+ "Transitive" = "11:FALSE"
+ "Keys"
+ {
+ }
+ "Values"
+ {
+ }
+ }
+ }
+ "Values"
+ {
+ }
+ }
+ }
+ }
+ "HKCR"
+ {
+ "Keys"
+ {
+ }
+ }
+ "HKU"
+ {
+ "Keys"
+ {
+ }
+ }
+ "HKPU"
+ {
+ "Keys"
+ {
+ }
+ }
+ }
+ "Sequences"
+ {
+ }
+ "Shortcut"
+ {
+ }
+ "UserInterface"
+ {
+ "{B654A020-6903-4E6A-A86C-75DC463DB54B}:_26BD223BEAF44137BAD38A211617F2E9"
+ {
+ "UseDynamicProperties" = "11:FALSE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdUserInterface.wim"
+ }
+ "{8D9DEE8B-DD8B-4F48-9072-C4364E4F4011}:_38BB4BED33F244738C4AC960CC0C8001"
+ {
+ "Name" = "8:#1901"
+ "Sequence" = "3:1"
+ "Attributes" = "3:2"
+ "Dialogs"
+ {
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_B0B3B5C1F139416CA30BEC9724DD7C68"
+ {
+ "Sequence" = "3:100"
+ "DisplayName" = "8:Progress"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdProgressDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "ShowProgress"
+ {
+ "Name" = "8:ShowProgress"
+ "DisplayName" = "8:#1009"
+ "Description" = "8:#1109"
+ "Type" = "3:5"
+ "ContextData" = "8:1;True=1;False=0"
+ "Attributes" = "3:0"
+ "Setting" = "3:0"
+ "Value" = "3:1"
+ "DefaultValue" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ }
+ }
+ "{8D9DEE8B-DD8B-4F48-9072-C4364E4F4011}:_3F751F04EDA24F67B1F104C0075D6250"
+ {
+ "Name" = "8:#1900"
+ "Sequence" = "3:2"
+ "Attributes" = "3:1"
+ "Dialogs"
+ {
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_7A6D9140B95E4EF1B89EA075657BC505"
+ {
+ "Sequence" = "3:200"
+ "DisplayName" = "8:Installation Folder"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdAdminFolderDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_D0A26A2B99634D2CAB2D13175B68C73F"
+ {
+ "Sequence" = "3:100"
+ "DisplayName" = "8:Welcome"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdAdminWelcomeDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "CopyrightWarning"
+ {
+ "Name" = "8:CopyrightWarning"
+ "DisplayName" = "8:#1002"
+ "Description" = "8:#1102"
+ "Type" = "3:3"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:1"
+ "Value" = "8:#1202"
+ "DefaultValue" = "8:#1202"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Welcome"
+ {
+ "Name" = "8:Welcome"
+ "DisplayName" = "8:#1003"
+ "Description" = "8:#1103"
+ "Type" = "3:3"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:1"
+ "Value" = "8:#1203"
+ "DefaultValue" = "8:#1203"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_DA1202FB917B4357B4E84104848A65AE"
+ {
+ "Sequence" = "3:300"
+ "DisplayName" = "8:Confirm Installation"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdAdminConfirmDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ }
+ }
+ "{8D9DEE8B-DD8B-4F48-9072-C4364E4F4011}:_58460D94ED6D45DAA6F984638440BEEB"
+ {
+ "Name" = "8:#1902"
+ "Sequence" = "3:2"
+ "Attributes" = "3:3"
+ "Dialogs"
+ {
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_89A82E63E01942A992AD270A2E4505DF"
+ {
+ "Sequence" = "3:100"
+ "DisplayName" = "8:Finished"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdAdminFinishedDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ }
+ }
+ "{8D9DEE8B-DD8B-4F48-9072-C4364E4F4011}:_74EF7D43B5254DCAB47620B555A35AFF"
+ {
+ "Name" = "8:#1902"
+ "Sequence" = "3:1"
+ "Attributes" = "3:3"
+ "Dialogs"
+ {
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_2D9D9DBC01B04592A4CE920E554E4975"
+ {
+ "Sequence" = "3:100"
+ "DisplayName" = "8:Finished"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdFinishedDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "UpdateText"
+ {
+ "Name" = "8:UpdateText"
+ "DisplayName" = "8:#1058"
+ "Description" = "8:#1158"
+ "Type" = "3:15"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:1"
+ "Value" = "8:#1258"
+ "DefaultValue" = "8:#1258"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ }
+ }
+ "{8D9DEE8B-DD8B-4F48-9072-C4364E4F4011}:_7AD9EA6D9E654E7C8ADE395310AA72BE"
+ {
+ "Name" = "8:#1901"
+ "Sequence" = "3:2"
+ "Attributes" = "3:2"
+ "Dialogs"
+ {
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_2BC68147F0B24346B444A9381E6467FE"
+ {
+ "Sequence" = "3:100"
+ "DisplayName" = "8:Progress"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdAdminProgressDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "ShowProgress"
+ {
+ "Name" = "8:ShowProgress"
+ "DisplayName" = "8:#1009"
+ "Description" = "8:#1109"
+ "Type" = "3:5"
+ "ContextData" = "8:1;True=1;False=0"
+ "Attributes" = "3:0"
+ "Setting" = "3:0"
+ "Value" = "3:1"
+ "DefaultValue" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ }
+ }
+ "{8D9DEE8B-DD8B-4F48-9072-C4364E4F4011}:_B3FB25833E0144E8B64CE2827A0D557F"
+ {
+ "Name" = "8:#1900"
+ "Sequence" = "3:1"
+ "Attributes" = "3:1"
+ "Dialogs"
+ {
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_25ACFA11F84D4623A06452B4C53CA704"
+ {
+ "Sequence" = "3:100"
+ "DisplayName" = "8:Welcome"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdWelcomeDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "CopyrightWarning"
+ {
+ "Name" = "8:CopyrightWarning"
+ "DisplayName" = "8:#1002"
+ "Description" = "8:#1102"
+ "Type" = "3:3"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:1"
+ "Value" = "8:#1202"
+ "DefaultValue" = "8:#1202"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ "Welcome"
+ {
+ "Name" = "8:Welcome"
+ "DisplayName" = "8:#1003"
+ "Description" = "8:#1103"
+ "Type" = "3:3"
+ "ContextData" = "8:"
+ "Attributes" = "3:0"
+ "Setting" = "3:1"
+ "Value" = "8:#1203"
+ "DefaultValue" = "8:#1203"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_720DB0273B7246BC98C89098F314F307"
+ {
+ "Sequence" = "3:300"
+ "DisplayName" = "8:Confirm Installation"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdConfirmDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ "{18ADD6EC-89FE-4ED7-AD3E-211C40278470}:_AE4F2ED222C94D67A81227D137F31AF5"
+ {
+ "Sequence" = "3:200"
+ "DisplayName" = "8:Installation Folder"
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdFolderDlg.wid"
+ "Properties"
+ {
+ "BannerBitmap"
+ {
+ "Name" = "8:BannerBitmap"
+ "DisplayName" = "8:#1001"
+ "Description" = "8:#1101"
+ "Type" = "3:8"
+ "ContextData" = "8:Bitmap"
+ "Attributes" = "3:4"
+ "Setting" = "3:1"
+ "UsePlugInResources" = "11:TRUE"
+ }
+ }
+ }
+ }
+ }
+ "{B654A020-6903-4E6A-A86C-75DC463DB54B}:_E92AB8923820480FB0A10BC986FCB86B"
+ {
+ "UseDynamicProperties" = "11:FALSE"
+ "IsDependency" = "11:FALSE"
+ "SourcePath" = "8:<VsdDialogDir>\\VsdBasicDialogs.wim"
+ }
+ }
+ "MergeModule"
+ {
+ "{35A69C6E-5BA4-440D-803D-762B59A45393}:_66C3065023A74F609C625313053DA8F8"
+ {
+ "UseDynamicProperties" = "11:TRUE"
+ "IsDependency" = "11:TRUE"
+ "SourcePath" = "8:vc_user_crt71_rtl_x86_---.msm"
+ "Properties"
+ {
+ }
+ "LanguageId" = "3:0"
+ "Exclude" = "11:FALSE"
+ "Folder" = "8:"
+ "Feature" = "8:"
+ "IsolateTo" = "8:"
+ }
+ }
+ "ProjectOutput"
+ {
+ "{8062640A-2EEE-46E9-AB67-688E9A886E9F}:_E4F05E1860E8454A9F39C151AA3D3A86"
+ {
+ "SourcePath" = "8:..\\GDCMatroska\\Release\\GDCMatroska.dll"
+ "TargetName" = "8:"
+ "Tag" = "8:"
+ "Folder" = "8:_5E96DF27E65D45FB9A81C83FD074A586"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Vital" = "11:TRUE"
+ "ReadOnly" = "11:FALSE"
+ "Hidden" = "11:FALSE"
+ "System" = "11:FALSE"
+ "Permanent" = "11:FALSE"
+ "SharedLegacy" = "11:FALSE"
+ "PackageAs" = "3:1"
+ "Register" = "3:1"
+ "Exclude" = "11:FALSE"
+ "IsDependency" = "11:FALSE"
+ "IsolateTo" = "8:"
+ "ProjectOutputGroupRegister" = "3:4"
+ "OutputConfiguration" = "8:"
+ "OutputGroupCanonicalName" = "8:Built"
+ "OutputProjectGuid" = "8:{D850DCDA-78AD-42C1-BF71-B5995900BCA9}"
+ "ShowKeyOutput" = "11:TRUE"
+ "ExcludeFilters"
+ {
+ }
+ }
+ }
+ "VJSharpPlugin"
+ {
+ }
+ }
+}
Modified: trunk/MatroskaUtils/GDC/GoogleDesktopComponent.sln
===================================================================
--- trunk/MatroskaUtils/GDC/GoogleDesktopComponent.sln 2005-04-09 18:30:20 UTC (rev 1117)
+++ trunk/MatroskaUtils/GDC/GoogleDesktopComponent.sln 2005-04-09 18:52:52 UTC (rev 1118)
@@ -1,47 +1,23 @@
Microsoft Visual Studio Solution File, Format Version 8.00
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmatroska", "..\..\libmatroska\make\vc7\lib\static\libmatroska.v71.vcproj", "{FD69BB21-A9BF-4486-9B8A-F0E88C82F704}"
- ProjectSection(ProjectDependencies) = postProject
- {168C0F0D-9975-4D24-885C-87A9B018617E} = {168C0F0D-9975-4D24-885C-87A9B018617E}
- EndProjectSection
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libebml", "..\..\libebml\make\vc7\lib\libebml.v71.vcproj", "{168C0F0D-9975-4D24-885C-87A9B018617E}"
- ProjectSection(ProjectDependencies) = postProject
- EndProjectSection
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MatroskaUtils", "..\MatroskaUtils.vcproj", "{E3C2A4E1-3DC5-4B67-A3F1-D8AB645DC750}"
- ProjectSection(ProjectDependencies) = postProject
- EndProjectSection
-EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GoogleDesktopComponentCommon", "common\GoogleDesktopComponentCommon.vcproj", "{5A0D55C3-7C2A-4B61-8F28-6B757052788F}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GDCMatroska", "GDCMatroska\GDCMatroska.vcproj", "{D850DCDA-78AD-42C1-BF71-B5995900BCA9}"
ProjectSection(ProjectDependencies) = postProject
- {168C0F0D-9975-4D24-885C-87A9B018617E} = {168C0F0D-9975-4D24-885C-87A9B018617E}
- {FD69BB21-A9BF-4486-9B8A-F0E88C82F704} = {FD69BB21-A9BF-4486-9B8A-F0E88C82F704}
{5A0D55C3-7C2A-4B61-8F28-6B757052788F} = {5A0D55C3-7C2A-4B61-8F28-6B757052788F}
- {E3C2A4E1-3DC5-4B67-A3F1-D8AB645DC750} = {E3C2A4E1-3DC5-4B67-A3F1-D8AB645DC750}
EndProjectSection
EndProject
+Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "GDCMatroska-Setup", "GDCMatroska-Setup\GDCMatroska-Setup.vdproj", "{79B454A7-63FC-4774-9EF1-A823D2CC0DB9}"
+ ProjectSection(ProjectDependencies) = postProject
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfiguration) = preSolution
Debug = Debug
Release = Release
EndGlobalSection
GlobalSection(ProjectConfiguration) = postSolution
- {FD69BB21-A9BF-4486-9B8A-F0E88C82F704}.Debug.ActiveCfg = Debug|Win32
- {FD69BB21-A9BF-4486-9B8A-F0E88C82F704}.Debug.Build.0 = Debug|Win32
- {FD69BB21-A9BF-4486-9B8A-F0E88C82F704}.Release.ActiveCfg = Release|Win32
- {FD69BB21-A9BF-4486-9B8A-F0E88C82F704}.Release.Build.0 = Release|Win32
- {168C0F0D-9975-4D24-885C-87A9B018617E}.Debug.ActiveCfg = Debug|Win32
- {168C0F0D-9975-4D24-885C-87A9B018617E}.Debug.Build.0 = Debug|Win32
- {168C0F0D-9975-4D24-885C-87A9B018617E}.Release.ActiveCfg = Release|Win32
- {168C0F0D-9975-4D24-885C-87A9B018617E}.Release.Build.0 = Release|Win32
- {E3C2A4E1-3DC5-4B67-A3F1-D8AB645DC750}.Debug.ActiveCfg = Debug Minimal|Win32
- {E3C2A4E1-3DC5-4B67-A3F1-D8AB645DC750}.Debug.Build.0 = Debug Minimal|Win32
- {E3C2A4E1-3DC5-4B67-A3F1-D8AB645DC750}.Release.ActiveCfg = Release Minimal|Win32
- {E3C2A4E1-3DC5-4B67-A3F1-D8AB645DC750}.Release.Build.0 = Release Minimal|Win32
{5A0D55C3-7C2A-4B61-8F28-6B757052788F}.Debug.ActiveCfg = Debug|Win32
{5A0D55C3-7C2A-4B61-8F28-6B757052788F}.Debug.Build.0 = Debug|Win32
{5A0D55C3-7C2A-4B61-8F28-6B757052788F}.Release.ActiveCfg = Release|Win32
@@ -50,6 +26,8 @@
{D850DCDA-78AD-42C1-BF71-B5995900BCA9}.Debug.Build.0 = Debug|Win32
{D850DCDA-78AD-42C1-BF71-B5995900BCA9}.Release.ActiveCfg = Release|Win32
{D850DCDA-78AD-42C1-BF71-B5995900BCA9}.Release.Build.0 = Release|Win32
+ {79B454A7-63FC-4774-9EF1-A823D2CC0DB9}.Debug.ActiveCfg = Debug
+ {79B454A7-63FC-4774-9EF1-A823D2CC0DB9}.Release.ActiveCfg = Release
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection
Modified: trunk/MatroskaUtils/GDC/common/GoogleDesktopComponentCommon.vcproj
===================================================================
--- trunk/MatroskaUtils/GDC/common/GoogleDesktopComponentCommon.vcproj 2005-04-09 18:30:20 UTC (rev 1117)
+++ trunk/MatroskaUtils/GDC/common/GoogleDesktopComponentCommon.vcproj 2005-04-09 18:52:52 UTC (rev 1118)
@@ -55,11 +55,11 @@
OutputDirectory="Release"
IntermediateDirectory="Release"
ConfigurationType="4"
- CharacterSet="2">
+ CharacterSet="1">
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
- RuntimeLibrary="2"
+ RuntimeLibrary="0"
UsePrecompiledHeader="3"
WarningLevel="3"
Detect64BitPortabilityProblems="TRUE"
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.