1 /*
2  * $Id$
3  * Copyright (c) 2010-2015, Matroska (non-profit organisation)
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *     * Redistributions of source code must retain the above copyright
9  *       notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above copyright
11  *       notice, this list of conditions and the following disclaimer in the
12  *       documentation and/or other materials provided with the distribution.
13  *     * Neither the name of the Matroska assocation nor the
14  *       names of its contributors may be used to endorse or promote products
15  *       derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY the Matroska association ``AS IS'' AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL The Matroska Foundation BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 #include "mkvalidator_stdafx.h"
29 #include "mkvalidator_project.h"
30 #ifndef CONFIG_EBML_UNICODE
31 #define CONFIG_EBML_UNICODE
32 #endif
33 #include "matroska/matroska.h"
34 #include "matroska/matroska_sem.h"
35 
36 /*!
37  * \todo verify the track timecode scale is not null
38  * \todo verify that the size of frames inside a lace is legit (ie the remaining size for the last must be > 0)
39  * \todo verify that items with a limited set of values don't use other values
40  * \todo verify that timecodes for each track are increasing (for keyframes and p frames)
41  * \todo optionally show the use of deprecated elements
42  * \todo support concatenated segments
43  */
44 
45 static textwriter *StdErr = NULL;
46 static ebml_master *RSegmentInfo = NULL, *RTrackInfo = NULL, *RChapters = NULL, *RTags = NULL, *RCues = NULL, *RAttachments = NULL, *RSeekHead = NULL, *RSeekHead2 = NULL;
47 static array RClusters;
48 static array Tracks;
49 static size_t TrackMax=0;
50 static bool_t Warnings = 1;
51 static bool_t Live = 0;
52 static bool_t Details = 0;
53 static bool_t DivX = 0;
54 static bool_t Quiet = 0;
55 static timecode_t MinTime = INVALID_TIMECODE_T, MaxTime = INVALID_TIMECODE_T;
56 static timecode_t ClusterTime = INVALID_TIMECODE_T;
57 
58 // some macros for code readability
59 #define EL_Pos(elt)         EBML_ElementPosition((const ebml_element*)elt)
60 #define EL_Int(elt)         EBML_IntegerValue((const ebml_integer*)elt)
61 #define EL_Type(elt, type)  EBML_ElementIsType((const ebml_element*)elt, type)
62 #define EL_DataSize(elt)    EBML_ElementDataSize((const ebml_element*)elt, 1)
63 
64 typedef struct track_info
65 {
66     int Num;
67     int Kind;
68     filepos_t DataLength;
69     ebml_string *CodecID;
70 
71 } track_info;
72 
73 #ifdef TARGET_WIN
74 #include <windows.h>
DebugMessage(const tchar_t * Msg,...)75 void DebugMessage(const tchar_t* Msg,...)
76 {
77 #if !defined(NDEBUG) || defined(LOGFILE) || defined(LOGTIME)
78 	va_list Args;
79 	tchar_t Buffer[1024],*s=Buffer;
80 
81 	va_start(Args,Msg);
82 	vstprintf_s(Buffer,TSIZEOF(Buffer), Msg, Args);
83 	va_end(Args);
84 	tcscat_s(Buffer,TSIZEOF(Buffer),T("\r\n"));
85 #endif
86 
87 #ifdef LOGTIME
88     {
89         tchar_t timed[1024];
90         SysTickToString(timed,TSIZEOF(timed),GetTimeTick(),1,1,0);
91         stcatprintf_s(timed,TSIZEOF(timed),T(" %s"),s);
92         s = timed;
93     }
94 #endif
95 
96 #if !defined(NDEBUG)
97 	OutputDebugString(s);
98 #endif
99 
100 #if defined(LOGFILE)
101 {
102     static FILE* f=NULL;
103     static char s8[1024];
104     size_t i;
105     if (!f)
106 #if defined(TARGET_WINCE)
107     {
108         tchar_t DocPath[MAXPATH];
109         char LogPath[MAXPATH];
110         charconv *ToStr = CharConvOpen(NULL,CHARSET_DEFAULT);
111         GetDocumentPath(NULL,DocPath,TSIZEOF(DocPath),FTYPE_LOG); // more visible via ActiveSync
112         if (!DocPath[0])
113             tcscpy_s(DocPath,TSIZEOF(DocPath),T("\\My Documents"));
114         if (!PathIsFolder(NULL,DocPath))
115             FolderCreate(NULL,DocPath);
116         tcscat_s(DocPath,TSIZEOF(DocPath),T("\\corelog.txt"));
117         CharConvST(ToStr,LogPath,sizeof(LogPath),DocPath);
118         CharConvClose(ToStr);
119         f=fopen(LogPath,"a+b");
120         if (!f)
121             f=fopen("\\corelog.txt","a+b");
122     }
123 #else
124         f=fopen("\\corelog.txt","a+b");
125 #endif
126     for (i=0;s[i];++i)
127         s8[i]=(char)s[i];
128     s8[i]=0;
129     fputs(s8,f);
130     fflush(f);
131 }
132 #endif
133 }
134 #endif
135 
GetProfileName(size_t ProfileNum)136 static const tchar_t *GetProfileName(size_t ProfileNum)
137 {
138 static const tchar_t *Profile[7] = {T("unknown"), T("matroska v1"), T("matroska v2"), T("matroska v3"), T("webm"), T("matroska+DivX"), T("matroska v4")};
139 	switch (ProfileNum)
140 	{
141 	default:                  return Profile[0];
142 	case PROFILE_MATROSKA_V1: return Profile[1];
143 	case PROFILE_MATROSKA_V2: return Profile[2];
144 	case PROFILE_MATROSKA_V3: return Profile[3];
145 	case PROFILE_WEBM:        return Profile[4];
146 	case PROFILE_DIVX:        return Profile[5];
147 	case PROFILE_MATROSKA_V4: return Profile[6];
148 	}
149 }
150 
OutputError(int ErrCode,const tchar_t * ErrString,...)151 static int OutputError(int ErrCode, const tchar_t *ErrString, ...)
152 {
153 	tchar_t Buffer[MAXLINE];
154 	va_list Args;
155 	va_start(Args,ErrString);
156 	vstprintf_s(Buffer,TSIZEOF(Buffer), ErrString, Args);
157 	va_end(Args);
158 	TextPrintf(StdErr,T("\rERR%03X: %s\r\n"),ErrCode,Buffer);
159 	return -ErrCode;
160 }
161 
OutputWarning(int ErrCode,const tchar_t * ErrString,...)162 static void OutputWarning(int ErrCode, const tchar_t *ErrString, ...)
163 {
164     if (Warnings)
165     {
166 	    tchar_t Buffer[MAXLINE];
167 	    va_list Args;
168 	    va_start(Args,ErrString);
169 	    vstprintf_s(Buffer,TSIZEOF(Buffer), ErrString, Args);
170 	    va_end(Args);
171 	    TextPrintf(StdErr,T("\rWRN%03X: %s\r\n"),ErrCode,Buffer);
172     }
173 }
174 
CheckUnknownElements(ebml_element * Elt)175 static filepos_t CheckUnknownElements(ebml_element *Elt)
176 {
177 	tchar_t IdStr[32], String[MAXPATH];
178 	ebml_element *SubElt;
179 	filepos_t VoidAmount = 0;
180 	for (SubElt = EBML_MasterChildren(Elt); SubElt; SubElt = EBML_MasterNext(SubElt))
181 	{
182 		if (Node_IsPartOf(SubElt,EBML_DUMMY_ID))
183 		{
184             EBML_ElementGetName(Elt,String,TSIZEOF(String));
185 			EBML_IdToString(IdStr,TSIZEOF(IdStr),EBML_ElementClassID(SubElt));
186 			OutputWarning(12,T("Unknown element in %s %s at %") TPRId64 T(" (size %") TPRId64 T(" total %") TPRId64 T(")"),String,IdStr,EL_Pos(SubElt),EL_DataSize(SubElt), EBML_ElementFullSize(SubElt, 0));
187 		}
188 		else if (Node_IsPartOf(SubElt,EBML_VOID_CLASS))
189 		{
190 			VoidAmount = EBML_ElementFullSize(SubElt,0);
191 		}
192 		else if (Node_IsPartOf(SubElt,EBML_MASTER_CLASS))
193 		{
194 			VoidAmount += CheckUnknownElements(SubElt);
195 		}
196 	}
197 	return VoidAmount;
198 }
199 
gcd(int64_t a,int64_t b)200 static int64_t gcd(int64_t a, int64_t b)
201 {
202     for (;;)
203     {
204         int64_t c = a % b;
205         if(!c) return b;
206         a = b;
207         b = c;
208     }
209 }
210 
CheckVideoTrack(ebml_master * Track,int TrackNum,int ProfileNum)211 static int CheckVideoTrack(ebml_master *Track, int TrackNum, int ProfileNum)
212 {
213 	int Result = 0;
214 	ebml_element *Elt, *PixelW, *PixelH;
215 	ebml_integer *Unit;
216 	ebml_master *Video;
217 	Video = (ebml_master*)EBML_MasterFindChild(Track,&MATROSKA_ContextVideo);
218 	if (!Video)
219 		Result = OutputError(0xE0,T("Video track at %") TPRId64 T(" is missing a Video element"),EL_Pos(Track));
220 	// check the DisplayWidth and DisplayHeight are correct
221 	else
222 	{
223 		int64_t DisplayW = 0,DisplayH = 0;
224 		PixelW = EBML_MasterGetChild(Video,&MATROSKA_ContextPixelWidth);
225 		if (!PixelW)
226 			Result |= OutputError(0xE1,T("Video track #%d at %") TPRId64 T(" has no pixel width"),TrackNum,EL_Pos(Track));
227 		PixelH = EBML_MasterGetChild(Video,&MATROSKA_ContextPixelHeight);
228 		if (!PixelH)
229 			Result |= OutputError(0xE2,T("Video track #%d at %") TPRId64 T(" has no pixel height"),TrackNum,EL_Pos(Track));
230 
231         Unit = (ebml_integer*)EBML_MasterGetChild(Video,&MATROSKA_ContextDisplayUnit);
232 		assert(Unit!=NULL);
233 
234 		Elt = EBML_MasterFindChild(Video,&MATROSKA_ContextDisplayWidth);
235 		if (Elt)
236 			DisplayW = EL_Int(Elt);
237 		else if (EL_Int(Unit)!=MATROSKA_DISPLAY_UNIT_PIXEL)
238 			Result |= OutputError(0xE2,T("Video track #%d at %") TPRId64 T(" has an implied non pixel width"),TrackNum,EL_Pos(Track));
239         else if (PixelW)
240 			DisplayW = EL_Int(PixelW);
241 
242 		Elt = EBML_MasterFindChild(Video,&MATROSKA_ContextDisplayHeight);
243 		if (Elt)
244 			DisplayH = EL_Int(Elt);
245 		else if (EL_Int(Unit)!=MATROSKA_DISPLAY_UNIT_PIXEL)
246 			Result |= OutputError(0xE2,T("Video track #%d at %") TPRId64 T(" has an implied non pixel height"),TrackNum,EL_Pos(Track));
247 		else if (PixelH)
248 			DisplayH = EL_Int(PixelH);
249 
250 		if (DisplayH==0)
251 			Result |= OutputError(0xE7,T("Video track #%d at %") TPRId64 T(" has a null display height"),TrackNum,EL_Pos(Track));
252 		if (DisplayW==0)
253 			Result |= OutputError(0xE7,T("Video track #%d at %") TPRId64 T(" has a null display width"),TrackNum,EL_Pos(Track));
254 
255 		if (EL_Int(Unit)==MATROSKA_DISPLAY_UNIT_PIXEL && PixelW && PixelH)
256 		{
257 			// check if the pixel sizes appear valid
258 			if (DisplayW < EL_Int(PixelW) && DisplayH < EL_Int(PixelH))
259 			{
260                 int Serious = gcd(DisplayW,DisplayH)==1; // the DAR values were reduced as much as possible
261                 if (DisplayW*EL_Int(PixelH) == DisplayH*EL_Int(PixelW))
262                     Serious++; // same aspect ratio as the source
263                 if (8*DisplayW <= EL_Int(PixelW) && 8*DisplayH <= EL_Int(PixelH))
264                     Serious+=2; // too much shrinking compared to the original pixels
265                 if (ProfileNum!=PROFILE_WEBM)
266                     --Serious; // in Matroska it's tolerated as it's been operating like that for a while
267 
268 				if (Serious>2)
269 					Result |= OutputError(0xE3,T("The output pixels for Video track #%d seem wrong %") TPRId64 T("x%") TPRId64 T("px from %") TPRId64 T("x%") TPRId64,TrackNum,DisplayW,DisplayH,EL_Int(PixelW),EL_Int(PixelH));
270 				else if (Serious)
271 					OutputWarning(0xE3,T("The output pixels for Video track #%d seem wrong %") TPRId64 T("x%") TPRId64 T("px from %") TPRId64 T("x%") TPRId64,TrackNum,DisplayW,DisplayH,EL_Int(PixelW),EL_Int(PixelH));
272 			}
273 		}
274 
275         if (EL_Int(Unit)==MATROSKA_DISPLAY_UNIT_DAR)
276         {
277             // crop values should never exist
278             Elt = EBML_MasterFindChild(Video,&MATROSKA_ContextPixelCropTop);
279             if (Elt)
280                 Result |= OutputError(0xE4,T("Video track #%d is using unconstrained aspect ratio and has top crop at %") TPRId64,TrackNum,EL_Pos(Elt));
281             Elt = EBML_MasterFindChild(Video,&MATROSKA_ContextPixelCropBottom);
282             if (Elt)
283                 Result |= OutputError(0xE4,T("Video track #%d is using unconstrained aspect ratio and has bottom crop at %") TPRId64,TrackNum,EL_Pos(Elt));
284             Elt = EBML_MasterFindChild(Video,&MATROSKA_ContextPixelCropLeft);
285             if (Elt)
286                 Result |= OutputError(0xE4,T("Video track #%d is using unconstrained aspect ratio and has left crop at %") TPRId64,TrackNum,EL_Pos(Elt));
287             Elt = EBML_MasterFindChild(Video,&MATROSKA_ContextPixelCropRight);
288             if (Elt)
289                 Result |= OutputError(0xE4,T("Video track #%d is using unconstrained aspect ratio and has right crop at %") TPRId64,TrackNum,EL_Pos(Elt));
290 
291 			if (PixelW && DisplayW == EL_Int(PixelW))
292 				OutputWarning(0xE7,T("DisplayUnit seems to be pixels not aspect-ratio for Video track #%d %") TPRId64 T("px width from %") TPRId64,TrackNum,DisplayW,EL_Int(PixelW));
293 			if (PixelH && DisplayH == EL_Int(PixelH))
294 				OutputWarning(0xE7,T("DisplayUnit seems to be pixels not aspect-ratio for Video track #%d %") TPRId64 T("px height from %") TPRId64,TrackNum,DisplayH,EL_Int(PixelH));
295         }
296         else
297         {
298             // crop values should be less than the extended value
299             PixelW = EBML_MasterGetChild(Video,&MATROSKA_ContextPixelCropTop);
300             PixelH = EBML_MasterGetChild(Video,&MATROSKA_ContextPixelCropBottom);
301             if (EL_Int(PixelW) + EL_Int(PixelH) >= DisplayH)
302                 Result |= OutputError(0xE5,T("Video track #%d is cropping too many vertical pixels %") TPRId64 T(" vs %") TPRId64 T(" + %") TPRId64,TrackNum, DisplayH, EL_Int(PixelW), EL_Int(PixelH));
303 
304             PixelW = EBML_MasterGetChild(Video,&MATROSKA_ContextPixelCropLeft);
305             PixelH = EBML_MasterGetChild(Video,&MATROSKA_ContextPixelCropRight);
306             if (EL_Int(PixelW) + EL_Int(PixelH) >= DisplayW)
307                 Result |= OutputError(0xE6,T("Video track #%d is cropping too many horizontal pixels %") TPRId64 T(" vs %") TPRId64 T(" + %") TPRId64,TrackNum, DisplayW, EL_Int(PixelW), EL_Int(PixelH));
308         }
309 	}
310 	return Result;
311 }
312 
CheckTracks(ebml_master * Tracks,int ProfileNum)313 static int CheckTracks(ebml_master *Tracks, int ProfileNum)
314 {
315 	ebml_master *Track;
316 	ebml_element *TrackType, *TrackNum, *Elt, *Elt2;
317 	ebml_string *CodecID;
318 	tchar_t CodecName[MAXPATH],String[MAXPATH];
319 	int Result = 0;
320 	Track = (ebml_master*)EBML_MasterFindChild(Tracks, &MATROSKA_ContextTrackEntry);
321 	while (Track)
322 	{
323         // check if the codec is valid for the profile
324 		TrackNum = EBML_MasterGetChild(Track, &MATROSKA_ContextTrackNumber);
325 		if (TrackNum)
326 		{
327 			TrackType = EBML_MasterGetChild(Track, &MATROSKA_ContextTrackType);
328 			CodecID = (ebml_string*)EBML_MasterGetChild(Track, &MATROSKA_ContextCodecID);
329 			if (!CodecID)
330 				Result |= OutputError(0x300,T("Track #%d has no CodecID defined"),(int)EL_Int(TrackNum));
331 			else if (!TrackType)
332 				Result |= OutputError(0x301,T("Track #%d has no type defined"),(int)EL_Int(TrackNum));
333 			else
334 			{
335 				EBML_StringGet(CodecID,CodecName,TSIZEOF(CodecName));
336 				tcscpy_s(String,TSIZEOF(String),CodecName);
337 				if (tcscmp(tcsupr(String),CodecName)!=0)
338 					OutputWarning(0x307,T("Track #%d codec '%s' should be uppercase"),(int)EL_Int(TrackNum),CodecName);
339 				if (tcslen(String)<3 || String[1]!='_' || (String[0]!='A' && String[0]!='V' && String[0]!='S' && String[0]!='B'))
340 					OutputWarning(0x308,T("Track #%d codec '%s' doesn't appear to be valid"),(int)EL_Int(TrackNum),String);
341 
342                 // check that the audio frequencies are not 0
343                 if (EL_Int(TrackType) == TRACK_TYPE_AUDIO)
344                 {
345                     Elt = EBML_MasterGetChild(Track, &MATROSKA_ContextAudio);
346                     if (Elt==NULL)
347                         Result |= OutputError(0x309,T("Audio Track #%d has no audio settings"),(int)EL_Int(TrackNum));
348                     else
349                     {
350                         Elt2 = EBML_MasterFindChild(Elt, &MATROSKA_ContextOutputSamplingFrequency);
351                         if (Elt2 && EBML_FloatValue((ebml_float*)Elt2)==0)
352                             Result |= OutputError(0x30A,T("Audio Track #%d has a null output sampling frequency"),(int)EL_Int(TrackNum));
353                         Elt2 = EBML_MasterFindChild(Elt, &MATROSKA_ContextSamplingFrequency);
354                         if (Elt2 && EBML_FloatValue((ebml_float*)Elt2)==0)
355                             Result |= OutputError(0x30A,T("Audio Track #%d has a null sampling frequency"),(int)EL_Int(TrackNum));
356                     }
357                 }
358 
359 				if (ProfileNum==PROFILE_WEBM)
360 				{
361 					if (EL_Int(TrackType) != TRACK_TYPE_AUDIO && EL_Int(TrackType) != TRACK_TYPE_VIDEO)
362 						Result |= OutputError(0x302,T("Track #%d type %d not supported for profile '%s'"),(int)EL_Int(TrackNum),(int)EL_Int(TrackType),GetProfileName(ProfileNum));
363 					if (CodecID)
364 					{
365 						if (EL_Int(TrackType) == TRACK_TYPE_AUDIO)
366 						{
367 							if (!tcsisame_ascii(CodecName,T("A_VORBIS")))
368 								Result |= OutputError(0x303,T("Track #%d codec %s not supported for profile '%s'"),(int)EL_Int(TrackNum),CodecName,GetProfileName(ProfileNum));
369 						}
370 						else if (EL_Int(TrackType) == TRACK_TYPE_VIDEO)
371 						{
372 							if (!tcsisame_ascii(CodecName,T("V_VP8")))
373 								Result |= OutputError(0x304,T("Track #%d codec %s not supported for profile '%s'"),(int)EL_Int(TrackNum),CodecName,GetProfileName(ProfileNum));
374 						}
375 					}
376 				}
377 			}
378 		}
379 
380         // check if the AttachmentLink values match existing attachments
381 		TrackType = EBML_MasterFindChild(Track, &MATROSKA_ContextAttachmentLink);
382         while (TrackType)
383         {
384             if (!RAttachments)
385             {
386                 if (TrackNum)
387 				    Result |= OutputError(0x305,T("Track #%d has attachment links but not attachments in the file"),(int)EL_Int(TrackNum));
388                 else
389                     Result |= OutputError(0x305,T("Track at %") TPRId64 T(" has attachment links but not attachments in the file"),EL_Pos(Track));
390                 break;
391             }
392 
393             for (Elt=EBML_MasterChildren(RAttachments);Elt;Elt=EBML_MasterNext(Elt))
394             {
395                 if (EL_Type(Elt, &MATROSKA_ContextAttachedFile))
396                 {
397                     Elt2 = EBML_MasterFindChild((ebml_master*)Elt, &MATROSKA_ContextFileUID);
398                     if (Elt2 && EL_Int(Elt2) == EL_Int(TrackType))
399                         break;
400                 }
401             }
402             if (!Elt)
403             {
404                 if (TrackNum)
405 				    Result |= OutputError(0x306,T("Track #%d attachment link UID 0x%") TPRIx64 T(" not found in attachments"),(int)EL_Int(TrackNum),EL_Int(TrackType));
406                 else
407                     Result |= OutputError(0x306,T("Track at %") TPRId64 T(" attachment link UID 0x%") TPRIx64 T(" not found in attachments"),EL_Pos(Track),EL_Int(TrackType));
408             }
409 
410             TrackType = EBML_MasterFindNextElt(Track, TrackType, 0, 0);
411         }
412 
413 		Track = (ebml_master*)EBML_MasterFindNextElt(Tracks, (ebml_element*)Track, 0, 0);
414 	}
415 	return Result;
416 }
417 
418 struct profile_check
419 {
420     int *Result;
421     const ebml_element *Parent;
422     const tchar_t *EltName;
423     int ProfileMask;
424 };
425 
ProfileCallback(struct profile_check * check,int type,const tchar_t * ClassName,const ebml_element * Elt)426 static bool_t ProfileCallback(struct profile_check *check, int type, const tchar_t *ClassName, const ebml_element* Elt)
427 {
428     if (type==MASTER_CHECK_PROFILE_INVALID)
429 		*check->Result |= OutputError(0x201,T("Invalid '%s' for profile '%s' in %s at %") TPRId64,ClassName,GetProfileName(check->ProfileMask),check->EltName,EL_Pos(check->Parent));
430     else if (type==MASTER_CHECK_MISSING_MANDATORY)
431         *check->Result |= OutputError(0x200,T("Missing element '%s' in %s at %") TPRId64, ClassName,check->EltName,EL_Pos(check->Parent));
432     else if (type==MASTER_CHECK_MULTIPLE_UNIQUE)
433 		*check->Result |= OutputError(0x202,T("Unique element '%s' in %s at %") TPRId64 T(" found more than once at %") TPRId64, ClassName,check->EltName,EL_Pos(check->Parent),EL_Pos(Elt));
434     return 0; // don't remove anything
435 }
436 
CheckProfileViolation(ebml_element * Elt,int ProfileMask)437 static int CheckProfileViolation(ebml_element *Elt, int ProfileMask)
438 {
439 	int Result = 0;
440 	tchar_t String[MAXPATH];
441 	ebml_element *SubElt;
442     struct profile_check Checker;
443 
444 	if (Node_IsPartOf(Elt,EBML_MASTER_CLASS))
445 	{
446 	    EBML_ElementGetName(Elt,String,TSIZEOF(String));
447         if (!EBML_MasterIsChecksumValid((ebml_master*)Elt))
448             Result |= OutputError(0x203,T("Invalid checksum for element '%s' at %") TPRId64,String,EL_Pos(Elt));
449 
450         Checker.EltName = String;
451         Checker.ProfileMask = ProfileMask;
452         Checker.Parent = Elt;
453         Checker.Result = &Result;
454         EBML_MasterCheckContext((ebml_master*)Elt, ProfileMask, ProfileCallback, &Checker);
455 
456 		for (SubElt = EBML_MasterChildren(Elt); SubElt; SubElt = EBML_MasterNext(SubElt))
457 			if (Node_IsPartOf(SubElt,EBML_MASTER_CLASS))
458     		    Result |= CheckProfileViolation(SubElt,ProfileMask);
459 	}
460 
461 	return Result;
462 }
463 
CheckSeekHead(ebml_master * SeekHead)464 static int CheckSeekHead(ebml_master *SeekHead)
465 {
466 	int Result = 0;
467 	matroska_seekpoint *RLevel1 = (matroska_seekpoint*)EBML_MasterFindChild(SeekHead, &MATROSKA_ContextSeek);
468     bool_t BSegmentInfo = 0, BTrackInfo = 0, BCues = 0, BTags = 0, BChapters = 0, BAttachments = 0, BSecondSeek = 0;
469 	while (RLevel1)
470 	{
471 		filepos_t Pos = MATROSKA_MetaSeekAbsolutePos(RLevel1);
472 		fourcc_t SeekId = MATROSKA_MetaSeekID(RLevel1);
473 		tchar_t IdString[32];
474 
475 		EBML_IdToString(IdString,TSIZEOF(IdString),SeekId);
476 		if (Pos == INVALID_FILEPOS_T)
477 			Result |= OutputError(0x60,T("The SeekPoint at %") TPRId64 T(" has an unknown position (ID %s)"),EL_Pos(RLevel1),IdString);
478 		else if (SeekId==0)
479 			Result |= OutputError(0x61,T("The SeekPoint at %") TPRId64 T(" has no ID defined (position %") TPRId64 T(")"),EL_Pos(RLevel1),Pos);
480 		else if (MATROSKA_MetaSeekIsClass(RLevel1, &MATROSKA_ContextInfo))
481 		{
482 			if (!RSegmentInfo)
483 				Result |= OutputError(0x62,T("The SeekPoint at %") TPRId64 T(" references an unknown SegmentInfo at %") TPRId64,EL_Pos(RLevel1),Pos);
484 			else if (EL_Pos(RSegmentInfo) != Pos)
485 				Result |= OutputError(0x63,T("The SeekPoint at %") TPRId64 T(" references a SegmentInfo at wrong position %") TPRId64 T(" (real %") TPRId64 T(")"),EL_Pos(RLevel1),Pos,EL_Pos(RSegmentInfo));
486             BSegmentInfo = 1;
487 		}
488 		else if (MATROSKA_MetaSeekIsClass(RLevel1, &MATROSKA_ContextTracks))
489 		{
490 			if (!RTrackInfo)
491 				Result |= OutputError(0x64,T("The SeekPoint at %") TPRId64 T(" references an unknown TrackInfo at %") TPRId64,EL_Pos(RLevel1),Pos);
492 			else if (EL_Pos(RTrackInfo) != Pos)
493 				Result |= OutputError(0x65,T("The SeekPoint at %") TPRId64 T(" references a TrackInfo at wrong position %") TPRId64 T(" (real %") TPRId64 T(")"),EL_Pos(RLevel1),Pos,EL_Pos(RTrackInfo));
494             BTrackInfo = 1;
495 		}
496 		else if (MATROSKA_MetaSeekIsClass(RLevel1, &MATROSKA_ContextCues))
497 		{
498 			if (!RCues)
499 				Result |= OutputError(0x66,T("The SeekPoint at %") TPRId64 T(" references an unknown Cues at %") TPRId64,EL_Pos(RLevel1),Pos);
500 			else if (EL_Pos(RCues) != Pos)
501 				Result |= OutputError(0x67,T("The SeekPoint at %") TPRId64 T(" references a Cues at wrong position %") TPRId64 T(" (real %") TPRId64 T(")"),EL_Pos(RLevel1),Pos,EL_Pos(RCues));
502             BCues = 1;
503 		}
504 		else if (MATROSKA_MetaSeekIsClass(RLevel1, &MATROSKA_ContextTags))
505 		{
506 			if (!RTags)
507 				Result |= OutputError(0x68,T("The SeekPoint at %") TPRId64 T(" references an unknown Tags at %") TPRId64,EL_Pos(RLevel1),Pos);
508 			else if (EL_Pos(RTags) != Pos)
509 				Result |= OutputError(0x69,T("The SeekPoint at %") TPRId64 T(" references a Tags at wrong position %") TPRId64 T(" (real %") TPRId64 T(")"),EL_Pos(RLevel1),Pos,EL_Pos(RTags));
510             BTags = 1;
511 		}
512 		else if (MATROSKA_MetaSeekIsClass(RLevel1, &MATROSKA_ContextChapters))
513 		{
514 			if (!RChapters)
515 				Result |= OutputError(0x6A,T("The SeekPoint at %") TPRId64 T(" references an unknown Chapters at %") TPRId64,EL_Pos(RLevel1),Pos);
516 			else if (EL_Pos(RChapters) != Pos)
517 				Result |= OutputError(0x6B,T("The SeekPoint at %") TPRId64 T(" references a Chapters at wrong position %") TPRId64 T(" (real %") TPRId64 T(")"),EL_Pos(RLevel1),Pos,EL_Pos(RChapters));
518             BChapters = 1;
519 		}
520 		else if (MATROSKA_MetaSeekIsClass(RLevel1, &MATROSKA_ContextAttachments))
521 		{
522 			if (!RAttachments)
523 				Result |= OutputError(0x6C,T("The SeekPoint at %") TPRId64 T(" references an unknown Attachments at %") TPRId64,EL_Pos(RLevel1),Pos);
524 			else if (EL_Pos(RAttachments) != Pos)
525 				Result |= OutputError(0x6D,T("The SeekPoint at %") TPRId64 T(" references a Attachments at wrong position %") TPRId64 T(" (real %") TPRId64 T(")"),EL_Pos(RLevel1),Pos,EL_Pos(RAttachments));
526             BAttachments = 1;
527 		}
528 		else if (MATROSKA_MetaSeekIsClass(RLevel1, &MATROSKA_ContextSeekHead))
529 		{
530 			if (EL_Pos(SeekHead) == Pos)
531 				Result |= OutputError(0x6E,T("The SeekPoint at %") TPRId64 T(" references references its own SeekHead"),EL_Pos(RLevel1));
532 			else if (SeekHead == RSeekHead)
533             {
534                 if (!RSeekHead2)
535 				    Result |= OutputError(0x6F,T("The SeekPoint at %") TPRId64 T(" references an unknown secondary SeekHead at %") TPRId64,EL_Pos(RLevel1),Pos);
536                 BSecondSeek = 1;
537             }
538 			else if (SeekHead == RSeekHead2 && Pos!=EL_Pos(RSeekHead))
539 			    Result |= OutputError(0x70,T("The SeekPoint at %") TPRId64 T(" references an unknown extra SeekHead at %") TPRId64,EL_Pos(RLevel1),Pos);
540 		}
541 		else if (MATROSKA_MetaSeekIsClass(RLevel1, &MATROSKA_ContextCluster))
542 		{
543 			ebml_element **Cluster;
544 			for (Cluster = ARRAYBEGIN(RClusters,ebml_element*);Cluster != ARRAYEND(RClusters,ebml_element*); ++Cluster)
545 			{
546 				if (EL_Pos(*Cluster) == Pos)
547 					break;
548 			}
549 			if (Cluster == ARRAYEND(RClusters,ebml_element*) && Cluster != ARRAYBEGIN(RClusters,ebml_element*))
550 				Result |= OutputError(0x71,T("The SeekPoint at %") TPRId64 T(" references a Cluster not found at %") TPRId64,EL_Pos(RLevel1),Pos);
551 		}
552 		else
553 			OutputWarning(0x860,T("The SeekPoint at %") TPRId64 T(" references an element that is not a known level 1 ID %s at %") TPRId64 T(")"),EL_Pos(RLevel1),IdString,Pos);
554 		RLevel1 = (matroska_seekpoint*)EBML_MasterFindNextElt(SeekHead, (ebml_element*)RLevel1, 0, 0);
555 	}
556     if (SeekHead == RSeekHead)
557     {
558         if (!BSegmentInfo && RSegmentInfo)
559             OutputWarning(0x861,T("The SegmentInfo is not referenced in the main SeekHead"));
560         if (!BTrackInfo && RTrackInfo)
561             OutputWarning(0x861,T("The TrackInfo is not referenced in the main SeekHead"));
562         if (!BCues && RCues)
563             OutputWarning(0x861,T("The Cues is not referenced in the main SeekHead"));
564         if (!BTags && RTags)
565             OutputWarning(0x861,T("The Tags is not referenced in the main SeekHead"));
566         if (!BChapters && RChapters)
567             OutputWarning(0x861,T("The Chapters is not referenced in the main SeekHead"));
568         if (!BAttachments && RAttachments)
569             OutputWarning(0x861,T("The Attachments is not referenced in the main SeekHead"));
570         if (!BSecondSeek && RSeekHead2)
571             OutputWarning(0x861,T("The secondary SeekHead is not referenced in the main SeekHead"));
572     }
573 	return Result;
574 }
575 
LinkClusterBlocks(void)576 static void LinkClusterBlocks(void)
577 {
578 	matroska_cluster **Cluster;
579 	for (Cluster=ARRAYBEGIN(RClusters,matroska_cluster*);Cluster!=ARRAYEND(RClusters,matroska_cluster*);++Cluster)
580 		MATROSKA_LinkClusterBlocks(*Cluster, RSegmentInfo, RTrackInfo, 1);
581 }
582 
TrackIsLaced(int16_t TrackNum)583 static bool_t TrackIsLaced(int16_t TrackNum)
584 {
585     ebml_element *TrackData;
586     ebml_master *Track = (ebml_master*)EBML_MasterFindChild(RTrackInfo, &MATROSKA_ContextTrackEntry);
587     while (Track)
588     {
589         TrackData = EBML_MasterGetChild(Track, &MATROSKA_ContextTrackNumber);
590         if (EL_Int(TrackData) == TrackNum)
591         {
592             TrackData = EBML_MasterGetChild(Track, &MATROSKA_ContextFlagLacing);
593             return EL_Int(TrackData) != 0;
594         }
595         Track = (ebml_master*)EBML_MasterFindNextElt(RTrackInfo, (ebml_element*)Track, 0, 0);
596     }
597     return 1;
598 }
599 
TrackIsVideo(int16_t TrackNum)600 static bool_t TrackIsVideo(int16_t TrackNum)
601 {
602     ebml_element *TrackData;
603     ebml_master *Track = (ebml_master*)EBML_MasterFindChild(RTrackInfo, &MATROSKA_ContextTrackEntry);
604     while (Track)
605     {
606         TrackData = EBML_MasterGetChild(Track, &MATROSKA_ContextTrackNumber);
607         if (EL_Int(TrackData) == TrackNum)
608         {
609             TrackData = EBML_MasterGetChild(Track, &MATROSKA_ContextTrackType);
610             return EL_Int(TrackData) == TRACK_TYPE_VIDEO;
611         }
612         // look for TrackNum in the next Track
613         Track = (ebml_master*)EBML_MasterFindNextElt(RTrackInfo, (ebml_element*)Track, 0, 0);
614     }
615     return 0;
616 }
617 
TrackNeedsKeyframe(int16_t TrackNum)618 static bool_t TrackNeedsKeyframe(int16_t TrackNum)
619 {
620     ebml_element *TrackData;
621     ebml_master *Track = (ebml_master*)EBML_MasterFindChild(RTrackInfo, &MATROSKA_ContextTrackEntry);
622     while (Track)
623     {
624         TrackData = EBML_MasterGetChild(Track, &MATROSKA_ContextTrackNumber);
625         if (EL_Int(TrackData) == TrackNum)
626         {
627             TrackData = EBML_MasterGetChild(Track, &MATROSKA_ContextTrackType);
628             switch (EL_Int(TrackData))
629             {
630             case TRACK_TYPE_VIDEO:
631                 return 0;
632             case TRACK_TYPE_AUDIO:
633                 {
634                     tchar_t CodecName[MAXDATA];
635                     ebml_string *CodecID = (ebml_string*)EBML_MasterGetChild(Track, &MATROSKA_ContextCodecID);
636                     EBML_StringGet(CodecID,CodecName,TSIZEOF(CodecName));
637                     return !tcsisame_ascii(CodecName,T("A_TRUEHD"));
638                 }
639             default: return 1;
640             }
641         }
642         // look for TrackNum in the next Track
643         Track = (ebml_master*)EBML_MasterFindNextElt(RTrackInfo, (ebml_element*)Track, 0, 0);
644     }
645     return 0;
646 }
647 
CheckVideoStart(void)648 static int CheckVideoStart(void)
649 {
650 	int Result = 0;
651 	ebml_master **Cluster;
652     ebml_element *Block, *GBlock;
653     int16_t BlockNum;
654     timecode_t ClusterTimecode;
655     array TrackKeyframe;
656 	array TrackFirstKeyframePos;
657 
658 	for (Cluster=ARRAYBEGIN(RClusters,ebml_master*);Cluster!=ARRAYEND(RClusters,ebml_master*);++Cluster)
659     {
660         ArrayInit(&TrackKeyframe);
661         ArrayResize(&TrackKeyframe,sizeof(bool_t)*(TrackMax+1),256);
662         ArrayZero(&TrackKeyframe);
663         ArrayInit(&TrackFirstKeyframePos);
664         ArrayResize(&TrackFirstKeyframePos,sizeof(filepos_t)*(TrackMax+1),256);
665 		ArrayZero(&TrackFirstKeyframePos);
666 
667         ClusterTimecode = MATROSKA_ClusterTimecode((matroska_cluster*)*Cluster);
668         if (ClusterTimecode==INVALID_TIMECODE_T)
669             Result |= OutputError(0xC1,T("The Cluster at %") TPRId64 T(" has no timecode"),EL_Pos(*Cluster));
670         else if (ClusterTime!=INVALID_TIMECODE_T && ClusterTime >= ClusterTimecode)
671 			OutputWarning(0xC2,T("The timecode of the Cluster at %") TPRId64 T(" is not incrementing (may be intentional)"),EL_Pos(*Cluster));
672         ClusterTime = ClusterTimecode;
673 
674 	    for (Block = EBML_MasterChildren(*Cluster);Block;Block=EBML_MasterNext(Block))
675 	    {
676 		    if (EL_Type(Block, &MATROSKA_ContextBlockGroup))
677 		    {
678 			    for (GBlock = EBML_MasterChildren(Block);GBlock;GBlock=EBML_MasterNext(GBlock))
679 			    {
680 				    if (EL_Type(GBlock, &MATROSKA_ContextBlock))
681 				    {
682                         BlockNum = MATROSKA_BlockTrackNum((matroska_block*)GBlock);
683 						if (BlockNum > ARRAYCOUNT(TrackKeyframe,bool_t))
684 							OutputError(0xC3,T("Unknown track #%d in Cluster at %") TPRId64 T(" in Block at %") TPRId64,(int)BlockNum,EL_Pos(*Cluster),EL_Pos(GBlock));
685                         else if (TrackIsVideo(BlockNum))
686 						{
687 							if (!ARRAYBEGIN(TrackKeyframe,bool_t)[BlockNum] && MATROSKA_BlockKeyframe((matroska_block*)GBlock))
688 								ARRAYBEGIN(TrackKeyframe,bool_t)[BlockNum] = 1;
689 							if (!ARRAYBEGIN(TrackKeyframe,bool_t)[BlockNum] && ARRAYBEGIN(TrackFirstKeyframePos,filepos_t)[BlockNum]==0)
690 								ARRAYBEGIN(TrackFirstKeyframePos,filepos_t)[BlockNum] = EL_Pos(*Cluster);
691                         }
692 					    break;
693 				    }
694 			    }
695 		    }
696 		    else if (EL_Type(Block, &MATROSKA_ContextSimpleBlock))
697 		    {
698                 BlockNum = MATROSKA_BlockTrackNum((matroska_block*)Block);
699 				if (BlockNum > ARRAYCOUNT(TrackKeyframe,bool_t))
700                     OutputError(0xC3,T("Unknown track #%d in Cluster at %") TPRId64 T(" in SimpleBlock at %") TPRId64,(int)BlockNum,EL_Pos(*Cluster),EL_Pos(Block));
701                 else if (TrackIsVideo(BlockNum))
702 				{
703 					if (!ARRAYBEGIN(TrackKeyframe,bool_t)[BlockNum] && MATROSKA_BlockKeyframe((matroska_block*)Block))
704 						ARRAYBEGIN(TrackKeyframe,bool_t)[BlockNum] = 1;
705 					if (!ARRAYBEGIN(TrackKeyframe,bool_t)[BlockNum] && ARRAYBEGIN(TrackFirstKeyframePos,filepos_t)[BlockNum]==0)
706 						ARRAYBEGIN(TrackFirstKeyframePos,filepos_t)[BlockNum] = EL_Pos(*Cluster);
707                 }
708 		    }
709 	    }
710 		for (BlockNum=0;BlockNum<ARRAYCOUNT(TrackKeyframe,bool_t);++BlockNum)
711 		{
712 			if (ARRAYBEGIN(TrackKeyframe,bool_t)[BlockNum] && ARRAYBEGIN(TrackFirstKeyframePos,filepos_t)[BlockNum]!=0)
713 				OutputWarning(0xC0,T("First Block for video track #%d in Cluster at %") TPRId64 T(" is not a keyframe"),(int)BlockNum,ARRAYBEGIN(TrackFirstKeyframePos,filepos_t)[BlockNum]);
714 		}
715         ArrayClear(&TrackKeyframe);
716         ArrayClear(&TrackFirstKeyframePos);
717     }
718 	return Result;
719 }
720 
CheckPosSize(const ebml_element * RSegment)721 static int CheckPosSize(const ebml_element *RSegment)
722 {
723 	int Result = 0;
724 	ebml_element **Cluster,*PrevCluster=NULL;
725     ebml_element *Elt;
726 
727 	for (Cluster=ARRAYBEGIN(RClusters,ebml_element*);Cluster!=ARRAYEND(RClusters,ebml_element*);++Cluster)
728     {
729         Elt = EBML_MasterFindChild((ebml_master*)*Cluster,&MATROSKA_ContextPrevSize);
730         if (Elt)
731         {
732             if (PrevCluster==NULL)
733                 Result |= OutputError(0xA0,T("The PrevSize %") TPRId64 T(" was set on the first Cluster at %") TPRId64,EL_Int(Elt),EL_Pos(Elt));
734             else if (EL_Int(Elt) != EL_Pos(*Cluster) - EL_Pos(PrevCluster))
735                 Result |= OutputError(0xA1,T("The Cluster PrevSize %") TPRId64 T(" at %") TPRId64 T(" should be %") TPRId64,EL_Int(Elt),EL_Pos(Elt),EL_Pos(*Cluster) - EL_Pos(PrevCluster));
736         }
737         Elt = EBML_MasterFindChild((ebml_master*)*Cluster,&MATROSKA_ContextPosition);
738         if (Elt)
739         {
740             if (EL_Int(Elt) != EL_Pos(*Cluster) - EBML_ElementPositionData(RSegment))
741                 Result |= OutputError(0xA2,T("The Cluster position %") TPRId64 T(" at %") TPRId64 T(" should be %") TPRId64,EL_Int(Elt),EL_Pos(Elt),EL_Pos(*Cluster) - EBML_ElementPositionData(RSegment));
742         }
743         PrevCluster = *Cluster;
744     }
745 	return Result;
746 }
747 
CheckLacingKeyframe(void)748 static int CheckLacingKeyframe(void)
749 {
750 	int Result = 0;
751 	matroska_cluster **Cluster;
752     ebml_element *Block, *GBlock;
753     int16_t BlockNum;
754     timecode_t BlockTime;
755     size_t Frame,TrackIdx;
756 
757 	for (Cluster=ARRAYBEGIN(RClusters,matroska_cluster*);Cluster!=ARRAYEND(RClusters,matroska_cluster*);++Cluster)
758     {
759 	    for (Block = EBML_MasterChildren(*Cluster);Block;Block=EBML_MasterNext(Block))
760 	    {
761 		    if (EL_Type(Block, &MATROSKA_ContextBlockGroup))
762 		    {
763 			    for (GBlock = EBML_MasterChildren(Block);GBlock;GBlock=EBML_MasterNext(GBlock))
764 			    {
765 				    if (EL_Type(GBlock, &MATROSKA_ContextBlock))
766 				    {
767                         //MATROSKA_ContextFlagLacing
768                         BlockNum = MATROSKA_BlockTrackNum((matroska_block*)GBlock);
769                         for (TrackIdx=0; TrackIdx<ARRAYCOUNT(Tracks,track_info); ++TrackIdx)
770                             if (ARRAYBEGIN(Tracks,track_info)[TrackIdx].Num == BlockNum)
771                                 break;
772 
773                         if (TrackIdx==ARRAYCOUNT(Tracks,track_info))
774                             Result |= OutputError(0xB2,T("Block at %") TPRId64 T(" is using an unknown track #%d"),EL_Pos(GBlock),(int)BlockNum);
775                         else
776                         {
777                             if (MATROSKA_BlockLaced((matroska_block*)GBlock) && !TrackIsLaced(BlockNum))
778                                 Result |= OutputError(0xB0,T("Block at %") TPRId64 T(" track #%d is laced but the track is not"),EL_Pos(GBlock),(int)BlockNum);
779                             if (!MATROSKA_BlockKeyframe((matroska_block*)GBlock) && TrackNeedsKeyframe(BlockNum))
780                                 Result |= OutputError(0xB1,T("Block at %") TPRId64 T(" track #%d is not a keyframe"),EL_Pos(GBlock),(int)BlockNum);
781 
782                             for (Frame=0; Frame<MATROSKA_BlockGetFrameCount((matroska_block*)GBlock); ++Frame)
783                                 ARRAYBEGIN(Tracks,track_info)[TrackIdx].DataLength += MATROSKA_BlockGetLength((matroska_block*)GBlock,Frame);
784                             if (Details)
785                             {
786                                 BlockTime = MATROSKA_BlockTimecode((matroska_block*)GBlock);
787                                 if (MinTime==INVALID_TIMECODE_T || MinTime>BlockTime)
788                                     MinTime = BlockTime;
789                                 if (MaxTime==INVALID_TIMECODE_T || MaxTime<BlockTime)
790                                     MaxTime = BlockTime;
791                             }
792                         }
793 					    break;
794 				    }
795 			    }
796 		    }
797 		    else if (EL_Type(Block, &MATROSKA_ContextSimpleBlock))
798 		    {
799                 BlockNum = MATROSKA_BlockTrackNum((matroska_block*)Block);
800                 for (TrackIdx=0; TrackIdx<ARRAYCOUNT(Tracks,track_info); ++TrackIdx)
801                     if (ARRAYBEGIN(Tracks,track_info)[TrackIdx].Num == BlockNum)
802                         break;
803 
804                 if (TrackIdx==ARRAYCOUNT(Tracks,track_info))
805                     Result |= OutputError(0xB2,T("Block at %") TPRId64 T(" is using an unknown track #%d"),EL_Pos(Block),(int)BlockNum);
806                 else
807                 {
808                     if (MATROSKA_BlockLaced((matroska_block*)Block) && !TrackIsLaced(BlockNum))
809                         Result |= OutputError(0xB0,T("SimpleBlock at %") TPRId64 T(" track #%d is laced but the track is not"),EL_Pos(Block),(int)BlockNum);
810                     if (!MATROSKA_BlockKeyframe((matroska_block*)Block) && TrackNeedsKeyframe(BlockNum))
811                         Result |= OutputError(0xB1,T("SimpleBlock at %") TPRId64 T(" track #%d is not a keyframe"),EL_Pos(Block),(int)BlockNum);
812                     for (Frame=0; Frame<MATROSKA_BlockGetFrameCount((matroska_block*)Block); ++Frame)
813                         ARRAYBEGIN(Tracks,track_info)[TrackIdx].DataLength += MATROSKA_BlockGetLength((matroska_block*)Block,Frame);
814                     if (Details)
815                     {
816                         BlockTime = MATROSKA_BlockTimecode((matroska_block*)Block);
817                         if (MinTime==INVALID_TIMECODE_T || MinTime>BlockTime)
818                             MinTime = BlockTime;
819                         if (MaxTime==INVALID_TIMECODE_T || MaxTime<BlockTime)
820                             MaxTime = BlockTime;
821                     }
822                 }
823 		    }
824 	    }
825     }
826 	return Result;
827 }
828 
CheckCueEntries(ebml_master * Cues)829 static int CheckCueEntries(ebml_master *Cues)
830 {
831 	int Result = 0;
832 	timecode_t TimecodeEntry, PrevTimecode = INVALID_TIMECODE_T;
833 	int16_t TrackNumEntry;
834 	matroska_cluster **Cluster;
835 	matroska_block *Block;
836     int ClustNum = 0;
837 
838 	if (!RSegmentInfo)
839 		Result |= OutputError(0x310,T("A Cues (index) is defined but no SegmentInfo was found"));
840 	else if (ARRAYCOUNT(RClusters,matroska_cluster*))
841 	{
842 		matroska_cuepoint *CuePoint = (matroska_cuepoint*)EBML_MasterFindChild(Cues, &MATROSKA_ContextCuePoint);
843 		while (CuePoint)
844 		{
845             if (!Quiet && ClustNum++ % 24 == 0)
846                 TextWrite(StdErr,T("."));
847 			MATROSKA_LinkCueSegmentInfo(CuePoint,RSegmentInfo);
848 			TimecodeEntry = MATROSKA_CueTimecode(CuePoint);
849 			TrackNumEntry = MATROSKA_CueTrackNum(CuePoint);
850 
851 			if (TimecodeEntry < PrevTimecode && PrevTimecode != INVALID_TIMECODE_T)
852 				OutputWarning(0x311,T("The Cues entry for timecode %") TPRId64 T(" ms is listed after entry %") TPRId64 T(" ms"),Scale64(TimecodeEntry,1,1000000),Scale64(PrevTimecode,1,1000000));
853 
854 			// find a matching Block
855 			for (Cluster = ARRAYBEGIN(RClusters,matroska_cluster*);Cluster != ARRAYEND(RClusters,matroska_cluster*); ++Cluster)
856 			{
857 				Block = MATROSKA_GetBlockForTimecode(*Cluster, TimecodeEntry, TrackNumEntry);
858 				if (Block)
859 					break;
860 			}
861 			if (Cluster == ARRAYEND(RClusters,matroska_cluster*))
862 				Result |= OutputError(0x312,T("CueEntry Track #%d and timecode %") TPRId64 T(" ms not found"),(int)TrackNumEntry,Scale64(TimecodeEntry,1,1000000));
863 			PrevTimecode = TimecodeEntry;
864 			CuePoint = (matroska_cuepoint*)EBML_MasterFindNextElt(Cues, (ebml_element*)CuePoint, 0, 0);
865 		}
866 	}
867 	return Result;
868 }
869 
870 #if defined(TARGET_WIN) && defined(UNICODE)
wmain(int argc,const wchar_t * argv[])871 int wmain(int argc, const wchar_t *argv[])
872 #else
873 int main(int argc, const char *argv[])
874 #endif
875 {
876     int Result = 0;
877     int ShowUsage = 0;
878     int ShowVersion = 0;
879     parsercontext p;
880     textwriter _StdErr;
881     stream *Input = NULL;
882     tchar_t Path[MAXPATHFULL];
883     tchar_t String[MAXLINE];
884     ebml_master *EbmlHead = NULL, *RSegment = NULL, *RLevel1 = NULL, *Prev, *RLevelX, **Cluster;
885 	ebml_element *EbmlDocVer, *EbmlReadDocVer;
886     ebml_string *LibName, *AppName;
887     ebml_parser_context RContext;
888     ebml_parser_context RSegmentContext;
889     int i,UpperElement;
890 	int MatroskaProfile = 0;
891     bool_t HasVideo = 0;
892 	int DotCount;
893     track_info *TI;
894 	filepos_t VoidAmount = 0;
895 
896     // Core-C init phase
897     ParserContext_Init(&p,NULL,NULL,NULL);
898 	StdAfx_Init((nodemodule*)&p);
899     ProjectSettings((nodecontext*)&p);
900 
901     // EBML & Matroska Init
902     MATROSKA_Init((nodecontext*)&p);
903 
904     ArrayInit(&RClusters);
905     ArrayInit(&Tracks);
906 
907     StdErr = &_StdErr;
908     memset(StdErr,0,sizeof(_StdErr));
909     StdErr->Stream = (stream*)NodeSingleton(&p,STDERR_ID);
910     assert(StdErr->Stream!=NULL);
911 
912 	for (i=1;i<argc;++i)
913 	{
914 #if defined(TARGET_WIN) && defined(UNICODE)
915 	    Node_FromWcs(&p,Path,TSIZEOF(Path),argv[i]);
916 #else
917 		Node_FromStr(&p,Path,TSIZEOF(Path),argv[i]);
918 #endif
919 		if (tcsisame_ascii(Path,T("--no-warn"))) Warnings = 0;
920 		else if (tcsisame_ascii(Path,T("--live"))) Live = 1;
921 		else if (tcsisame_ascii(Path,T("--details"))) Details = 1;
922 		else if (tcsisame_ascii(Path,T("--divx"))) DivX = 1;
923 		else if (tcsisame_ascii(Path,T("--version"))) ShowVersion = 1;
924 		else if (tcsisame_ascii(Path,T("--quiet"))) Quiet = 1;
925         else if (tcsisame_ascii(Path,T("--help"))) {ShowVersion = 1; ShowUsage = 1;}
926 		else if (i<argc-1) TextPrintf(StdErr,T("Unknown parameter '%s'\r\n"),Path);
927 	}
928 
929     if (argc < 2 || ShowVersion)
930     {
931         TextWrite(StdErr,T("mkvalidator v") PROJECT_VERSION T(", Copyright (c) 2010-2015 Matroska Foundation\r\n"));
932         if (argc < 2 || ShowUsage)
933         {
934             Result = OutputError(1,T("Usage: mkvalidator [options] <matroska_src>"));
935 		    TextWrite(StdErr,T("Options:\r\n"));
936 		    TextWrite(StdErr,T("  --no-warn   only output errors, no warnings\r\n"));
937             TextWrite(StdErr,T("  --live      only output errors/warnings relevant to live streams\r\n"));
938             TextWrite(StdErr,T("  --details   show details for valid files\r\n"));
939             TextWrite(StdErr,T("  --divx      assume the file is using DivX specific extensions\r\n"));
940             TextWrite(StdErr,T("  --quiet     don't ouput progress and file info\r\n"));
941             TextWrite(StdErr,T("  --version   show the version of mkvalidator\r\n"));
942             TextWrite(StdErr,T("  --help      show this screen\r\n"));
943         }
944         goto exit;
945     }
946 
947 #if defined(TARGET_WIN) && defined(UNICODE)
948     Node_FromWcs(&p,Path,TSIZEOF(Path),argv[argc-1]);
949 #else
950 	Node_FromStr(&p,Path,TSIZEOF(Path),argv[argc-1]);
951 #endif
952     Input = StreamOpen(&p,Path,SFLAG_RDONLY/*|SFLAG_BUFFERED*/);
953     if (!Input)
954     {
955         TextPrintf(StdErr,T("Could not open file \"%s\" for reading\r\n"),Path);
956         Result = -2;
957         goto exit;
958     }
959 
960     // parse the source file to determine if it's a Matroska file and determine the location of the key parts
961     RContext.Context = &MATROSKA_ContextStream;
962     RContext.EndPosition = INVALID_FILEPOS_T;
963     RContext.UpContext = NULL;
964     RContext.Profile = 0;
965     EbmlHead = (ebml_master*)EBML_FindNextElement(Input, &RContext, &UpperElement, 0);
966 	if (!EbmlHead || !EL_Type(EbmlHead, &EBML_ContextHead))
967     {
968         Result = OutputError(3,T("EBML head not found! Are you sure it's a matroska/webm file?"));
969         goto exit;
970     }
971 
972     if (!Quiet) TextWrite(StdErr,T("."));
973 
974 	if (EBML_ElementReadData(EbmlHead,Input,&RContext,0,SCOPE_ALL_DATA, 1)!=ERR_NONE)
975     {
976         Result = OutputError(4,T("Could not read the EBML head"));
977         goto exit;
978     }
979     if (!EBML_MasterIsChecksumValid(EbmlHead))
980     {
981         Result = OutputError(12,T("The EBML header is damaged (invalid CheckSum)"));
982         goto exit;
983     }
984 
985 	VoidAmount += CheckUnknownElements((ebml_element*)EbmlHead);
986 
987 	RLevel1 = (ebml_master*)EBML_MasterGetChild(EbmlHead,&EBML_ContextReadVersion);
988 	if (EL_Int(RLevel1) > EBML_MAX_VERSION)
989 		OutputError(5,T("The EBML read version is not supported: %d"),(int)EL_Int(RLevel1));
990 
991 	RLevel1 = (ebml_master*)EBML_MasterGetChild(EbmlHead,&EBML_ContextMaxIdLength);
992 	if (EL_Int(RLevel1) > EBML_MAX_ID)
993 		OutputError(6,T("The EBML max ID length is not supported: %d"),(int)EL_Int(RLevel1));
994 
995 	RLevel1 = (ebml_master*)EBML_MasterGetChild(EbmlHead,&EBML_ContextMaxSizeLength);
996 	if (EL_Int(RLevel1) > EBML_MAX_SIZE)
997 		OutputError(7,T("The EBML max size length is not supported: %d"),(int)EL_Int(RLevel1));
998 
999 	RLevel1 = (ebml_master*)EBML_MasterGetChild(EbmlHead,&EBML_ContextDocType);
1000     EBML_StringGet((ebml_string*)RLevel1,String,TSIZEOF(String));
1001     if (tcscmp(String,T("matroska"))!=0 && tcscmp(String,T("webm"))!=0)
1002 	{
1003 		Result = OutputError(8,T("The EBML doctype is not supported: %s"),String);
1004 		goto exit;
1005 	}
1006 
1007 	EbmlDocVer = EBML_MasterGetChild(EbmlHead,&EBML_ContextDocTypeVersion);
1008 	EbmlReadDocVer = EBML_MasterGetChild(EbmlHead,&EBML_ContextDocTypeReadVersion);
1009 
1010 	if (EL_Int(EbmlReadDocVer) > EL_Int(EbmlDocVer))
1011 		OutputError(9,T("The DocType version %d is higher than the read Doctype version %d"),(int)EL_Int(EbmlDocVer),(int)EL_Int(EbmlReadDocVer));
1012 
1013 	if (tcscmp(String,T("matroska"))==0)
1014 	{
1015         if (DivX)
1016 			MatroskaProfile = PROFILE_DIVX;
1017         else if (EL_Int(EbmlDocVer)==4)
1018 		    MatroskaProfile = PROFILE_MATROSKA_V4;
1019         else if (EL_Int(EbmlDocVer)==3)
1020 		    MatroskaProfile = PROFILE_MATROSKA_V3;
1021         else if (EL_Int(EbmlDocVer)==2)
1022 		    MatroskaProfile = PROFILE_MATROSKA_V2;
1023 		else if (EL_Int(EbmlDocVer)==1)
1024 	    	MatroskaProfile = PROFILE_MATROSKA_V1;
1025 		else
1026 			OutputError(10,T("Unknown Matroska profile %d/%d"),(int)EL_Int(EbmlDocVer),(int)EL_Int(EbmlReadDocVer));
1027 	}
1028 	else if (tcscmp(String,T("webm"))==0)
1029 		MatroskaProfile = PROFILE_WEBM;
1030 
1031     if (!Quiet) TextWrite(StdErr,T("."));
1032 
1033 	// find the segment
1034 	RSegment = (ebml_master*)EBML_FindNextElement(Input, &RContext, &UpperElement, 1);
1035     RSegmentContext.Context = &MATROSKA_ContextSegment;
1036     RSegmentContext.EndPosition = EBML_ElementPositionEnd((ebml_element*)RSegment);
1037     RSegmentContext.UpContext = &RContext;
1038     RSegmentContext.Profile = MatroskaProfile;
1039 
1040     RContext.EndPosition = EBML_ElementPositionEnd((ebml_element*)RSegment);
1041 
1042 	UpperElement = 0;
1043 	DotCount = 0;
1044 	Prev = NULL;
1045     RLevel1 = (ebml_master*)EBML_FindNextElement(Input, &RSegmentContext, &UpperElement, 1);
1046     while (RLevel1)
1047 	{
1048         RLevelX = NULL;
1049         if (EL_Type(RLevel1, &MATROSKA_ContextCluster))
1050         {
1051             if (EBML_ElementReadData(RLevel1,Input,&RSegmentContext,0,SCOPE_PARTIAL_DATA,4)==ERR_NONE)
1052 			{
1053                 ArrayAppend(&RClusters,&RLevel1,sizeof(RLevel1),256);
1054 				NodeTree_SetParent(RLevel1, RSegment, NULL);
1055 				VoidAmount += CheckUnknownElements((ebml_element*)RLevel1);
1056 				Result |= CheckProfileViolation((ebml_element*)RLevel1, MatroskaProfile);
1057                 RLevelX = (ebml_master*)EBML_ElementSkipData((ebml_element*)RLevel1, Input, &RSegmentContext, NULL, 1);
1058 			}
1059 			else
1060 			{
1061 				Result = OutputError(0x180,T("Failed to read the Cluster at %") TPRId64 T(" size %") TPRId64,EL_Pos(RLevel1),EL_DataSize(RLevel1));
1062 				goto exit;
1063 			}
1064         }
1065         else if (EL_Type(RLevel1, &MATROSKA_ContextSeekHead))
1066         {
1067             if (Live)
1068             {
1069                 OutputWarning(0x170,T("The live stream has a SeekHead at %") TPRId64,EL_Pos(RLevel1));
1070 			    RLevelX = (ebml_master*)EBML_ElementSkipData((ebml_element*)RLevel1, Input, &RSegmentContext, NULL, 1);
1071                 NodeDelete((node*)RLevel1);
1072                 RLevel1 = NULL;
1073             }
1074             else if (EBML_ElementReadData(RLevel1,Input,&RSegmentContext,1,SCOPE_ALL_DATA,2)==ERR_NONE)
1075 			{
1076 				if (!RSeekHead)
1077 					RSeekHead = RLevel1;
1078 				else if (!RSeekHead2)
1079                 {
1080 					OutputWarning(0x103,T("Unnecessary secondary SeekHead was found at %") TPRId64,EL_Pos(RLevel1));
1081 					RSeekHead2 = RLevel1;
1082                 }
1083 				else
1084 					OutputWarning(0x101,T("Extra SeekHead found at %") TPRId64 T(" (size %") TPRId64 T(")"),EL_Pos(RLevel1),EL_DataSize(RLevel1));
1085 				NodeTree_SetParent(RLevel1, RSegment, NULL);
1086 				VoidAmount += CheckUnknownElements((ebml_element*)RLevel1);
1087 				Result |= CheckProfileViolation((ebml_element*)RLevel1, MatroskaProfile);
1088 			}
1089 			else
1090 			{
1091 				Result = OutputError(0x100,T("Failed to read the SeekHead at %") TPRId64 T(" size %") TPRId64,EL_Pos(RLevel1),EL_DataSize(RLevel1));
1092 				goto exit;
1093 			}
1094 		}
1095         else if (EL_Type(RLevel1, &MATROSKA_ContextInfo))
1096         {
1097             if (EBML_ElementReadData(RLevel1,Input,&RSegmentContext,1,SCOPE_ALL_DATA,1)==ERR_NONE)
1098 			{
1099 				if (RSegmentInfo != NULL)
1100 					OutputWarning(0x110,T("Extra SegmentInfo found at %") TPRId64 T(" (size %") TPRId64 T(")"),EL_Pos(RLevel1),EL_DataSize(RLevel1));
1101 				else
1102 				{
1103 					RSegmentInfo = RLevel1;
1104 					NodeTree_SetParent(RLevel1, RSegment, NULL);
1105 					VoidAmount += CheckUnknownElements((ebml_element*)RLevel1);
1106 					Result |= CheckProfileViolation((ebml_element*)RLevel1, MatroskaProfile);
1107 
1108                     if (Live)
1109                     {
1110                         ebml_master *Elt = (ebml_master*)EBML_MasterFindChild(RLevel1,&MATROSKA_ContextDuration);
1111                         if (Elt)
1112                             OutputWarning(0x112,T("The live Segment has a duration set at %") TPRId64,EL_Pos(Elt));
1113                     }
1114                 }
1115 			}
1116 			else
1117 			{
1118 				Result = OutputError(0x111,T("Failed to read the SegmentInfo at %") TPRId64 T(" size %") TPRId64,EL_Pos(RLevel1),EL_DataSize(RLevel1));
1119 				goto exit;
1120 			}
1121 		}
1122         else if (EL_Type(RLevel1, &MATROSKA_ContextTracks))
1123         {
1124             if (EBML_ElementReadData(RLevel1,Input,&RSegmentContext,1,SCOPE_ALL_DATA,4)==ERR_NONE)
1125 			{
1126 				if (RTrackInfo != NULL)
1127 					OutputWarning(0x120,T("Extra TrackInfo found at %") TPRId64 T(" (size %") TPRId64 T(")"),EL_Pos(RLevel1),EL_DataSize(RLevel1));
1128 				else
1129 				{
1130                     size_t TrackCount;
1131 					ebml_master *Elt;
1132 
1133                     RTrackInfo = RLevel1;
1134 					NodeTree_SetParent(RLevel1, RSegment, NULL);
1135 					VoidAmount += CheckUnknownElements((ebml_element*)RLevel1);
1136 					Result |= CheckProfileViolation((ebml_element*)RLevel1, MatroskaProfile);
1137 
1138                     Elt = (ebml_master*)EBML_MasterFindChild(RTrackInfo,&MATROSKA_ContextTrackEntry);
1139                     TrackCount = 0;
1140                     while (Elt)
1141                     {
1142 						Elt = (ebml_master*)EBML_MasterNextChild(RTrackInfo,Elt);
1143                         ++TrackCount;
1144                     }
1145 
1146                     ArrayResize(&Tracks,TrackCount*sizeof(track_info),256);
1147                     ArrayZero(&Tracks);
1148 
1149                     Elt = (ebml_master*)EBML_MasterFindChild(RTrackInfo,&MATROSKA_ContextTrackEntry);
1150                     TrackCount = 0;
1151                     while (Elt)
1152                     {
1153                         EbmlDocVer = EBML_MasterFindChild(Elt,&MATROSKA_ContextTrackNumber);
1154                         assert(EbmlDocVer!=NULL);
1155                         if (EbmlDocVer)
1156                         {
1157                             TrackMax = max(TrackMax,(size_t)EL_Int(EbmlDocVer));
1158                             ARRAYBEGIN(Tracks,track_info)[TrackCount].Num = (int)EL_Int(EbmlDocVer);
1159                         }
1160                         EbmlDocVer = EBML_MasterFindChild(Elt,&MATROSKA_ContextTrackType);
1161                         assert(EbmlDocVer!=NULL);
1162                         if (EbmlDocVer)
1163                         {
1164                             if (EL_Int(EbmlDocVer)==TRACK_TYPE_VIDEO)
1165 							{
1166 								Result |= CheckVideoTrack(Elt, ARRAYBEGIN(Tracks,track_info)[TrackCount].Num, MatroskaProfile);
1167                                 HasVideo = 1;
1168 							}
1169                             ARRAYBEGIN(Tracks,track_info)[TrackCount].Kind = (int)EL_Int(EbmlDocVer);
1170                         }
1171                         ARRAYBEGIN(Tracks,track_info)[TrackCount].CodecID = (ebml_string*)EBML_MasterFindChild(Elt,&MATROSKA_ContextCodecID);
1172                         Elt = (ebml_master*)EBML_MasterNextChild(RTrackInfo,Elt);
1173                         ++TrackCount;
1174                     }
1175                     EbmlDocVer = NULL;
1176                     Elt = NULL;
1177                 }
1178 			}
1179 			else
1180 			{
1181 				Result = OutputError(0x121,T("Failed to read the TrackInfo at %") TPRId64 T(" size %") TPRId64,EL_Pos(RLevel1),EL_DataSize(RLevel1));
1182 				goto exit;
1183 			}
1184 		}
1185         else if (EL_Type(RLevel1, &MATROSKA_ContextCues))
1186         {
1187             if (Live)
1188             {
1189                 OutputWarning(0x171,T("The live stream has Cues at %") TPRId64,EL_Pos(RLevel1));
1190 			    RLevelX = (ebml_master*)EBML_ElementSkipData((ebml_element*)RLevel1, Input, &RSegmentContext, NULL, 1);
1191                 NodeDelete((node*)RLevel1);
1192                 RLevel1 = NULL;
1193             }
1194             else if (EBML_ElementReadData(RLevel1,Input,&RSegmentContext,1,SCOPE_ALL_DATA,3)==ERR_NONE)
1195 			{
1196 				if (RCues != NULL)
1197 					OutputWarning(0x130,T("Extra Cues found at %") TPRId64 T(" (size %") TPRId64 T(")"),EL_Pos(RLevel1),EL_DataSize(RLevel1));
1198 				else
1199 				{
1200 					RCues = RLevel1;
1201 					NodeTree_SetParent(RLevel1, RSegment, NULL);
1202 					VoidAmount += CheckUnknownElements((ebml_element*)RLevel1);
1203 					Result |= CheckProfileViolation((ebml_element*)RLevel1, MatroskaProfile);
1204 				}
1205 			}
1206 			else
1207 			{
1208 				Result = OutputError(0x131,T("Failed to read the Cues at %") TPRId64 T(" size %") TPRId64,EL_Pos(RLevel1),EL_DataSize(RLevel1));
1209 				goto exit;
1210 			}
1211 		}
1212         else if (EL_Type(RLevel1, &MATROSKA_ContextChapters))
1213         {
1214             if (Live)
1215             {
1216                 Result |= OutputError(0x172,T("The live stream has Chapters at %") TPRId64,EL_Pos(RLevel1));
1217 			    RLevelX = (ebml_master*)EBML_ElementSkipData((ebml_element*)RLevel1, Input, &RSegmentContext, NULL, 1);
1218                 NodeDelete((node*)RLevel1);
1219                 RLevel1 = NULL;
1220             }
1221             else if (EBML_ElementReadData(RLevel1,Input,&RSegmentContext,1,SCOPE_ALL_DATA,16)==ERR_NONE)
1222 			{
1223 				if (RChapters != NULL)
1224 					OutputWarning(0x140,T("Extra Chapters found at %") TPRId64 T(" (size %") TPRId64 T(")"),EL_Pos(RLevel1),EL_DataSize(RLevel1));
1225 				else
1226 				{
1227 					RChapters = RLevel1;
1228 					NodeTree_SetParent(RLevel1, RSegment, NULL);
1229 					VoidAmount += CheckUnknownElements((ebml_element*)RLevel1);
1230 					Result |= CheckProfileViolation((ebml_element*)RLevel1, MatroskaProfile);
1231 				}
1232 			}
1233 			else
1234 			{
1235 				Result = OutputError(0x141,T("Failed to read the Chapters at %") TPRId64 T(" size %") TPRId64,EL_Pos(RLevel1),EL_DataSize(RLevel1));
1236 				goto exit;
1237 			}
1238 		}
1239         else if (EL_Type(RLevel1, &MATROSKA_ContextTags))
1240         {
1241             if (EBML_ElementReadData(RLevel1,Input,&RSegmentContext,1,SCOPE_ALL_DATA,4)==ERR_NONE)
1242 			{
1243 				if (RTags != NULL)
1244 					Result |= OutputError(0x150,T("Extra Tags found at %") TPRId64 T(" (size %") TPRId64 T(")"),EL_Pos(RLevel1),EL_DataSize(RLevel1));
1245 				else
1246 				{
1247 					RTags = RLevel1;
1248 					NodeTree_SetParent(RLevel1, RSegment, NULL);
1249 					VoidAmount += CheckUnknownElements((ebml_element*)RLevel1);
1250 					Result |= CheckProfileViolation((ebml_element*)RLevel1, MatroskaProfile);
1251 				}
1252 			}
1253 			else
1254 			{
1255 				Result = OutputError(0x151,T("Failed to read the Tags at %") TPRId64 T(" size %") TPRId64,EL_Pos(RLevel1),EL_DataSize(RLevel1));
1256 				goto exit;
1257 			}
1258 		}
1259         else if (EL_Type(RLevel1, &MATROSKA_ContextAttachments))
1260         {
1261             if (Live)
1262             {
1263                 Result |= OutputError(0x173,T("The live stream has a Attachments at %") TPRId64,EL_Pos(RLevel1));
1264 			    RLevelX = (ebml_master*)EBML_ElementSkipData((ebml_element*)RLevel1, Input, &RSegmentContext, NULL, 1);
1265                 NodeDelete((node*)RLevel1);
1266                 RLevel1 = NULL;
1267             }
1268             else if (EBML_ElementReadData(RLevel1,Input,&RSegmentContext,1,SCOPE_ALL_DATA,3)==ERR_NONE)
1269 			{
1270 				if (RAttachments != NULL)
1271 					Result |= OutputError(0x160,T("Extra Attachments found at %") TPRId64 T(" (size %") TPRId64 T(")"),EL_Pos(RLevel1),EL_DataSize(RLevel1));
1272 				else
1273 				{
1274 					RAttachments = RLevel1;
1275 					NodeTree_SetParent(RLevel1, RSegment, NULL);
1276 					VoidAmount += CheckUnknownElements((ebml_element*)RLevel1);
1277 					Result |= CheckProfileViolation((ebml_element*)RLevel1, MatroskaProfile);
1278 				}
1279 			}
1280 			else
1281 			{
1282 				Result = OutputError(0x161,T("Failed to read the Attachments at %") TPRId64 T(" size %") TPRId64,EL_Pos(RLevel1),EL_DataSize(RLevel1));
1283 				goto exit;
1284 			}
1285 		}
1286 		else
1287 		{
1288 			if (Node_IsPartOf(RLevel1,EBML_DUMMY_ID))
1289 			{
1290 				tchar_t Id[32];
1291 				EBML_IdToString(Id,TSIZEOF(Id),EBML_ElementClassID((ebml_element*)RLevel1));
1292 				OutputWarning(0x80,T("Unknown element %s at %") TPRId64 T(" size %") TPRId64,Id,EL_Pos(RLevel1),EL_DataSize(RLevel1));
1293 			}
1294 			if (Node_IsPartOf(RLevel1,EBML_VOID_CLASS))
1295 			{
1296 				VoidAmount += EBML_ElementFullSize((ebml_element*)RLevel1,0);
1297 			}
1298 			RLevelX = (ebml_master*)EBML_ElementSkipData((ebml_element*)RLevel1, Input, &RSegmentContext, NULL, 1);
1299             NodeDelete((node*)RLevel1);
1300             RLevel1 = NULL;
1301 		}
1302         if (!Quiet) {
1303             TextWrite(StdErr,T(".")); ++DotCount;
1304 		    if (!(DotCount % 60))
1305 			    TextWrite(StdErr,T("\r                                                              \r"));
1306         }
1307 
1308 		Prev = RLevel1;
1309         if (RLevelX)
1310             RLevel1 = RLevelX;
1311         else
1312 		    RLevel1 = (ebml_master*)EBML_FindNextElement(Input, &RSegmentContext, &UpperElement, 1);
1313 	}
1314 
1315 	if (!RSegmentInfo)
1316 	{
1317 		Result = OutputError(0x40,T("The segment is missing a SegmentInfo"));
1318 		goto exit;
1319 	}
1320 
1321 	if (Prev)
1322 	{
1323 		if (EBML_ElementPositionEnd((ebml_element*)RSegment)!=INVALID_FILEPOS_T && EBML_ElementPositionEnd((ebml_element*)RSegment)!=EBML_ElementPositionEnd((ebml_element*)Prev))
1324 			Result |= OutputError(0x42,T("The segment's size %") TPRId64 T(" doesn't match the position where it ends %") TPRId64,EBML_ElementPositionEnd((ebml_element*)RSegment),EBML_ElementPositionEnd((ebml_element*)Prev));
1325 	}
1326 
1327 	if (!RSeekHead)
1328     {
1329         if (!Live)
1330 		    OutputWarning(0x801,T("The segment has no SeekHead section"));
1331     }
1332 	else
1333 		Result |= CheckSeekHead(RSeekHead);
1334 	if (RSeekHead2)
1335 		Result |= CheckSeekHead(RSeekHead2);
1336 
1337 	if (ARRAYCOUNT(RClusters,ebml_element*))
1338 	{
1339         if (!Quiet) TextWrite(StdErr,T("."));
1340 		LinkClusterBlocks();
1341 
1342         if (HasVideo)
1343             Result |= CheckVideoStart();
1344         Result |= CheckLacingKeyframe();
1345         Result |= CheckPosSize((ebml_element*)RSegment);
1346 		if (!RCues)
1347         {
1348             if (!Live && ARRAYCOUNT(RClusters,ebml_element*)>1)
1349 			    OutputWarning(0x800,T("The segment has Clusters but no Cues section (bad for seeking)"));
1350         }
1351 		else
1352 			Result |= CheckCueEntries(RCues);
1353 		if (!RTrackInfo)
1354 		{
1355 			Result = OutputError(0x41,T("The segment has Clusters but no TrackInfo section"));
1356 			goto exit;
1357 		}
1358 	}
1359 
1360     if (!Quiet) TextWrite(StdErr,T("."));
1361 	if (RTrackInfo)
1362 		CheckTracks(RTrackInfo, MatroskaProfile);
1363 
1364     for (TI=ARRAYBEGIN(Tracks,track_info); TI!=ARRAYEND(Tracks,track_info); ++TI)
1365     {
1366         if (TI->DataLength==0)
1367             OutputWarning(0xB8,T("Track #%d is defined but has no frame"),TI->Num);
1368     }
1369 
1370 	if (VoidAmount > 4*1024)
1371 		OutputWarning(0xD0,T("There are %") TPRId64 T(" bytes of void data\r\n"),VoidAmount);
1372 
1373 	if (!Quiet && Result==0)
1374     {
1375         TextPrintf(StdErr,T("\r%s %s: the file appears to be valid\r\n"),PROJECT_NAME,PROJECT_VERSION);
1376         if (Details)
1377         {
1378             track_info *TI;
1379             for (TI=ARRAYBEGIN(Tracks,track_info); TI!=ARRAYEND(Tracks,track_info); ++TI)
1380             {
1381                 EBML_StringGet(TI->CodecID,String,TSIZEOF(String));
1382                 TextPrintf(StdErr,T("Track #%d %18s %") TPRId64 T(" bits/s\r\n"),TI->Num,String,Scale64(TI->DataLength,8000000000,MaxTime-MinTime));
1383             }
1384         }
1385     }
1386 
1387 exit:
1388 	if (!Quiet && RSegmentInfo)
1389 	{
1390 		tchar_t App[MAXPATH];
1391 		App[0] = 0;
1392 		LibName = (ebml_string*)EBML_MasterFindChild(RSegmentInfo,&MATROSKA_ContextMuxingApp);
1393 		AppName = (ebml_string*)EBML_MasterFindChild(RSegmentInfo,&MATROSKA_ContextWritingApp);
1394 		if (LibName)
1395 		{
1396 			EBML_StringGet(LibName,String,TSIZEOF(String));
1397 			tcscat_s(App,TSIZEOF(App),String);
1398 		}
1399 		if (AppName)
1400 		{
1401 			EBML_StringGet(AppName,String,TSIZEOF(String));
1402 			if (App[0])
1403 				tcscat_s(App,TSIZEOF(App),T(" / "));
1404 			tcscat_s(App,TSIZEOF(App),String);
1405 		}
1406 		if (App[0]==0)
1407 			tcscat_s(App,TSIZEOF(App),T("<unknown>"));
1408 	    TextPrintf(StdErr,T("\r\tfile created with %s\r\n"),App);
1409 	}
1410 
1411     for (Cluster = ARRAYBEGIN(RClusters,ebml_master*);Cluster != ARRAYEND(RClusters,ebml_master*); ++Cluster)
1412         NodeDelete((node*)*Cluster);
1413     ArrayClear(&RClusters);
1414     if (RAttachments)
1415         NodeDelete((node*)RAttachments);
1416     if (RTags)
1417         NodeDelete((node*)RTags);
1418     if (RCues)
1419         NodeDelete((node*)RCues);
1420     if (RChapters)
1421         NodeDelete((node*)RChapters);
1422     if (RTrackInfo)
1423         NodeDelete((node*)RTrackInfo);
1424     if (RSegmentInfo)
1425         NodeDelete((node*)RSegmentInfo);
1426     if (RLevel1)
1427         NodeDelete((node*)RLevel1);
1428     if (RSegment)
1429         NodeDelete((node*)RSegment);
1430     if (EbmlHead)
1431         NodeDelete((node*)EbmlHead);
1432     ArrayClear(&Tracks);
1433     if (Input)
1434         StreamClose(Input);
1435 
1436     // EBML & Matroska ending
1437     MATROSKA_Done((nodecontext*)&p);
1438 
1439     // Core-C ending
1440 	StdAfx_Done((nodemodule*)&p);
1441     ParserContext_Done(&p);
1442 
1443     return Result;
1444 }
1445