Bump version to 0.6.4.1
[libmkv.git] / src / ebml.c
bloba2cb3a0968068130ab0f4d36fbfa338ab1abedf4
1 /*****************************************************************************
2 * matroska.c:
3 *****************************************************************************
4 * Copyright (C) 2005 x264 project
6 * Authors: Mike Matsnev
7 * Nathan Caldwell
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
23 #include "config.h"
24 #include "libmkv.h"
25 #include "matroska.h"
27 mk_Context *mk_createContext(mk_Writer *w, mk_Context *parent,
28 unsigned id)
30 mk_Context *c;
32 if (w->freelist) {
33 c = w->freelist;
34 w->freelist = w->freelist->next;
35 } else {
36 c = malloc(sizeof(*c));
37 memset(c, 0, sizeof(*c));
40 if (c == NULL)
41 return NULL;
43 c->parent = parent;
44 c->owner = w;
45 c->id = id;
47 if (c->owner->actlist)
48 c->owner->actlist->prev = &c->next;
49 c->next = c->owner->actlist;
50 c->prev = &c->owner->actlist;
51 c->owner->actlist = c;
53 return c;
56 int mk_appendContextData(mk_Context *c, const void *data, uint64_t size)
58 uint64_t ns = c->d_cur + size;
60 if (ns > c->d_max) {
61 void *dp;
62 uint64_t dn = c->d_max ? c->d_max << 1 : 16;
63 while (ns > dn)
64 dn <<= 1;
66 dp = realloc(c->data, dn);
67 if (dp == NULL)
68 return -1;
70 c->data = dp;
71 c->d_max = dn;
74 memcpy((char *) c->data + c->d_cur, data, size);
76 c->d_cur = ns;
78 return 0;
81 int mk_writeEbmlHeader(mk_Writer *w, const char *doctype,
82 uint64_t doctype_version,
83 uint64_t doctype_readversion)
85 mk_Context *c = NULL;
87 if ((c = mk_createContext(w, w->root, EBML_ID_HEADER)) == NULL) /* EBML */
88 return -1;
89 CHECK(mk_writeUInt(c, EBML_ID_EBMLVERSION, EBML_VERSION)); /* EBMLVersion */
90 CHECK(mk_writeUInt(c, EBML_ID_EBMLREADVERSION, EBML_VERSION)); /* EBMLReadVersion */
91 CHECK(mk_writeUInt(c, EBML_ID_EBMLMAXIDLENGTH, 4)); /* EBMLMaxIDLength */
92 CHECK(mk_writeUInt(c, EBML_ID_EBMLMAXSIZELENGTH, 8)); /* EBMLMaxSizeLength */
93 CHECK(mk_writeStr(c, EBML_ID_DOCTYPE, doctype)); /* DocType */
94 CHECK(mk_writeUInt(c, EBML_ID_DOCTYPEVERSION, doctype_version)); /* DocTypeVersion */
95 /* DocTypeReadversion */
96 CHECK(mk_writeUInt(c, EBML_ID_DOCTYPEREADVERSION, doctype_readversion));
97 CHECK(mk_closeContext(c, 0));
99 return 0;
102 int mk_writeID(mk_Context *c, unsigned id)
104 unsigned char c_id[4] = { id >> 24, id >> 16, id >> 8, id };
106 if (c_id[0])
107 return mk_appendContextData(c, c_id, 4);
108 if (c_id[1])
109 return mk_appendContextData(c, c_id + 1, 3);
110 if (c_id[2])
111 return mk_appendContextData(c, c_id + 2, 2);
112 return mk_appendContextData(c, c_id + 3, 1);
115 int mk_writeSize(mk_Context *c, uint64_t size)
117 unsigned char c_size[8] = { 0x01, size >> 48, size >> 40, size >> 32,
118 size >> 24, size >> 16, size >> 8, size };
120 if (size < 0x7fll) {
121 c_size[7] |= 0x80;
122 return mk_appendContextData(c, c_size + 7, 1);
124 if (size < 0x3fffll) {
125 c_size[6] |= 0x40;
126 return mk_appendContextData(c, c_size + 6, 2);
128 if (size < 0x1fffffll) {
129 c_size[5] |= 0x20;
130 return mk_appendContextData(c, c_size + 5, 3);
132 if (size < 0x0fffffffll) {
133 c_size[4] |= 0x10;
134 return mk_appendContextData(c, c_size + 4, 4);
136 if (size < 0x07ffffffffll) {
137 c_size[3] |= 0x08;
138 return mk_appendContextData(c, c_size + 3, 5);
140 if (size < 0x03ffffffffffll) {
141 c_size[2] |= 0x04;
142 return mk_appendContextData(c, c_size + 2, 6);
144 if (size < 0x01ffffffffffffll) {
145 c_size[1] |= 0x02;
146 return mk_appendContextData(c, c_size + 1, 7);
148 return mk_appendContextData(c, c_size, 8);
151 int mk_writeSSize(mk_Context *c, int64_t size)
153 uint64_t u_size = (uint64_t) llabs(size);
154 /* Need to shift by one below so ebmlSizeSize returns the correct size. */
155 unsigned size_size = mk_ebmlSizeSize(u_size << 1);
157 switch (size_size) {
158 case 1:
159 size += 0x3fll;
160 break;
161 case 2:
162 size += 0x1fffll;
163 break;
164 case 3:
165 size += 0x0fffffll;
166 break;
167 case 4:
168 size += 0x07ffffffll;
169 break;
170 case 5:
171 size += 0x03ffffffffll;
172 break;
173 case 6:
174 size += 0x01ffffffffffll;
175 break;
176 case 7:
177 size += 0x00ffffffffffffll;
178 break;
179 default: /* Matroska currently doesn't support any int > 56-bit. */
180 return -1;
183 return mk_writeSize(c, size);
186 int mk_flushContextID(mk_Context *c)
188 unsigned char size_undf[8] = { 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
190 if (c->id == 0)
191 return 0;
193 CHECK(mk_writeID(c->parent, c->id));
194 CHECK(mk_appendContextData(c->parent, &size_undf, 8));
196 c->id = 0;
198 return 0;
201 int mk_flushContextData(mk_Context *c)
203 mk_Writer *w = c->owner;
205 if (c->d_cur == 0)
206 return 0;
208 if (c->parent) {
209 CHECK(mk_appendContextData(c->parent, c->data, c->d_cur));
210 } else {
211 if (fwrite(c->data, c->d_cur, 1, w->fp) != 1)
212 return -1;
213 w->f_pos += c->d_cur;
214 if (w->f_pos > w->f_eof)
215 w->f_eof = w->f_pos;
218 c->d_cur = 0;
220 return 0;
223 int mk_closeContext(mk_Context *c, int64_t *off)
225 if (c->id) {
226 CHECK(mk_writeID(c->parent, c->id));
227 CHECK(mk_writeSize(c->parent, c->d_cur));
230 if (c->parent && off != NULL)
231 *off += c->parent->d_cur;
233 CHECK(mk_flushContextData(c));
235 if (c->next)
236 c->next->prev = c->prev;
237 *(c->prev) = c->next;
238 c->next = c->owner->freelist;
239 c->owner->freelist = c;
241 return 0;
244 void mk_destroyContexts(mk_Writer *w)
246 mk_Context *cur, *next;
248 for (cur = w->freelist; cur; cur = next) {
249 next = cur->next;
250 free(cur->data);
251 free(cur);
254 for (cur = w->actlist; cur; cur = next) {
255 next = cur->next;
256 free(cur->data);
257 free(cur);
260 w->freelist = w->actlist = w->root = NULL;
263 int mk_writeStr(mk_Context *c, unsigned id, const char *str)
265 size_t len = strlen(str);
267 CHECK(mk_writeID(c, id));
268 CHECK(mk_writeSize(c, len));
269 CHECK(mk_appendContextData(c, str, len));
270 return 0;
273 int mk_writeBin(mk_Context *c, unsigned id, const void *data,
274 unsigned size)
276 CHECK(mk_writeID(c, id));
277 CHECK(mk_writeSize(c, size));
278 CHECK(mk_appendContextData(c, data, size));
279 return 0;
282 int mk_writeUInt(mk_Context *c, unsigned id, uint64_t ui)
284 unsigned char c_ui[8] = { ui >> 56, ui >> 48, ui >> 40, ui >> 32,
285 ui >> 24, ui >> 16, ui >> 8, ui };
286 unsigned i = 0;
288 CHECK(mk_writeID(c, id));
289 while (i < 7 && c_ui[i] == 0)
290 ++i;
291 CHECK(mk_writeSize(c, 8 - i));
292 CHECK(mk_appendContextData(c, c_ui + i, 8 - i));
293 return 0;
296 int mk_writeSInt(mk_Context *c, unsigned id, int64_t si)
298 unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32,
299 si >> 24, si >> 16, si >> 8, si };
300 unsigned i = 0;
302 CHECK(mk_writeID(c, id));
303 if (si < 0) {
304 while (i < 7 && c_si[i] == 0xff && c_si[i + 1] & 0x80)
305 ++i;
306 } else {
307 while (i < 7 && c_si[i] == 0 && !(c_si[i + 1] & 0x80))
308 ++i;
310 CHECK(mk_writeSize(c, 8 - i));
311 CHECK(mk_appendContextData(c, c_si + i, 8 - i));
312 return 0;
315 int mk_writeFloatRaw(mk_Context *c, float f)
317 union {
318 float f;
319 unsigned u;
320 } u;
321 unsigned char c_f[4];
323 u.f = f;
324 c_f[0] = u.u >> 24;
325 c_f[1] = u.u >> 16;
326 c_f[2] = u.u >> 8;
327 c_f[3] = u.u;
329 return mk_appendContextData(c, c_f, 4);
332 int mk_writeFloat(mk_Context *c, unsigned id, float f)
334 CHECK(mk_writeID(c, id));
335 CHECK(mk_writeSize(c, 4));
336 CHECK(mk_writeFloatRaw(c, f));
337 return 0;
340 int mk_writeVoid(mk_Context *c, uint64_t length)
342 /* It would probably be faster to do this on the stack. */
343 char *c_void = calloc(length, sizeof(*c_void));
345 CHECK(mk_writeID(c, EBML_ID_VOID));
346 CHECK(mk_writeSize(c, length));
347 CHECK(mk_appendContextData(c, c_void, length));
348 free(c_void);
350 return 0;
353 unsigned mk_ebmlSizeSize(uint64_t s)
355 if (s < 0x7fll)
356 return 1;
357 if (s < 0x3fffll)
358 return 2;
359 if (s < 0x1fffffll)
360 return 3;
361 if (s < 0x0fffffffll)
362 return 4;
363 if (s < 0x07ffffffffll)
364 return 5;
365 if (s < 0x03ffffffffffll)
366 return 6;
367 if (s < 0x01ffffffffffffll)
368 return 7;
369 return 8;
372 unsigned mk_ebmlSIntSize(int64_t si)
374 unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32,
375 si >> 24, si >> 16, si >> 8, si };
376 unsigned i = 0;
378 if (si < 0) {
379 while (i < 7 && c_si[i] == 0xff && c_si[i + 1] & 0x80)
380 ++i;
381 } else {
382 while (i < 7 && c_si[i] == 0 && !(c_si[i + 1] & 0x80))
383 ++i;
386 return 8 - i;
389 unsigned mk_ebmlUIntSize(uint64_t ui)
391 unsigned char c_ui[8] = { ui >> 56, ui >> 48, ui >> 40, ui >> 32,
392 ui >> 24, ui >> 16, ui >> 8, ui };
393 unsigned i = 0;
395 while (i < 7 && c_ui[i] == 0)
396 ++i;
398 return 8 - i;