Added basic pndvalidator app, though it doesn't actually validate anyone. <- fill...
[pandora-libraries.git] / lib / pnd_tinyxml.cpp
1
2 #include "tinyxml/tinyxml.h"
3 #include "../include/pnd_pxml.h"
4 #include "pnd_tinyxml.h"
5
6 //Easily change the tag names if required (globally in this file):
7 #include "pnd_pxml_names.h"
8
9 extern "C" {
10
11 unsigned char pnd_pxml_load ( const char* pFilename, pnd_pxml_t *app ) {
12   FILE *f;
13   char *b;
14   unsigned int len;
15
16   f = fopen ( pFilename, "r" );
17
18   if ( ! f ) {
19     return ( 0 );
20   }
21
22   fseek ( f, 0, SEEK_END );
23
24   len = ftell ( f );
25
26   fseek ( f, 0, SEEK_SET );
27
28   b = (char*) malloc ( len );
29
30   if ( ! b ) {
31     fclose ( f );
32     return ( 0 );
33   }
34
35   fread ( b, 1, len, f );
36
37   return ( pnd_pxml_parse ( pFilename, b, len, app ) );
38 }
39
40 char *pnd_pxml_get_attribute(TiXmlElement *elem, const char *name)
41 {
42         const char *value = elem->Attribute(name);
43         if (value)
44                 return strdup(value);
45         else
46                 return NULL;
47 }
48
49 unsigned char pnd_pxml_parse_titles(const TiXmlHandle hRoot, pnd_pxml_t *app)
50 {
51         TiXmlElement *pElem;
52         app->titles_alloc_c = 4;  //TODO: adjust this based on how many titles a PXML usually has. Power of 2.
53
54         app->titles = (pnd_localized_string_t *)malloc(sizeof(pnd_localized_string_t) * app->titles_alloc_c);
55         if (!app->titles) return (0); //errno = NOMEM
56
57         //Go through all title tags and load them.
58         for (pElem = hRoot.FirstChild(PND_PXML_ENAME_TITLE).Element(); pElem;
59                 pElem = pElem->NextSiblingElement(PND_PXML_ENAME_TITLE))
60         {
61
62           if ( ! pElem->GetText() ) {
63             continue;
64           }
65
66                 char *text = strdup(pElem->GetText());
67                 if (!text) continue;
68
69                 char *lang = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_TITLELANG);
70                 if (!lang) continue;
71
72                 app->titles_c++;
73                 if (app->titles_c > app->titles_alloc_c) //we don't have enough strings allocated
74                 {
75                         app->titles_alloc_c <<= 1;
76                         app->titles = (pnd_localized_string_t *)realloc((void*)app->titles, app->titles_alloc_c);
77                         if (!app->titles) return (0); //errno = ENOMEM
78                 }
79
80                 pnd_localized_string_t *title = &app->titles[app->titles_c - 1];
81                 title->language = lang;
82                 title->string = text;
83         }
84
85         return (1);
86 }
87
88 unsigned char pnd_pxml_parse_descriptions(const TiXmlHandle hRoot, pnd_pxml_t *app)
89 {
90         TiXmlElement *pElem;
91         app->descriptions_alloc_c = 4; //TODO: adjust this based on how many descriptions a PXML usually has. Power of 2.
92
93         app->descriptions = (pnd_localized_string_t *)malloc(sizeof(pnd_localized_string_t) * app->descriptions_alloc_c);
94         if (!app->descriptions) 
95         {
96                 app->descriptions_alloc_c = 0;
97                 return (0); //errno = NOMEM
98         }
99
100         for (pElem = hRoot.FirstChild(PND_PXML_ENAME_DESCRIPTION).Element(); pElem; 
101                 pElem = pElem->NextSiblingElement(PND_PXML_ENAME_DESCRIPTION))
102         {
103
104           if ( ! pElem->GetText() ) {
105             continue;
106           }
107
108                 char *text = strdup(pElem->GetText());
109                 if (!text) continue;
110
111                 char *lang = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_DESCRLANG);
112                 if (!lang) continue;
113
114                 app->descriptions_c++;
115                 if (app->descriptions_c > app->descriptions_alloc_c) //we don't have enough strings allocated
116                 {
117                         app->descriptions_alloc_c <<= 1;
118                         app->descriptions = (pnd_localized_string_t*)realloc((void*)app->descriptions, app->descriptions_alloc_c);
119                         if (!app->descriptions) return (0); //errno = ENOMEM
120                 }
121
122                 pnd_localized_string_t *description = &app->descriptions[app->descriptions_c - 1];
123                 description->language = lang;
124                 description->string = text;
125         }
126         return (1);
127 }
128
129 unsigned char pnd_pxml_parse ( const char *pFilename, char *buffer, unsigned int length, pnd_pxml_t *app ) {
130         //Load the XML document
131         TiXmlDocument doc;
132         doc.Parse(buffer);
133
134         TiXmlElement *pElem = NULL;
135
136         //Find the root element
137         TiXmlHandle hDoc(&doc);
138         TiXmlHandle hRoot(0);
139
140         pElem = hDoc.FirstChild("PXML").Element();
141         if (!pElem) return (0);
142         hRoot = TiXmlHandle(pElem);
143
144         //Get unique ID first.
145         app->unique_id = pnd_pxml_get_attribute(hRoot.Element(), PND_PXML_ATTRNAME_UID);
146
147         //Everything related to the title:
148         pnd_pxml_parse_titles(hRoot, app);
149
150         //Everything description-related:
151         pnd_pxml_parse_descriptions(hRoot, app);
152
153         //Everything launcher-related in one tag:
154         if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_EXEC).Element()) )
155         {
156                 app->background = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_EXECBG); //if this returns NULL, the struct is filled with NULL. No need to check.
157                 app->standalone = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_EXECSTAL);
158                 app->exec       = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_EXECCMD);
159                 app->startdir   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_EXECWD);
160         }
161
162         //The app icon:
163         if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_ICON).Element()) )
164         {
165                 app->icon       = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_ICONSRC);
166         }
167
168         //The preview pics:
169         if ( (pElem = hRoot.FirstChild(PND_PXML_NODENAME_PREVPICS).Element()) )
170         {
171                 //TODO: Change this if pnd_pxml_t gains the feature of more pics than 2.
172                 if ( (pElem = pElem->FirstChildElement(PND_PXML_ENAME_PREVPIC)) )
173                 {
174                         app->previewpic1 = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_PREVPICSRC);
175
176                         if ( (pElem = pElem->NextSiblingElement(PND_PXML_ENAME_PREVPIC)) )
177                         {
178                                 app->previewpic2 = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_PREVPICSRC);
179                         }
180                 }       
181         } //previewpic
182
183         //The author info:
184         if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_AUTHOR).Element()) )
185         {
186                 app->author_name    = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_AUTHORNAME);
187                 app->author_website = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_AUTHORWWW);
188                 //TODO: Uncomment this if the author gets email support.
189                 //app->author_email = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_AUTHOREMAIL));
190         }
191
192         //The version info:
193         if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_VERSION).Element()) )
194         {
195                 app->version_major   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERMAJOR);
196                 app->version_minor   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERMINOR);
197                 app->version_release = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERREL);
198                 app->version_build   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERBUILD);
199         }
200
201         //The OS version info:
202         if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_OSVERSION).Element()) )
203         {
204                 app->osversion_major   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_OSVERMAJOR);
205                 app->osversion_minor   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_OSVERMINOR);
206                 app->osversion_release = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_OSVERREL);
207                 app->osversion_build   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_OSVERBUILD);
208         }
209
210         int i; //For now, we need to keep track of the index of categories.
211         //Categories:
212         if ( (pElem = hRoot.FirstChildElement(PND_PXML_NODENAME_CATS).Element()) ) //First, enter the "categories" node.
213         {
214                 i = 0;
215
216                 //Goes through all the top-level categories and their sub-categories. i helps limit these to 2.
217                 for (pElem = pElem->FirstChildElement(PND_PXML_ENAME_CAT); pElem && i < 2; 
218                         pElem = pElem->NextSiblingElement(PND_PXML_ENAME_CAT), i++)
219                 {
220                         //TODO: Fix pnd_pxml_t so that there can be more than 2 category 'trees' and more than 2 subcategories. Then this can be removed.
221                         switch (i)
222                         {
223                         case 0: //first category counts as the main cat for now
224                                 app->main_category = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_CATNAME);
225                                 break;
226
227                         case 1: //...second as the alternative
228                                 app->altcategory = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_CATNAME);
229                         }
230
231                         TiXmlElement *pSubCatElem; //the sub-elements for a main category.
232                         int j = 0; //the subcategory index within this category
233
234                         //Goes through all the subcategories within this category. j helps limit these to 2.
235                         for (pSubCatElem = pElem->FirstChildElement(PND_PXML_ENAME_SUBCAT); pSubCatElem && j < 2;
236                                 pSubCatElem = pSubCatElem->NextSiblingElement(PND_PXML_ENAME_SUBCAT), j++)
237                         {
238                                 char *subcat = pnd_pxml_get_attribute(pSubCatElem, PND_PXML_ATTRNAME_SUBCATNAME);
239                                 if (!(subcat)) continue;
240
241                                 //TODO: This is ugly. Fix pnd_pxml_t so that there can be more than 2 category 'trees' and more than 2 subcategories. Then this can be removed.
242                                 switch (j & (i << 1))
243                                 {
244                                 case 0:
245                                         app->subcategory1 = subcat;
246                                         break;
247                                 case 1:
248                                         app->subcategory2 = subcat;
249                                         break;
250                                 case 2:
251                                         app->altsubcategory1 = subcat;
252                                         break;
253                                 case 3:
254                                         app->altsubcategory2 = subcat;
255                                 }
256                         }
257                 }
258         }
259
260         //All file associations:
261         //Step into the associations node
262         if ( (pElem = hRoot.FirstChild(PND_PXML_NODENAME_ASSOCS).Element()) )
263         {
264                 i = 0;
265                 //Go through all associations. i serves as index; since the format only supports 3 associations we need to keep track of the number.
266                 for (pElem = pElem->FirstChildElement(PND_PXML_ENAME_ASSOC); pElem && i < 3; 
267                         pElem = pElem->NextSiblingElement(PND_PXML_ENAME_ASSOC), i++)
268                 {
269                         char *name = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_ASSOCNAME);
270                         char *filetype = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_ASSOCFTYPE);
271                         char *paramter = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_ASSOCARGS);
272
273                         if (!(name && filetype && paramter)) continue;
274
275                         switch(i) //TODO: same problem here: only 3 associations supported
276                         {
277                                 case 0:
278                                 {
279                                         app->associationitem1_name      = name;
280                                         app->associationitem1_filetype  = filetype;
281                                         app->associationitem1_parameter = paramter;
282                                         break;
283                                 }
284                                 case 1:
285                                 {
286                                         app->associationitem2_name      = name;
287                                         app->associationitem2_filetype  = filetype;
288                                         app->associationitem2_parameter = paramter;
289                                         break;
290                                 }
291                                 case 2:
292                                 {
293                                         app->associationitem3_name      = name;
294                                         app->associationitem3_filetype  = filetype;
295                                         app->associationitem3_parameter = paramter;
296                                 }
297                         }
298                 }
299         }
300
301         //Performance related things (aka: Clockspeed XD):
302         pElem = hRoot.FirstChild(PND_PXML_ENAME_CLOCK).Element();
303         if (pElem)
304         {       
305                 app->clockspeed = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_CLOCKFREQ);
306         }
307
308         // Package
309         pElem = hRoot.FirstChild ( PND_PXML_ENAME_PACKAGE ).Element();
310         if ( pElem ) {  
311           app -> package_name = pnd_pxml_get_attribute ( pElem, PND_PXML_ATTRNAME_PACKAGE_NAME );
312           app -> package_release_date = pnd_pxml_get_attribute ( pElem, PND_PXML_ATTRNAME_PACKAGE_DATE );
313         }
314
315         return (1);
316 }
317
318 } // extern C