ebmlelement.c 9.47 KB
Newer Older
1
/*
Steve Lhomme's avatar
Steve Lhomme committed
2
 * $Id$
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 * Copyright (c) 2008, Matroska Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * 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.
 *     * Neither the name of the Matroska Foundation nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY The Matroska Foundation ``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 Matroska Foundation 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.
 */
#include "ebml/ebml.h"

30
static bool_t ValidateSize(const ebml_element *p)
31 32 33 34 35 36 37 38 39 40 41
{
    return 1;
}

static void PostCreate(ebml_element *Element)
{
    Element->DefaultSize = -1;
    Element->ElementPosition = INVALID_FILEPOS_T;
    Element->SizePosition = INVALID_FILEPOS_T;
}

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
static bool_t NeedsDataSizeUpdate(ebml_element *Element, bool_t bWithDefault)
{
    if (!Element->bNeedDataSizeUpdate)
        return 0;
    if (!bWithDefault && EBML_ElementIsDefaultValue(Element))
        return 0;
    return 1;
}

static filepos_t UpdateDataSize(ebml_element *Element, bool_t bWithDefault, bool_t bForceRender)
{
	if (!bWithDefault && EBML_ElementIsDefaultValue(Element))
		return 0;

	if (Element->DefaultSize > Element->DataSize)
		Element->DataSize = Element->DefaultSize;

    Element->bNeedDataSizeUpdate = 0;
#if !defined(NDEBUG)
    assert(!EBML_ElementNeedsDataSizeUpdate(Element, bWithDefault));
#endif
    return Element->DataSize;
}

66 67
static err_t Create(ebml_element *Element)
{
68
    Element->DataSize = INVALID_FILEPOS_T;
69
    Element->bNeedDataSizeUpdate = 1;
70 71 72 73 74 75 76 77 78 79
    return ERR_NONE;
}

META_START(EBMLElement_Class,EBML_ELEMENT_CLASS)
META_CLASS(SIZE,sizeof(ebml_element))
META_CLASS(VMT_SIZE,sizeof(ebml_element_vmt))
META_CLASS(FLAGS,CFLAG_ABSTRACT)
META_CLASS(CREATE,Create)
META_VMT(TYPE_FUNC,ebml_element_vmt,PostCreate,PostCreate)
META_VMT(TYPE_FUNC,ebml_element_vmt,ValidateSize,ValidateSize)
80 81
META_VMT(TYPE_FUNC,ebml_element_vmt,UpdateDataSize,UpdateDataSize)
META_VMT(TYPE_FUNC,ebml_element_vmt,NeedsDataSizeUpdate,NeedsDataSizeUpdate)
82 83 84 85 86 87 88

META_PARAM(TYPE,EBML_ELEMENT_INFINITESIZE,TYPE_BOOLEAN)
META_DYNAMIC(TYPE_BOOLEAN,EBML_ELEMENT_INFINITESIZE)

META_PARAM(TYPE,EBML_ELEMENT_OBJECT,TYPE_PTR)
META_DYNAMIC(TYPE_PTR,EBML_ELEMENT_OBJECT)

89 90 91 92 93
META_END_CONTINUE(NODETREE_CLASS)

META_START_CONTINUE(EBML_DUMMY_ID)
META_CLASS(SIZE,sizeof(ebml_dummy))
META_END(EBML_BINARY_CLASS)
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116

bool_t EBML_ElementIsFiniteSize(const ebml_element *Element)
{
    return (Node_GetData((const node*)Element,EBML_ELEMENT_INFINITESIZE,TYPE_BOOLEAN) == 0);
}

void EBML_ElementSetInfiniteSize(const ebml_element *Element, bool_t Set)
{
    bool_t b = Set;
    Node_SetData((node*)Element,EBML_ELEMENT_INFINITESIZE,TYPE_BOOLEAN,&b);
}

bool_t EBML_ElementIsDummy(const ebml_element *Element)
{
    return Node_IsPartOf(Element,EBML_DUMMY_ID);
}

