1 /*
2  * $Id$
3  * Copyright (c) 2011, 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 
29 /**
30  * A tool to generate the libmatroska2 semantic files
31  *  input: specdata.xml
32  *  output: matroska_sem.c / matroska_sem.h
33  */
34 
35 #include "data2lib2_stdafx.h"
36 #include "spec_element.h"
37 
38 typedef struct table_extras
39 {
40     bool_t StartedGlobal;
41     bool_t PassedEBML;
42     bool_t IsLast;
43     bool_t InTags;
44     int CurrLevel;
45 
46 } table_extras;
47 
AddElementSemantic(textwriter * CFile,const SpecElement * elt,bool_t InRecursive)48 static void AddElementSemantic(textwriter *CFile, const SpecElement *elt, bool_t InRecursive)
49 {
50     TextPrintf(CFile, T("    {%d, %d, &MATROSKA_Context%s, "), InRecursive?0:(elt->Mandatory?1:0), elt->Multiple?0:1, elt->Name);
51     if (elt->InWebM && elt->MinVersion==1 && !elt->MaxVersion && elt->InDivX)
52         TextWrite(CFile, T("0"));
53     else {
54         bool_t hasData = 0;
55         if (!elt->MinVersion || elt->MinVersion>1) {
56             if (hasData)
57                 TextWrite(CFile, T("|"));
58             hasData = 1;
59             TextWrite(CFile, T("PROFILE_MATROSKA_V1"));
60         }
61         if (!elt->MinVersion || elt->MinVersion>2 || (elt->MaxVersion && elt->MaxVersion<2)) {
62             if (hasData)
63                 TextWrite(CFile, T("|"));
64             hasData = 1;
65             TextWrite(CFile, T("PROFILE_MATROSKA_V2"));
66         }
67         if (!elt->MinVersion || elt->MinVersion>3 || (elt->MaxVersion && elt->MaxVersion<3)) {
68             if (hasData)
69                 TextWrite(CFile, T("|"));
70             hasData = 1;
71             TextWrite(CFile, T("PROFILE_MATROSKA_V3"));
72         }
73         if (!elt->MinVersion || elt->MinVersion>4 || (elt->MaxVersion && elt->MaxVersion<4)) {
74             if (hasData)
75                 TextWrite(CFile, T("|"));
76             hasData = 1;
77             TextWrite(CFile, T("PROFILE_MATROSKA_V4"));
78         }
79         if (!elt->InDivX) {
80             if (hasData)
81                 TextWrite(CFile, T("|"));
82             hasData = 1;
83             TextWrite(CFile, T("PROFILE_DIVX"));
84         }
85         if (!elt->InWebM) {
86             if (hasData)
87                 TextWrite(CFile, T("|"));
88             hasData = 1;
89             TextWrite(CFile, T("PROFILE_WEBM"));
90         }
91     }
92     if (InRecursive)
93         TextWrite(CFile, T("}, // recursive\n"));
94     else
95         TextWrite(CFile, T("},\n"));
96 }
97 
IsValidElement(const SpecElement * elt)98 static bool_t IsValidElement(const SpecElement *elt)
99 {
100     return elt->InWebM || elt->MinVersion || elt->InDivX;
101 }
102 
OutputElementDefinition(const SpecElement ** pElt,const SpecElement ** EltEnd,textwriter * CFile,table_extras * Extras)103 static void OutputElementDefinition(const SpecElement **pElt, const SpecElement **EltEnd, textwriter *CFile, table_extras *Extras)
104 {
105     const SpecElement *elt = *pElt;
106 
107     if (pElt==EltEnd)
108         return;
109 
110     if (elt->Type==EBML_MASTER)
111     {
112         const SpecElement **sub;
113         for (sub = pElt+1; sub!=EltEnd; ++sub)
114         {
115             if ((*sub)->Level<= elt->Level && (*sub)->Level>=0)
116                 break;
117             if ((*sub)->Level== elt->Level+1)
118                 OutputElementDefinition(sub, EltEnd, CFile, Extras);
119         }
120     }
121 
122     //if (!IsValidElement(elt)) TextWrite(CFile, T("// not supported "));
123 
124     if (elt->Type != EBML_unknown && elt->Name[0])
125     {
126         if (elt->Level==-1 && !Extras->StartedGlobal)
127         {
128             Extras->StartedGlobal = 1;
129         }
130 
131         if (elt->Level>=0 && Extras->StartedGlobal)
132         {
133             Extras->StartedGlobal = 0;
134         }
135 
136         if (elt->Level==0 || (elt->Level==1 && Extras->PassedEBML))
137         {
138             Extras->InTags = 0;
139         }
140         Extras->CurrLevel = elt->Level;
141 
142         if (Extras->PassedEBML)
143         {
144             const tchar_t *s;
145             intptr_t value;
146 
147             if (elt->Type==EBML_MASTER)
148             {
149                 const SpecElement **sub;
150 
151                 // write the semantic
152                 TextPrintf(CFile, T("\nconst ebml_semantic EBML_Semantic%s[] = {\n"), elt->Name);
153                 if (elt->Recursive)
154                     AddElementSemantic(CFile, elt, 1);
155                 for (sub = pElt+1; sub!=EltEnd; ++sub)
156                 {
157                     if ((*sub)->Level<= elt->Level)
158                         break;
159                     if ((*sub)->Level== elt->Level+1)
160                     {
161                         //if (!IsValidElement(*sub)) TextWrite(CFile, T("// "));
162                         AddElementSemantic(CFile, *sub, 0);
163                     }
164                 }
165                 TextWrite(CFile, T("    {0, 0, NULL ,0} // end of the table\n};\n"));
166             }
167 
168             TextPrintf(CFile, T("const ebml_context MATROSKA_Context%s = {0x%X, "), elt->Name, elt->Id);
169             if (elt->Id==0x4DBB)
170                 TextWrite(CFile, T("MATROSKA_SEEKPOINT_CLASS, "));
171             else if (elt->Id==0xAE)
172                 TextWrite(CFile, T("MATROSKA_TRACKENTRY_CLASS, "));
173             else if (elt->Id==0x73A4 || elt->Id==0x3CB923 || elt->Id==0x3EB923)
174                 TextWrite(CFile, T("MATROSKA_SEGMENTUID_CLASS, "));
175             else if (elt->Id==0xA3 || elt->Id==0xA1)
176                 TextWrite(CFile, T("MATROSKA_BLOCK_CLASS, "));
177             else if (elt->Id==0xA0)
178                 TextWrite(CFile, T("MATROSKA_BLOCKGROUP_CLASS, "));
179             else if (elt->Id==0x1F43B675)
180                 TextWrite(CFile, T("MATROSKA_CLUSTER_CLASS, "));
181             else if (elt->Id==0xBB)
182                 TextWrite(CFile, T("MATROSKA_CUEPOINT_CLASS, "));
183             else if (elt->Id==0x465C)
184                 TextWrite(CFile, T("MATROSKA_BIGBINARY_CLASS, "));
185             else if (elt->Id==0x61A7)
186                 TextWrite(CFile, T("MATROSKA_ATTACHMENT_CLASS, "));
187             else switch (elt->Type)
188             {
189             case EBML_MASTER:
190                 TextWrite(CFile, T("EBML_MASTER_CLASS, "));
191                 break;
192             case EBML_INTEGER:
193                 TextWrite(CFile, T("EBML_SINTEGER_CLASS, "));
194                 break;
195             case EBML_UNSIGNED_INTEGER:
196                 if (tcsisame_ascii(elt->Range,T("0-1")))
197                     TextWrite(CFile, T("EBML_BOOLEAN_CLASS, "));
198                 else
199                     TextWrite(CFile, T("EBML_INTEGER_CLASS, "));
200                 break;
201             case EBML_DATE:
202                 TextWrite(CFile, T("EBML_DATE_CLASS, "));
203                 break;
204             case EBML_FLOAT:
205                 TextWrite(CFile, T("EBML_FLOAT_CLASS, "));
206                 break;
207             case EBML_STRING:
208                 TextWrite(CFile, T("EBML_STRING_CLASS, "));
209                 break;
210             case EBML_UNICODE_STRING:
211                 TextWrite(CFile, T("EBML_UNISTRING_CLASS, "));
212                 break;
213             case EBML_BINARY:
214                 TextWrite(CFile, T("EBML_BINARY_CLASS, "));
215                 break;
216             }
217             switch (elt->Type)
218             {
219             case EBML_INTEGER:
220             case EBML_UNSIGNED_INTEGER:
221             case EBML_DATE:
222                 s = elt->DefaultValue;
223                 if (!elt->DefaultValue[0])
224                     TextWrite(CFile, T("0, 0, ")); // no default value
225                 else if (ExprIsInt(&s,&value))
226                     TextPrintf(CFile, T("1, (intptr_t)%d, "), value);
227                 else
228                     TextPrintf(CFile, T("0, 0, "), s); // not supported
229                 break;
230             case EBML_FLOAT:
231                 s = elt->DefaultValue;
232                 if (!elt->DefaultValue[0])
233                     TextWrite(CFile, T("0, 0, ")); // no default value
234                 else if (ExprIsInt(&s,&value))
235                     TextPrintf(CFile, T("1, (intptr_t)%s, "), elt->DefaultValue);
236                 else
237                     TextPrintf(CFile, T("0, 0, "), s); // not supported
238                 break;
239             case EBML_STRING:
240             case EBML_UNICODE_STRING:
241                 if (!elt->DefaultValue[0])
242                     TextWrite(CFile, T("0, 0, ")); // no default value
243                 else
244                     TextPrintf(CFile, T("1, (intptr_t)\"%s\", "), elt->DefaultValue);
245                 break;
246             default:
247                 TextPrintf(CFile, T("0, 0, "), elt->DefaultValue);
248                 break;
249             }
250 
251             TextPrintf(CFile, T("\"%s\", "), elt->Name);
252             if (elt->Type!=EBML_MASTER)
253                 TextWrite(CFile, T("NULL, "));
254             else
255                 TextPrintf(CFile, T("EBML_Semantic%s, "), elt->Name);
256             TextWrite(CFile, T("EBML_SemanticGlobals, "));
257             TextWrite(CFile, T("NULL};\n"));
258         }
259     }
260 }
261 
OutputElementDeclaration(const SpecElement ** pElt,const SpecElement ** EltEnd,textwriter * CFile,table_extras * Extras)262 static void OutputElementDeclaration(const SpecElement **pElt, const SpecElement **EltEnd, textwriter *CFile, table_extras *Extras)
263 {
264     const SpecElement *elt = *pElt;
265 
266     if (pElt==EltEnd)
267         return;
268 
269     if (elt->Type==EBML_MASTER)
270     {
271         const SpecElement **sub;
272         for (sub = pElt+1; sub!=EltEnd; ++sub)
273         {
274             if ((*sub)->Level<= elt->Level && (*sub)->Level>=0)
275                 break;
276             if ((*sub)->Level== elt->Level+1)
277                 OutputElementDeclaration(sub, EltEnd, CFile, Extras);
278         }
279     }
280 
281     if (elt->Type != EBML_unknown && elt->Name[0])
282     {
283         if (elt->Level==-1 && !Extras->StartedGlobal)
284         {
285             Extras->StartedGlobal = 1;
286         }
287 
288         if (elt->Level>=0 && Extras->StartedGlobal)
289         {
290             Extras->StartedGlobal = 0;
291         }
292 
293         if (elt->Level==0 || (elt->Level==1 && Extras->PassedEBML))
294         {
295             Extras->InTags = 0;
296         }
297         Extras->CurrLevel = elt->Level;
298 
299         if (Extras->PassedEBML)
300         {
301             TextPrintf(CFile, T("extern const ebml_context MATROSKA_Context%s;\n"), elt->Name);
302             if (elt->Type==EBML_MASTER)
303                 TextWrite(CFile, T("\n"));
304         }
305     }
306 }
307 
ReadLevel(parser * p,array * Elements)308 static void ReadLevel(parser *p, array *Elements)
309 {
310     tchar_t Element[MAXDATA], String[MAXDATA], Value[MAXLINE];
311 
312     for (;;) {
313         if (ParserElementContent(p,Value,TSIZEOF(Value)) && Value[0])
314         {
315         }
316         else if (ParserIsElementNested(p, Element, TSIZEOF(Element)))
317         {
318             if (tcsisame_ascii(Element,T("element")))
319             {
320                 SpecElement *Dumper = (SpecElement*)NodeCreate(p->Context, SPEC_ELEMENT_CLASS);
321                 ReadSpecElement(Dumper, p);
322                 ArrayAppend(Elements, &Dumper, sizeof(Dumper), 128);
323             }
324             else
325 			{
326 				while (ParserIsAttrib(p, String, TSIZEOF(String)))
327 				{
328 					if (ParserAttribString(p, Value, TSIZEOF(Value))) {
329 						if (tcsisame_ascii(String,T("id")))
330 							tcsreplace(Value,TSIZEOF(Value),T(" "),T("_"));
331 					}
332 				}
333 
334 				if (p->ElementEof)
335 					p->ElementEof = 0;
336 				else
337                     ReadLevel(p, Elements);
338 			}
339         }
340         else
341         {
342             ParserSkipAfter(p,'>');
343             break;
344         }
345     }
346 }
347 
META_START(SpecElement_Class,SPEC_ELEMENT_CLASS)348 META_START(SpecElement_Class,SPEC_ELEMENT_CLASS)
349 META_CLASS(SIZE,sizeof(SpecElement))
350 META_END(NODE_CLASS)
351 
352 
353 static void OutputCHeader(textwriter *CFile, bool_t WithInclude)
354 {
355     TextWrite(CFile, T("/*\n"));
356     TextWrite(CFile, T(" * DO NOT EDIT, GENERATED WITH DATA2LIB2\n"));
357     TextWrite(CFile, T(" *\n"));
358     TextPrintf(CFile, T(" * $Id$\n"));
359     TextWrite(CFile, T(" * Copyright (c) 2008-2011, Matroska (non-profit organisation)\n"));
360     TextWrite(CFile, T(" * All rights reserved.\n"));
361     TextWrite(CFile, T(" *\n"));
362     TextWrite(CFile, T(" * Redistribution and use in source and binary forms, with or without\n"));
363     TextWrite(CFile, T(" * modification, are permitted provided that the following conditions are met:\n"));
364     TextWrite(CFile, T(" *     * Redistributions of source code must retain the above copyright\n"));
365     TextWrite(CFile, T(" *       notice, this list of conditions and the following disclaimer.\n"));
366     TextWrite(CFile, T(" *     * Redistributions in binary form must reproduce the above copyright\n"));
367     TextWrite(CFile, T(" *       notice, this list of conditions and the following disclaimer in the\n"));
368     TextWrite(CFile, T(" *       documentation and/or other materials provided with the distribution.\n"));
369     TextWrite(CFile, T(" *     * Neither the name of the Matroska assocation nor the\n"));
370     TextWrite(CFile, T(" *       names of its contributors may be used to endorse or promote products\n"));
371     TextWrite(CFile, T(" *       derived from this software without specific prior written permission.\n"));
372     TextWrite(CFile, T(" *\n"));
373     TextWrite(CFile, T(" * THIS SOFTWARE IS PROVIDED BY the Matroska association ``AS IS'' AND ANY\n"));
374     TextWrite(CFile, T(" * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"));
375     TextWrite(CFile, T(" * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n"));
376     TextWrite(CFile, T(" * DISCLAIMED. IN NO EVENT SHALL The Matroska Foundation BE LIABLE FOR ANY\n"));
377     TextWrite(CFile, T(" * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n"));
378     TextWrite(CFile, T(" * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n"));
379     TextWrite(CFile, T(" * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n"));
380     TextWrite(CFile, T(" * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"));
381     TextWrite(CFile, T(" * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n"));
382     TextWrite(CFile, T(" * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"));
383     TextWrite(CFile, T(" */\n"));
384     TextWrite(CFile, T("#include \"matroska/matroska.h\"\n"));
385     if (WithInclude) TextWrite(CFile, T("#include \"matroska/matroska_sem.h\"\n"));
386     TextWrite(CFile, T("#include \"matroska/matroska_internal.h\"\n"));
387     TextWrite(CFile, T("\n"));
388 }
389 
390 
main(void)391 int main(void)
392 {
393     parsercontext p;
394     parser parseIn;
395     stream *Input = NULL,*OutputC = NULL,*OutputH = NULL;
396     array Elements;
397     SpecElement **element;
398     //tchar_t Element[MAXLINE], String[MAXLINE], Value[MAXLINE];
399 
400     ParserContext_Init(&p,NULL,NULL,NULL);
401     StdAfx_Init((nodemodule*)&p);
402 
403     Input = StreamOpen(&p,T("specdata.xml"),SFLAG_RDONLY/*|SFLAG_BUFFERED*/);
404     OutputC = StreamOpen(&p,T("matroska_sem.c"),SFLAG_WRONLY|SFLAG_CREATE);
405     OutputH = StreamOpen(&p,T("matroska_sem.h"),SFLAG_WRONLY|SFLAG_CREATE);
406 
407     memset(&parseIn, 0, sizeof(parseIn));
408     ArrayInit(&Elements);
409 
410     if (ParserStreamXML(&parseIn, Input, &p, T("table"), 0)==ERR_NONE)
411     {
412         textwriter CFile;
413         table_extras Extras;
414 
415         ReadLevel(&parseIn, &Elements);
416 
417         memset(&Extras,0,sizeof(Extras));
418         memset(&CFile,0,sizeof(CFile));
419         Extras.CurrLevel = -1;
420 
421         CFile.Stream = OutputC;
422         OutputCHeader(&CFile, 1);
423 
424         for (element=ARRAYBEGIN(Elements,SpecElement*); element!=ARRAYEND(Elements,SpecElement*);++element) {
425             if ((element+1) == ARRAYEND(Elements,SpecElement*))
426                 Extras.IsLast = 1;
427             if ((*element)->Id == 0x18538067)
428             {
429                 Extras.PassedEBML = 1;
430                 OutputElementDefinition(element, ARRAYEND(Elements,SpecElement*), &CFile, &Extras);
431                 break;
432             }
433         }
434 
435         memset(&Extras,0,sizeof(Extras));
436         Extras.CurrLevel = -1;
437 
438         CFile.Stream = OutputH;
439         OutputCHeader(&CFile, 0);
440 
441         TextWrite(&CFile, T("#ifndef MATROSKA_SEMANTIC_H\n"));
442         TextWrite(&CFile, T("#define MATROSKA_SEMANTIC_H\n\n"));
443 
444         for (element=ARRAYBEGIN(Elements,SpecElement*); element!=ARRAYEND(Elements,SpecElement*);++element) {
445             if ((element+1) == ARRAYEND(Elements,SpecElement*))
446                 Extras.IsLast = 1;
447             if ((*element)->Id == 0x18538067)
448             {
449                 Extras.PassedEBML = 1;
450                 OutputElementDeclaration(element, ARRAYEND(Elements,SpecElement*), &CFile, &Extras);
451                 break;
452             }
453         }
454 
455         TextWrite(&CFile, T("#endif // MATROSKA_SEMANTIC_H\n"));
456     }
457 
458     for (element=ARRAYBEGIN(Elements,SpecElement*); element!=ARRAYEND(Elements,SpecElement*);++element)
459         NodeDelete((node*)*element);
460     ArrayClear(&Elements);
461 
462     StreamClose(Input);
463     StreamClose(OutputC);
464     StreamClose(OutputH);
465 
466 	StdAfx_Done((nodemodule*)&p);
467     ParserContext_Done(&p);
468     return 0;
469 }
470 
471