Move config.h to be the first include, so we get our defines before system includes.
[libmkv.git] / src / ebml.c
blobf3c8f85cd9e0695befb8161a460d066677e31c61
1 /*****************************************************************************
2 * matroska.c:
3 *****************************************************************************
4 * Copyright (C) 2005 x264 project
5 * $Id: $
7 * Authors: Mike Matsnev
8 * Nathan Caldwell
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
24 #include "config.h"
25 #include "libmkv.h"
26 #include "matroska.h"
28 mk_Context *mk_createContext(mk_Writer *w, mk_Context *parent, unsigned id) {
29 mk_Context *c;
31 if (w->freelist) {
32 c = w->freelist;
33 w->freelist = w->freelist->next;
34 } else {
35 c = malloc(sizeof(*c));
36 memset(c, 0, sizeof(*c));
39 if (c == NULL)
40 return NULL;
42 c->parent = parent;
43 c->owner = w;
44 c->id = id;
46 if (c->owner->actlist)
47 c->owner->actlist->prev = &c->next;
48 c->next = c->owner->actlist;
49 c->prev = &c->owner->actlist;
51 return c;
54 int mk_appendContextData(mk_Context *c, const void *data, uint64_t size) {
55 uint64_t ns = c->d_cur + size;
57 if (ns > c->d_max) {
58 void *dp;
59 uint64_t dn = c->d_max ? c->d_max << 1 : 16;
60 while (ns > dn)
61 dn <<= 1;
63 dp = realloc(c->data, dn);
64 if (dp == NULL)
65 return -1;
67 c->data = dp;
68 c->d_max = dn;
71 memcpy((char*)c->data + c->d_cur, data, size);
73 c->d_cur = ns;
75 return 0;
78 int mk_writeID(mk_Context *c, unsigned id) {
79 unsigned char c_id[4] = { id >> 24, id >> 16, id >> 8, id };
81 if (c_id[0])
82 return mk_appendContextData(c, c_id, 4);
83 if (c_id[1])
84 return mk_appendContextData(c, c_id+1, 3);
85 if (c_id[2])
86 return mk_appendContextData(c, c_id+2, 2);
87 return mk_appendContextData(c, c_id+3, 1);
90 int mk_writeSize(mk_Context *c, uint64_t size) {
91 unsigned char c_size[8] = { 0x01, size >> 48, size >> 40, size >> 32, size >> 24, size >> 16, size >> 8, size };
93 if (size < 0x7fll) {
94 c_size[7] |= 0x80;
95 return mk_appendContextData(c, c_size+7, 1);
97 if (size < 0x3fffll) {
98 c_size[6] |= 0x40;
99 return mk_appendContextData(c, c_size+6, 2);
101 if (size < 0x1fffffll) {
102 c_size[5] |= 0x20;
103 return mk_appendContextData(c, c_size+5, 3);
105 if (size < 0x0fffffffll) {
106 c_size[4] |= 0x10;
107 return mk_appendContextData(c, c_size+4, 4);
109 if (size < 0x07ffffffffll) {
110 c_size[3] |= 0x08;
111 return mk_appendContextData(c, c_size+3, 5);
113 if (size < 0x03ffffffffffll) {
114 c_size[2] |= 0x04;
115 return mk_appendContextData(c, c_size+2, 6);
117 if (size < 0x01ffffffffffffll) {
118 c_size[1] |= 0x02;
119 return mk_appendContextData(c, c_size+1, 7);
121 return mk_appendContextData(c, c_size, 8);
124 int mk_writeSSize(mk_Context *c, int64_t size) {
125 uint64_t u_size = (uint64_t)llabs(size);
126 unsigned size_size = mk_ebmlSizeSize( u_size << 1 ); // We need to shift by one to get the correct size here.
128 switch (size_size)
130 case 1:
131 size += 0x3fll;
132 break;
133 case 2:
134 size += 0x1fffll;
135 break;
136 case 3:
137 size += 0x0fffffll;
138 break;
139 case 4:
140 size += 0x07ffffffll;
141 break;
142 case 5:
143 size += 0x03ffffffffll;
144 break;
145 case 6:
146 size += 0x01ffffffffffll;
147 break;
148 case 7:
149 size += 0x00ffffffffffffll;
150 break;
151 default: // Matroska currently doesn't support any int > 56-bit.
152 return -1;
155 return mk_writeSize(c, size);
158 int mk_flushContextID(mk_Context *c) {
159 unsigned char size_undf[8] = {0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
161 if (c->id == 0)
162 return 0;
164 CHECK(mk_writeID(c->parent, c->id));
165 CHECK(mk_appendContextData(c->parent, &size_undf, 8));
167 c->id = 0;
169 return 0;
172 int mk_flushContextData(mk_Context *c) {
173 mk_Writer *w = c->owner;
175 if (c->d_cur == 0)
176 return 0;
178 if (c->parent)
179 CHECK(mk_appendContextData(c->parent, c->data, c->d_cur));
180 else {
181 if (fwrite(c->data, c->d_cur, 1, w->fp) != 1)
182 return -1;
183 w->f_pos += c->d_cur;
184 if (w->f_pos > w->f_eof)
185 w->f_eof = w->f_pos;
188 c->d_cur = 0;
190 return 0;
193 int mk_closeContext(mk_Context *c, int64_t *off) {
194 if (c->id) {
195 CHECK(mk_writeID(c->parent, c->id));
196 CHECK(mk_writeSize(c->parent, c->d_cur));
199 if (c->parent && off != NULL)
200 *off += c->parent->d_cur;
202 CHECK(mk_flushContextData(c));
204 if (c->next)
205 c->next->prev = c->prev;
206 *(c->prev) = c->next;
207 c->next = c->owner->freelist;
208 c->owner->freelist = c;
210 return 0;
213 void mk_destroyContexts(mk_Writer *w) {
214 mk_Context *cur, *next;
216 for (cur = w->freelist; cur; cur = next) {
217 next = cur->next;
218 free(cur->data);
219 free(cur);
222 for (cur = w->actlist; cur; cur = next) {
223 next = cur->next;
224 free(cur->data);
225 free(cur);
228 w->freelist = w->actlist = w->root = NULL;
231 int mk_writeStr(mk_Context *c, unsigned id, const char *str) {
232 size_t len = strlen(str);
234 CHECK(mk_writeID(c, id));
235 CHECK(mk_writeSize(c, len));
236 CHECK(mk_appendContextData(c, str, len));
237 return 0;
240 int mk_writeBin(mk_Context *c, unsigned id, const void *data, unsigned size) {
241 CHECK(mk_writeID(c, id));
242 CHECK(mk_writeSize(c, size));
243 CHECK(mk_appendContextData(c, data, size));
244 return 0;
247 int mk_writeUInt(mk_Context *c, unsigned id, uint64_t ui) {
248 unsigned char c_ui[8] = { ui >> 56, ui >> 48, ui >> 40, ui >> 32, ui >> 24, ui >> 16, ui >> 8, ui };
249 unsigned i = 0;
251 CHECK(mk_writeID(c, id));
252 while (i < 7 && c_ui[i] == 0)
253 ++i;
254 CHECK(mk_writeSize(c, 8 - i));
255 CHECK(mk_appendContextData(c, c_ui+i, 8 - i));
256 return 0;
259 int mk_writeSInt(mk_Context *c, unsigned id, int64_t si) {
260 unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, si >> 24, si >> 16, si >> 8, si };
261 unsigned i = 0;
263 CHECK(mk_writeID(c, id));
264 if (si < 0)
265 while (i < 7 && c_si[i] == 0xff && c_si[i+1] & 0x80)
266 ++i;
267 else
268 while (i < 7 && c_si[i] == 0 && !(c_si[i+1] & 0x80))
269 ++i;
270 CHECK(mk_writeSize(c, 8 - i));
271 CHECK(mk_appendContextData(c, c_si+i, 8 - i));
272 return 0;
275 int mk_writeFloatRaw(mk_Context *c, float f) {
276 union {
277 float f;
278 unsigned u;
279 } u;
280 unsigned char c_f[4];
282 u.f = f;
283 c_f[0] = u.u >> 24;
284 c_f[1] = u.u >> 16;
285 c_f[2] = u.u >> 8;
286 c_f[3] = u.u;
288 return mk_appendContextData(c, c_f, 4);
291 int mk_writeFloat(mk_Context *c, unsigned id, float f) {
292 CHECK(mk_writeID(c, id));
293 CHECK(mk_writeSize(c, 4));
294 CHECK(mk_writeFloatRaw(c, f));
295 return 0;
298 int mk_writeVoid(mk_Context *c, uint64_t length) {
299 char *c_void = calloc(length, sizeof(char));
301 CHECK(mk_writeID(c, 0xec));
302 CHECK(mk_writeSize(c, length));
303 CHECK(mk_appendContextData(c, c_void, length));
304 free(c_void);
305 return 0;
308 unsigned mk_ebmlSizeSize(uint64_t s) {
309 if (s < 0x7fll)
310 return 1;
311 if (s < 0x3fffll)
312 return 2;
313 if (s < 0x1fffffll)
314 return 3;
315 if (s < 0x0fffffffll)
316 return 4;
317 if (s < 0x07ffffffffll)
318 return 5;
319 if (s < 0x03ffffffffffll)
320 return 6;
321 if (s < 0x01ffffffffffffll)
322 return 7;
323 return 8;
326 unsigned mk_ebmlSIntSize(int64_t si) {
327 unsigned char c_si[8] = { si >> 56, si >> 48, si >> 40, si >> 32, si >> 24, si >> 16, si >> 8, si };
328 unsigned i = 0;
330 if (si < 0)
331 while (i < 7 && c_si[i] == 0xff && c_si[i+1] & 0x80)
332 ++i;
333 else
334 while (i < 7 && c_si[i] == 0 && !(c_si[i+1] & 0x80))
335 ++i;
337 return 8 - i;