ebml_element *EBML_ElementSkipData(ebml_element *p, stream *Input, const ebml_parser_context *Context, ebml_element *TestReadElt, bool_t AllowDummyElt)
{
	ebml_element *Result = NULL;
	if (EBML_ElementIsFiniteSize(p)) {
		assert(TestReadElt == NULL);
		assert(p->ElementPosition < p->SizePosition);
117
		Stream_Seek(Input, EBML_ElementPositionEnd(p), SEEK_SET);
118 119
	} else {
		// read elements until an upper element is found
120 121 122 123
		int bUpperElement = 0; // trick to call FindNextID correctly
		Result = EBML_FindNextElement(Input, Context, &bUpperElement, AllowDummyElt);
		if (Result != NULL)
            Stream_Seek(Input, EBML_ElementPositionData(Result), SEEK_SET);
124 125 126 127 128 129 130 131
	}
	return Result;
}

filepos_t EBML_ElementFullSize(const ebml_element *Element, bool_t bWithDefault)
{
	if (!bWithDefault && EBML_ElementIsDefaultValue(Element))
		return INVALID_FILEPOS_T; // won't be saved
132
	return Element->DataSize + GetIdLength(Element->Context->Id) + EBML_CodedSizeLength(Element->DataSize, Element->SizeLength, EBML_ElementIsFiniteSize(Element));
133 134
}

135 136 137 138 139
fourcc_t EBML_ElementClassID(const ebml_element *Element)
{
    return Element->Context->Id;
}

140 141
bool_t EBML_ElementInfiniteForceSize(ebml_element *Element, filepos_t NewSize)
{
142 143
	int OldSizeLen;
	filepos_t OldSize;
144 145

    assert(!EBML_ElementIsFiniteSize(Element));
146 147 148
	if (EBML_ElementIsFiniteSize(Element))
		return 0;

149 150 151
	OldSizeLen = EBML_CodedSizeLength(Element->DataSize, Element->SizeLength, EBML_ElementIsFiniteSize(Element));
	OldSize = Element->DataSize;
	Element->DataSize = NewSize;
152

153
	if (EBML_CodedSizeLength(Element->DataSize, Element->SizeLength, EBML_ElementIsFiniteSize(Element)) == OldSizeLen)
154 155 156 157
    {
		EBML_ElementSetInfiniteSize(Element,1);
		return 1;
	}
158
	Element->DataSize = OldSize;
159 160

	return 0;
161 162
}

163 164 165 166 167 168 169 170 171 172 173
size_t GetIdLength(fourcc_t Id)
{
    if ((Id & 0xFFFFFF00)==0)
        return 1;
    if ((Id & 0xFFFF0000)==0)
        return 2;
    if ((Id & 0xFF000000)==0)
        return 3;
    return 4;
}

174 175 176 177 178 179 180 181 182 183
size_t EBML_FillBufferID(uint8_t *Buffer, size_t BufSize, fourcc_t Id)
{
    size_t i,FinalHeadSize = GetIdLength(Id);
    if (BufSize < FinalHeadSize)
        return 0;
    for (i=0;i<FinalHeadSize;++i)
        Buffer[FinalHeadSize-i-1] = (uint8_t)(Id >> (i<<3));
    return FinalHeadSize;
}

184 185 186 187 188 189 190 191 192 193 194 195 196 197
size_t EBML_IdToString(tchar_t *Out, size_t OutLen, fourcc_t Id)
{
    size_t i,FinalHeadSize = GetIdLength(Id);
	if (OutLen < FinalHeadSize*4+1)
		return 0;
	Out[0] = 0;
    for (i=0;i<4;++i)
	{
		if (Out[0] || (Id >> 8*(3-i)) & 0xFF)
			stcatprintf_s(Out,OutLen,T("[%02X]"),(Id >> 8*(3-i)) & 0xFF);
	}
	return FinalHeadSize*4;
}

198 199 200 201 202 203 204 205 206 207 208 209 210
fourcc_t EBML_BufferToID(const uint8_t *Buffer)
{
	if (Buffer[0] & 0x80)
		return (fourcc_t)Buffer[0];
	if (Buffer[0] & 0x40)
		return (fourcc_t)((Buffer[0] << 8) + Buffer[1]);
	if (Buffer[0] & 0x20)
		return (fourcc_t)((Buffer[0] << 16) + (Buffer[1] << 8) + Buffer[2]);
	if (Buffer[0] & 0x10)
		return (fourcc_t)((Buffer[0] << 24) + (Buffer[1] << 16) + (Buffer[2] << 8) + Buffer[3]);
	return 0;
}

