1 /*
2  * $Id: ebmlelement.c 642 2010-11-28 08:38:47Z robux4 $
3  * Copyright (c) 2008-2010, 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 "ebml/ebml.h"
29 #include "ebml/ebml_internal.h"
30 
ValidateSize(const ebml_element * p)31 static bool_t ValidateSize(const ebml_element *p)
32 {
33     return 1;
34 }
35 
PostCreate(ebml_element * Element,bool_t SetDefault)36 static void PostCreate(ebml_element *Element, bool_t SetDefault)
37 {
38     Element->DefaultSize = -1;
39     Element->ElementPosition = INVALID_FILEPOS_T;
40     Element->SizePosition = INVALID_FILEPOS_T;
41 }
42 
NeedsDataSizeUpdate(ebml_element * Element,bool_t bWithDefault)43 static bool_t NeedsDataSizeUpdate(ebml_element *Element, bool_t bWithDefault)
44 {
45     if (!Element->bNeedDataSizeUpdate)
46         return 0;
47     if (!bWithDefault && EBML_ElementIsDefaultValue(Element))
48         return 0;
49     return 1;
50 }
51 
UpdateDataSize(ebml_element * Element,bool_t bWithDefault,bool_t bForceWithoutMandatory)52 static filepos_t UpdateDataSize(ebml_element *Element, bool_t bWithDefault, bool_t bForceWithoutMandatory)
53 {
54 	if (!bWithDefault && EBML_ElementIsDefaultValue(Element))
55 		return 0;
56 
57     if (Element->DefaultSize > Element->DataSize)
58 		Element->DataSize = Element->DefaultSize;
59 
60     Element->bNeedDataSizeUpdate = 0;
61 #if !defined(NDEBUG)
62     assert(!EBML_ElementNeedsDataSizeUpdate(Element, bWithDefault));
63 #endif
64     return Element->DataSize;
65 }
66 
Create(ebml_element * Element)67 static err_t Create(ebml_element *Element)
68 {
69     Element->DataSize = INVALID_FILEPOS_T;
70     Element->bNeedDataSizeUpdate = 1;
71     return ERR_NONE;
72 }
73 
META_START(EBMLElement_Class,EBML_ELEMENT_CLASS)74 META_START(EBMLElement_Class,EBML_ELEMENT_CLASS)
75 META_CLASS(SIZE,sizeof(ebml_element))
76 META_CLASS(VMT_SIZE,sizeof(ebml_element_vmt))
77 META_CLASS(FLAGS,CFLAG_ABSTRACT)
78 META_CLASS(CREATE,Create)
79 META_VMT(TYPE_FUNC,ebml_element_vmt,PostCreate,PostCreate)
80 META_VMT(TYPE_FUNC,ebml_element_vmt,ValidateSize,ValidateSize)
81 META_VMT(TYPE_FUNC,ebml_element_vmt,UpdateDataSize,UpdateDataSize)
82 META_VMT(TYPE_FUNC,ebml_element_vmt,NeedsDataSizeUpdate,NeedsDataSizeUpdate)
83 
84 META_PARAM(TYPE,EBML_ELEMENT_INFINITESIZE,TYPE_BOOLEAN)
85 META_DYNAMIC(TYPE_BOOLEAN,EBML_ELEMENT_INFINITESIZE)
86 
87 META_PARAM(TYPE,EBML_ELEMENT_OBJECT,TYPE_PTR)
88 META_DYNAMIC(TYPE_PTR,EBML_ELEMENT_OBJECT)
89 
90 META_END_CONTINUE(NODETREE_CLASS)
91 
92 META_START_CONTINUE(EBML_DUMMY_ID)
93 META_CLASS(SIZE,sizeof(ebml_dummy))
94 META_END(EBML_BINARY_CLASS)
95 
96 bool_t EBML_ElementIsFiniteSize(const ebml_element *Element)
97 {
98     return (Node_GetData((const node*)Element,EBML_ELEMENT_INFINITESIZE,TYPE_BOOLEAN) == 0);
99 }
100 
EBML_ElementSetInfiniteSize(const ebml_element * Element,bool_t Set)101 void EBML_ElementSetInfiniteSize(const ebml_element *Element, bool_t Set)
102 {
103     bool_t b = Set;
104     Node_SetData((node*)Element,EBML_ELEMENT_INFINITESIZE,TYPE_BOOLEAN,&b);
105 }
106 
EBML_ElementIsDummy(const ebml_element * Element)107 bool_t EBML_ElementIsDummy(const ebml_element *Element)
108 {
109     return Node_IsPartOf(Element,EBML_DUMMY_ID);
110 }
111 
EBML_ElementSkipData(ebml_element * p,stream * Input,const ebml_parser_context * Context,ebml_element * TestReadElt,bool_t AllowDummyElt)112 ebml_element *EBML_ElementSkipData(ebml_element *p, stream *Input, const ebml_parser_context *Context, ebml_element *TestReadElt, bool_t AllowDummyElt)
113 {
114 	ebml_element *Result = NULL;
115 	if (EBML_ElementIsFiniteSize(p)) {
116 		assert(TestReadElt == NULL);
117 		assert(p->ElementPosition < p->SizePosition);
118 		Stream_Seek(Input, EBML_ElementPositionEnd(p), SEEK_SET);
119 	} else {
120 		// read elements until an upper element is found
121 		int bUpperElement = 0; // trick to call FindNextID correctly
122 		Result = EBML_FindNextElement(Input, Context, &bUpperElement, AllowDummyElt);
123 		if (Result != NULL)
124             Stream_Seek(Input, EBML_ElementPositionData(Result), SEEK_SET);
125 	}
126 	return Result;
127 }
128 
EBML_ElementContext(const ebml_element * Element)129 const ebml_context *EBML_ElementContext(const ebml_element *Element)
130 {
131     return Element->Context;
132 }
133 
EBML_ElementFullSize(const ebml_element * Element,bool_t bWithDefault)134 filepos_t EBML_ElementFullSize(const ebml_element *Element, bool_t bWithDefault)
135 {
136 	if (!bWithDefault && EBML_ElementIsDefaultValue(Element))
137 		return INVALID_FILEPOS_T; // won't be saved
138 	return Element->DataSize + GetIdLength(Element->Context->Id) + EBML_CodedSizeLength(Element->DataSize, Element->SizeLength, EBML_ElementIsFiniteSize(Element));
139 }
140 
EBML_ElementDataSize(const ebml_element * Element,bool_t bWithDefault)141 filepos_t EBML_ElementDataSize(const ebml_element *Element, bool_t bWithDefault)
142 {
143     return Element->DataSize;
144 }
145 
EBML_ElementForceDataSize(ebml_element * Element,filepos_t Size)146 void EBML_ElementForceDataSize(ebml_element *Element, filepos_t Size)
147 {
148     Element->DataSize = Size;
149 }
150 
EBML_ElementSizeLength(const ebml_element * Element)151 uint8_t EBML_ElementSizeLength(const ebml_element *Element)
152 {
153     return Element->SizeLength;
154 }
155 
EBML_ElementSetSizeLength(ebml_element * Element,uint8_t SizeLength)156 void EBML_ElementSetSizeLength(ebml_element *Element, uint8_t SizeLength)
157 {
158     Element->SizeLength = SizeLength;
159 }
160 
EBML_ElementIsType(const ebml_element * Element,const ebml_context * Context)161 bool_t EBML_ElementIsType(const ebml_element *Element, const ebml_context *Context)
162 {
163     return Element->Context->Id == Context->Id;
164 }
165 
EBML_ElementClassID(const ebml_element * Element)166 fourcc_t EBML_ElementClassID(const ebml_element *Element)
167 {
168     return Element->Context->Id;
169 }
170 
EBML_ElementPosition(const ebml_element * Element)171 filepos_t EBML_ElementPosition(const ebml_element *Element)
172 {
173     return Element->ElementPosition;
174 }
175 
EBML_ElementForcePosition(ebml_element * Element,filepos_t Pos)176 void EBML_ElementForcePosition(ebml_element *Element, filepos_t Pos)
177 {
178     Element->ElementPosition = Pos;
179 }
180 
EBML_ElementPositionData(const ebml_element * Element)181 filepos_t EBML_ElementPositionData(const ebml_element *Element)
182 {
183     if (!EBML_ElementIsFiniteSize(Element))
184     {
185         if (!Element->SizeLength)
186             return INVALID_FILEPOS_T;
187         return Element->SizePosition + Element->SizeLength;
188     }
189     else
190         return Element->SizePosition + EBML_CodedSizeLength(Element->DataSize,Element->SizeLength,1);
191 }
192 
EBML_ElementPositionEnd(const ebml_element * Element)193 filepos_t EBML_ElementPositionEnd(const ebml_element *Element)
194 {
195     if (!EBML_ElementIsFiniteSize(Element))
196         return INVALID_FILEPOS_T; // the end position is unknown
197     else
198         return Element->SizePosition + EBML_CodedSizeLength(Element->DataSize,Element->SizeLength,1) + Element->DataSize;
199 }
200 
EBML_ElementInfiniteForceSize(ebml_element * Element,filepos_t NewSize)201 bool_t EBML_ElementInfiniteForceSize(ebml_element *Element, filepos_t NewSize)
202 {
203 	int OldSizeLen;
204 	filepos_t OldSize;
205 
206     assert(!EBML_ElementIsFiniteSize(Element));
207 	if (EBML_ElementIsFiniteSize(Element))
208 		return 0;
209 
210 	OldSizeLen = EBML_CodedSizeLength(Element->DataSize, Element->SizeLength, EBML_ElementIsFiniteSize(Element));
211 	OldSize = Element->DataSize;
212 	Element->DataSize = NewSize;
213 
214 	if (EBML_CodedSizeLength(Element->DataSize, Element->SizeLength, EBML_ElementIsFiniteSize(Element)) == OldSizeLen)
215     {
216 		EBML_ElementSetInfiniteSize(Element,1);
217 		return 1;
218 	}
219 	Element->DataSize = OldSize;
220 
221 	return 0;
222 }
223 
GetIdLength(fourcc_t Id)224 size_t GetIdLength(fourcc_t Id)
225 {
226     if ((Id & 0xFFFFFF00)==0)
227         return 1;
228     if ((Id & 0xFFFF0000)==0)
229         return 2;
230     if ((Id & 0xFF000000)==0)
231         return 3;
232     return 4;
233 }
234 
EBML_FillBufferID(uint8_t * Buffer,size_t BufSize,fourcc_t Id)235 size_t EBML_FillBufferID(uint8_t *Buffer, size_t BufSize, fourcc_t Id)
236 {
237     size_t i,FinalHeadSize = GetIdLength(Id);
238     if (BufSize < FinalHeadSize)
239         return 0;
240     for (i=0;i<FinalHeadSize;++i)
241         Buffer[FinalHeadSize-i-1] = (uint8_t)(Id >> (i<<3));
242     return FinalHeadSize;
243 }
244 
EBML_IdToString(tchar_t * Out,size_t OutLen,fourcc_t Id)245 size_t EBML_IdToString(tchar_t *Out, size_t OutLen, fourcc_t Id)
246 {
247     size_t i,FinalHeadSize = GetIdLength(Id);
248 	if (OutLen < FinalHeadSize*4+1)
249 		return 0;
250 	Out[0] = 0;
251     for (i=0;i<4;++i)
252 	{
253 		if (Out[0] || (Id >> 8*(3-i)) & 0xFF)
254 			stcatprintf_s(Out,OutLen,T("[%02X]"),(Id >> 8*(3-i)) & 0xFF);
255 	}
256 	return FinalHeadSize*4;
257 }
258 
EBML_BufferToID(const uint8_t * Buffer)259 fourcc_t EBML_BufferToID(const uint8_t *Buffer)
260 {
261 	if (Buffer[0] & 0x80)
262 		return (fourcc_t)Buffer[0];
263 	if (Buffer[0] & 0x40)
264 		return (fourcc_t)((Buffer[0] << 8) + Buffer[1]);
265 	if (Buffer[0] & 0x20)
266 		return (fourcc_t)((Buffer[0] << 16) + (Buffer[1] << 8) + Buffer[2]);
267 	if (Buffer[0] & 0x10)
268 		return (fourcc_t)((Buffer[0] << 24) + (Buffer[1] << 16) + (Buffer[2] << 8) + Buffer[3]);
269 	return 0;
270 }
271 
272 #if defined(CONFIG_EBML_WRITING)
EBML_ElementRender(ebml_element * Element,stream * Output,bool_t bWithDefault,bool_t bKeepPosition,bool_t bForceWithoutMandatory,filepos_t * Rendered)273 err_t EBML_ElementRender(ebml_element *Element, stream *Output, bool_t bWithDefault, bool_t bKeepPosition, bool_t bForceWithoutMandatory, filepos_t *Rendered)
274 {
275     err_t Result;
276     filepos_t _Rendered,WrittenSize;
277 #if !defined(NDEBUG)
278     filepos_t SupposedSize;
279 #endif
280 
281     if (!Rendered)
282         Rendered = &_Rendered;
283     *Rendered = 0;
284 
285     assert(Element->bValueIsSet || (bWithDefault && EBML_ElementIsDefaultValue(Element))); // an element is been rendered without a value set !!!
286 		                 // it may be a mandatory element without a default value
287 
288     if (!(Element->bValueIsSet || (bWithDefault && EBML_ElementIsDefaultValue(Element))))
289 		return ERR_INVALID_DATA;
290 
291 	if (!bWithDefault && EBML_ElementIsDefaultValue(Element))
292 		return ERR_INVALID_DATA;
293 
294 #if !defined(NDEBUG)
295     if (EBML_ElementNeedsDataSizeUpdate(Element, bWithDefault))
296 	    SupposedSize = EBML_ElementUpdateSize(Element, bWithDefault, bForceWithoutMandatory);
297     else
298         SupposedSize = Element->DataSize;
299 #else
300     if (EBML_ElementNeedsDataSizeUpdate(Element, bWithDefault))
301 	    EBML_ElementUpdateSize(Element, bWithDefault, bForceWithoutMandatory);
302 #endif
303 	Result = EBML_ElementRenderHead(Element, Output, bKeepPosition, &WrittenSize);
304     *Rendered += WrittenSize;
305     if (Result != ERR_NONE)
306         return Result;
307 
308     Result = EBML_ElementRenderData(Element, Output, bForceWithoutMandatory, bWithDefault, &WrittenSize);
309 #if !defined(NDEBUG)
310     if (SupposedSize != INVALID_FILEPOS_T) assert(WrittenSize == SupposedSize);
311 #endif
312     *Rendered += WrittenSize;
313 
314     return Result;
315 }
316 
EBML_ElementRenderHead(ebml_element * Element,stream * Output,bool_t bKeepPosition,filepos_t * Rendered)317 err_t EBML_ElementRenderHead(ebml_element *Element, stream *Output, bool_t bKeepPosition, filepos_t *Rendered)
318 {
319     err_t Err;
320 	uint8_t FinalHead[4+8]; // Class D + 64 bits coded size
321 	size_t i,FinalHeadSize;
322     int CodedSize;
323     filepos_t PosAfter,PosBefore = Stream_Seek(Output,0,SEEK_CUR);
324 
325 	FinalHeadSize = EBML_FillBufferID(FinalHead,sizeof(FinalHead),Element->Context->Id);
326 
327 	CodedSize = EBML_CodedSizeLength(Element->DataSize, Element->SizeLength, EBML_ElementIsFiniteSize(Element));
328 	EBML_CodedValueLength(Element->DataSize, CodedSize, &FinalHead[FinalHeadSize], EBML_ElementIsFiniteSize(Element));
329 	FinalHeadSize += CodedSize;
330 
331 	Err = Stream_Write(Output, FinalHead, FinalHeadSize, &i);
332     PosAfter = Stream_Seek(Output,0,SEEK_CUR);
333 	if (!bKeepPosition) {
334 		Element->ElementPosition = PosAfter - FinalHeadSize;
335 		Element->SizePosition = Element->ElementPosition + GetIdLength(Element->Context->Id);
336 	}
337     if (Rendered)
338         *Rendered = PosAfter - PosBefore;
339 	return Err;
340 }
341 #endif
342 
EBML_ElementGetName(const ebml_element * Element,tchar_t * Out,size_t OutLen)343 void EBML_ElementGetName(const ebml_element *Element, tchar_t *Out, size_t OutLen)
344 {
345     Node_FromStr(Element,Out,OutLen,Element->Context->ElementName);
346 }
347 
EBML_ElementGetClassName(const ebml_element * Element)348 const char *EBML_ElementGetClassName(const ebml_element *Element)
349 {
350     return Element->Context->ElementName;
351 }