211
#if defined(CONFIG_EBML_WRITING)
212
err_t EBML_ElementRender(ebml_element *Element, stream *Output, bool_t bWithDefault, bool_t bKeepPosition, bool_t bForceRender, filepos_t *Rendered)
213 214 215 216 217 218 219 220 221 222 223 224 225
{
    err_t Result;
    filepos_t _Rendered,WrittenSize;
#if !defined(NDEBUG)
    filepos_t SupposedSize;
#endif

    if (!Rendered)
        Rendered = &_Rendered;
    *Rendered = 0;

    assert(Element->bValueIsSet || (bWithDefault && Element->bDefaultIsSet)); // an element is been rendered without a value set !!!
		                 // it may be a mandatory element without a default value
226 227 228 229

    if (!(Element->bValueIsSet || (bWithDefault && Element->bDefaultIsSet)))
		return ERR_INVALID_DATA;

230 231 232 233
	if (!bWithDefault && EBML_ElementIsDefaultValue(Element))
		return ERR_INVALID_DATA;

#if !defined(NDEBUG)
234
    if (EBML_ElementNeedsDataSizeUpdate(Element, bWithDefault))
235 236 237
	    SupposedSize = EBML_ElementUpdateSize(Element, bWithDefault, bForceRender);
    else
        SupposedSize = Element->DataSize;
238
#else
239
    if (EBML_ElementNeedsDataSizeUpdate(Element, bWithDefault))
240
	    EBML_ElementUpdateSize(Element, bWithDefault, bForceRender);
241
#endif
242 243 244 245 246 247 248
	Result = EBML_ElementRenderHead(Element, Output, bKeepPosition, &WrittenSize);
    *Rendered += WrittenSize;
    if (Result != ERR_NONE)
        return Result;

    Result = EBML_ElementRenderData(Element, Output, bForceRender, bWithDefault, &WrittenSize);
#if !defined(NDEBUG)
249
    if (SupposedSize != INVALID_FILEPOS_T) assert(WrittenSize == SupposedSize);
250 251 252 253 254 255 256 257 258 259 260 261 262 263
#endif
    *Rendered += WrittenSize;

    return Result;
}

err_t EBML_ElementRenderHead(ebml_element *Element, stream *Output, bool_t bKeepPosition, filepos_t *Rendered)
{
    err_t Err;
	uint8_t FinalHead[4+8]; // Class D + 64 bits coded size
	size_t i,FinalHeadSize;
    int CodedSize;
    filepos_t PosAfter,PosBefore = Stream_Seek(Output,0,SEEK_CUR);
	
Steve Lhomme's avatar
Steve Lhomme committed
264
	FinalHeadSize = EBML_FillBufferID(FinalHead,sizeof(FinalHead),Element->Context->Id);
265

266
	CodedSize = EBML_CodedSizeLength(Element->DataSize, Element->SizeLength, EBML_ElementIsFiniteSize(Element));
267
	EBML_CodedValueLength(Element->DataSize, CodedSize, &FinalHead[FinalHeadSize], EBML_ElementIsFiniteSize(Element));
268 269 270 271 272 273 274 275 276 277 278 279 280
	FinalHeadSize += CodedSize;
	
	Err = Stream_Write(Output, FinalHead, FinalHeadSize, &i);
    PosAfter = Stream_Seek(Output,0,SEEK_CUR);
	if (!bKeepPosition) {
		Element->ElementPosition = PosAfter - FinalHeadSize;
		Element->SizePosition = Element->ElementPosition + GetIdLength(Element->Context->Id);
	}
    if (Rendered)
        *Rendered = PosAfter - PosBefore;
	return Err;
}
#endif
281 282 283 284 285 286 287 288 289 290

void EBML_ElementGetName(const ebml_element *Element, tchar_t *Out, size_t OutLen)
{
    Node_FromStr(Element,Out,OutLen,Element->Context->ElementName);
}

const char *EBML_ElementGetClassName(const ebml_element *Element)
{
    return Element->Context->ElementName;
}