Merged in changes from dflemstr (thanks!)
[pandora-libraries.git] / lib / pnd_tinyxml.cpp
index e0f2daa..9fbb25f 100644 (file)
@@ -3,6 +3,9 @@
 #include "../include/pnd_pxml.h"
 #include "pnd_tinyxml.h"
 
+//Easily change the tag names if required (globally in this file):
+#include "pnd_pxml_names.h"
+
 extern "C" {
 
 unsigned char pnd_pxml_load ( const char* pFilename, pnd_pxml_t *app ) {
@@ -34,303 +37,275 @@ unsigned char pnd_pxml_load ( const char* pFilename, pnd_pxml_t *app ) {
   return ( pnd_pxml_parse ( pFilename, b, len, app ) );
 }
 
-unsigned char pnd_pxml_parse ( const char *pFilename, char *buffer, unsigned int length, pnd_pxml_t *app ) {
-
-  //TiXmlDocument doc(pFilename);
-  //if (!doc.LoadFile()) return;
+char *pnd_pxml_get_attribute(TiXmlElement *elem, const char *name)
+{
+       const char *value = elem->Attribute(name);
+       if (value)
+               return strdup(value);
+       else
+               return NULL;
+}
 
-  TiXmlDocument doc;
+unsigned char pnd_pxml_parse_titles(const TiXmlHandle hRoot, pnd_pxml_t *app)
+{
+       TiXmlElement *pElem;
+       app->titles_alloc_c = 4;  //TODO: adjust this based on how many titles a PXML usually has. Power of 2.
 
-  doc.Parse ( buffer );
+       app->titles = (pnd_localized_string_t *)malloc(sizeof(pnd_localized_string_t) * app->titles_alloc_c);
+       if (!app->titles) return (0); //errno = NOMEM
 
-       TiXmlHandle hDoc(&doc);
-       TiXmlElement* pElem;
-       TiXmlHandle hRoot(0);
-
-       pElem=hDoc.FirstChildElement().Element();
-       if (!pElem) return ( 0 );
-       hRoot=TiXmlHandle(pElem);
-
-       pElem = hRoot.FirstChild( "title" ).FirstChildElement("en").Element();
-       if ( pElem )
+       //Go through all title tags and load them.
+       for (pElem = hRoot.FirstChild(PND_PXML_ENAME_TITLE).Element(); pElem;
+               pElem = pElem->NextSiblingElement(PND_PXML_ENAME_TITLE))
        {
-               app->title_en = strdup(pElem->GetText());
-       }
 
-       pElem = hRoot.FirstChild( "title" ).FirstChildElement("de").Element();
-       if ( pElem )
-       {
-               app->title_de = strdup(pElem->GetText());
-       }
+         if ( ! pElem->GetText() ) {
+           continue;
+         }
 
-       pElem = hRoot.FirstChild( "title" ).FirstChildElement("it").Element();
-       if ( pElem )
-       {
-               app->title_it = strdup(pElem->GetText());
-       }
-       
-       pElem = hRoot.FirstChild( "title" ).FirstChildElement("fr").Element();
-       if ( pElem )
-       {
-               app->title_fr = strdup(pElem->GetText());
-       }
+               char *text = strdup(pElem->GetText());
+               if (!text) continue;
 
-       pElem=hRoot.FirstChild("unique_id").Element();
-       if (pElem)
-       {       
-               app->unique_id = strdup(pElem->GetText());
-       }
+               char *lang = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_TITLELANG);
+               if (!lang) continue;
 
-       pElem=hRoot.FirstChild("standalone").Element();
-       if (pElem)
-       {       
-               app->standalone = strdup(pElem->GetText());
-       }
-
-       pElem=hRoot.FirstChild("icon").Element();
-       if (pElem)
-       {       
-#if 0
-               char anotherbuffer [ FILENAME_MAX ];
-               strcpy ( anotherbuffer, pFilename );
-               char *s = strstr ( anotherbuffer, PXML_FILENAME );
-               if ( s ) {
-                 strcpy ( s, strdup(pElem->GetText()));
-                 app->icon = strdup(anotherbuffer);
-               } else if ( ( s = strrchr ( anotherbuffer, '/' ) ) ) {
-                 s += 1;
-                 strcpy ( s, strdup(pElem->GetText()));
-                 app->icon = strdup(anotherbuffer);
+               app->titles_c++;
+               if (app->titles_c > app->titles_alloc_c) //we don't have enough strings allocated
+               {
+                       app->titles_alloc_c <<= 1;
+                       app->titles = (pnd_localized_string_t *)realloc((void*)app->titles, app->titles_alloc_c);
+                       if (!app->titles) return (0); //errno = ENOMEM
                }
-#endif
-               app->icon = strdup ( pElem->GetText() );
-       }
-
-       pElem = hRoot.FirstChild( "description" ).FirstChildElement("en").Element();
-       if ( pElem )
-       {
-               app->description_en = strdup(pElem->GetText());
-       }
-
-       pElem = hRoot.FirstChild( "description" ).FirstChildElement("de").Element();
-       if ( pElem )
-       {
-               app->description_de = strdup(pElem->GetText());
-       }
 
-       pElem = hRoot.FirstChild( "description" ).FirstChildElement("it").Element();
-       if ( pElem )
-       {
-               app->description_it = strdup(pElem->GetText());
-       }
-       
-       pElem = hRoot.FirstChild( "description" ).FirstChildElement("fr").Element();
-       if ( pElem )
-       {
-               app->description_fr = strdup(pElem->GetText());
-       }
-
-       pElem = hRoot.FirstChild( "previewpic" ).FirstChildElement("pic1").Element();
-       if ( pElem )
-       {
-               app->previewpic1 = strdup(pElem->GetText());
+               pnd_localized_string_t *title = &app->titles[app->titles_c - 1];
+               title->language = lang;
+               title->string = text;
        }
 
-       pElem = hRoot.FirstChild( "previewpic" ).FirstChildElement("pic2").Element();
-       if ( pElem )
-       {
-               app->previewpic2 = strdup(pElem->GetText());
-       }
+       return (1);
+}
 
-       pElem = hRoot.FirstChild( "author" ).FirstChildElement("name").Element();
-       if ( pElem )
-       {
-               app->author_name = strdup(pElem->GetText());
-       }
+unsigned char pnd_pxml_parse_descriptions(const TiXmlHandle hRoot, pnd_pxml_t *app)
+{
+       TiXmlElement *pElem;
+       app->descriptions_alloc_c = 4; //TODO: adjust this based on how many descriptions a PXML usually has. Power of 2.
 
-       pElem = hRoot.FirstChild( "author" ).FirstChildElement("website").Element();
-       if ( pElem )
+       app->descriptions = (pnd_localized_string_t *)malloc(sizeof(pnd_localized_string_t) * app->descriptions_alloc_c);
+       if (!app->descriptions) 
        {
-               app->author_website = strdup(pElem->GetText());;
+               app->descriptions_alloc_c = 0;
+               return (0); //errno = NOMEM
        }
 
-       pElem = hRoot.FirstChild( "version" ).FirstChildElement("major").Element();
-       if ( pElem )
+       for (pElem = hRoot.FirstChild(PND_PXML_ENAME_DESCRIPTION).Element(); pElem; 
+               pElem = pElem->NextSiblingElement(PND_PXML_ENAME_DESCRIPTION))
        {
-               app->version_major = strdup(pElem->GetText());
-       }       
 
-       pElem = hRoot.FirstChild( "version" ).FirstChildElement("minor").Element();
-       if ( pElem )
-       {
-               app->version_minor = strdup(pElem->GetText());
-       }       
+         if ( ! pElem->GetText() ) {
+           continue;
+         }
 
-       pElem = hRoot.FirstChild( "version" ).FirstChildElement("release").Element();
-       if ( pElem )
-       {
-               app->version_release = strdup(pElem->GetText());
-       }       
+               char *text = strdup(pElem->GetText());
+               if (!text) continue;
 
-       pElem = hRoot.FirstChild( "version" ).FirstChildElement("build").Element();
-       if ( pElem )
-       {
-               app->version_build = strdup(pElem->GetText());
-       }
+               char *lang = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_DESCRLANG);
+               if (!lang) continue;
 
-       pElem=hRoot.FirstChild("exec").Element();
-       if (pElem)
-       {       
-#if 0
-               char anotherbuffer [ FILENAME_MAX ];
-               strcpy ( anotherbuffer, pFilename );
-               char *s = strstr ( anotherbuffer, PXML_FILENAME );
-               printf ( "exec %s\n", pElem->GetText() );
-               if ( s ) {
-                 strcpy ( s, strdup(pElem->GetText()));
-                 app->exec = strdup(anotherbuffer);
-               } else if ( ( s = strrchr ( anotherbuffer, '/' ) ) ) {
-                 s += 1;
-                 strcpy ( s, strdup(pElem->GetText()));
-                 app->exec = strdup(anotherbuffer);
+               app->descriptions_c++;
+               if (app->descriptions_c > app->descriptions_alloc_c) //we don't have enough strings allocated
+               {
+                       app->descriptions_alloc_c <<= 1;
+                       app->descriptions = (pnd_localized_string_t*)realloc((void*)app->descriptions, app->descriptions_alloc_c);
+                       if (!app->descriptions) return (0); //errno = ENOMEM
                }
-#endif
-               app->exec = strdup ( pElem->GetText() );
-       }       
 
-       pElem = hRoot.FirstChild( "category" ).FirstChildElement("main").Element();
-       if ( pElem )
-       {
-               app->main_category = strdup(pElem->GetText());
+               pnd_localized_string_t *description = &app->descriptions[app->descriptions_c - 1];
+               description->language = lang;
+               description->string = text;
        }
+       return (1);
+}
 
-       pElem = hRoot.FirstChild( "category" ).FirstChildElement("subcategory1").Element();
-       if ( pElem )
-       {
-               app->subcategory1 = strdup(pElem->GetText());
-       }
+unsigned char pnd_pxml_parse ( const char *pFilename, char *buffer, unsigned int length, pnd_pxml_t *app ) {
+       //Load the XML document
+       TiXmlDocument doc;
+       doc.Parse(buffer);
 
-       pElem = hRoot.FirstChild( "category" ).FirstChildElement("subcategory2").Element();
-       if ( pElem )
-       {
-               app->subcategory2 = strdup(pElem->GetText());
-       }
+       TiXmlElement *pElem = NULL;
 
-       pElem = hRoot.FirstChild( "altcategory" ).FirstChildElement("main").Element();
-       if ( pElem )
-       {
-               app->altcategory = strdup(pElem->GetText());
-       }
+       //Find the root element
+       TiXmlHandle hDoc(&doc);
+       TiXmlHandle hRoot(0);
 
-       pElem = hRoot.FirstChild( "altcategory" ).FirstChildElement("subcategory1").Element();
-       if ( pElem )
-       {
-               app->altsubcategory1 = strdup(pElem->GetText());
-       }
+       pElem = hDoc.FirstChild("PXML").Element();
+       if (!pElem) return (0);
+       hRoot = TiXmlHandle(pElem);
 
-       pElem = hRoot.FirstChild( "altcategory" ).FirstChildElement("subcategory2").Element();
-       if ( pElem )
-       {
-               app->altsubcategory2 = strdup(pElem->GetText());
-       }
+       //Get unique ID first.
+       app->unique_id = pnd_pxml_get_attribute(hRoot.Element(), PND_PXML_ATTRNAME_UID);
 
-       pElem = hRoot.FirstChild( "osversion" ).FirstChildElement("major").Element();
-       if ( pElem )
-       {
-               app->osversion_major = strdup(pElem->GetText());
-       }       
+       //Everything related to the title:
+       pnd_pxml_parse_titles(hRoot, app);
 
-       pElem = hRoot.FirstChild( "osversion" ).FirstChildElement("minor").Element();
-       if ( pElem )
-       {
-               app->osversion_minor = strdup(pElem->GetText());
-       }       
-
-       pElem = hRoot.FirstChild( "osversion" ).FirstChildElement("release").Element();
-       if ( pElem )
-       {
-               app->osversion_release = strdup(pElem->GetText());
-       }       
+       //Everything description-related:
+       pnd_pxml_parse_descriptions(hRoot, app);
 
-       pElem = hRoot.FirstChild( "osversion" ).FirstChildElement("build").Element();
-       if ( pElem )
+       //Everything launcher-related in one tag:
+       if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_EXEC).Element()) )
        {
-               app->osversion_build = strdup(pElem->GetText());
+               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.
+               app->standalone = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_EXECSTAL);
+               app->exec       = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_EXECCMD);
+               app->startdir   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_EXECWD);
        }
 
-       pElem = hRoot.FirstChild( "associationitem1" ).FirstChildElement("name").Element();
-       if ( pElem )
+       //The app icon:
+       if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_ICON).Element()) )
        {
-               app->associationitem1_name = strdup(pElem->GetText());
+               app->icon       = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_ICONSRC);
        }
 
-       pElem = hRoot.FirstChild( "associationitem1" ).FirstChildElement("filetype").Element();
-       if ( pElem )
+       //The preview pics:
+       if ( (pElem = hRoot.FirstChild(PND_PXML_NODENAME_PREVPICS).Element()) )
        {
-               app->associationitem1_filetype = strdup(pElem->GetText());
-       }
+               //TODO: Change this if pnd_pxml_t gains the feature of more pics than 2.
+               if ( (pElem = pElem->FirstChildElement(PND_PXML_ENAME_PREVPIC)) )
+               {
+                       app->previewpic1 = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_PREVPICSRC);
 
-       pElem = hRoot.FirstChild( "associationitem1" ).FirstChildElement("parameter").Element();
-       if ( pElem )
-       {
-               app->associationitem1_parameter = strdup(pElem->GetText());
-       }
+                       if ( (pElem = pElem->NextSiblingElement(PND_PXML_ENAME_PREVPIC)) )
+                       {
+                               app->previewpic2 = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_PREVPICSRC);
+                       }
+               }       
+       } //previewpic
 
-       pElem = hRoot.FirstChild( "associationitem2" ).FirstChildElement("name").Element();
-       if ( pElem )
+       //The author info:
+       if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_AUTHOR).Element()) )
        {
-               app->associationitem2_name = strdup(pElem->GetText());
+               app->author_name    = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_AUTHORNAME);
+               app->author_website = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_AUTHORWWW);
+               //TODO: Uncomment this if the author gets email support.
+               //app->author_email = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_AUTHOREMAIL));
        }
 
-       pElem = hRoot.FirstChild( "associationitem2" ).FirstChildElement("filetype").Element();
-       if ( pElem )
+       //The version info:
+       if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_VERSION).Element()) )
        {
-               app->associationitem2_filetype = strdup(pElem->GetText());
+               app->version_major   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERMAJOR);
+               app->version_minor   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERMINOR);
+               app->version_release = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERREL);
+               app->version_build   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_VERBUILD);
        }
 
-       pElem = hRoot.FirstChild( "associationitem2" ).FirstChildElement("parameter").Element();
-       if ( pElem )
+       //The OS version info:
+       if ( (pElem = hRoot.FirstChild(PND_PXML_ENAME_OSVERSION).Element()) )
        {
-               app->associationitem2_parameter = strdup(pElem->GetText());
+               app->osversion_major   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_OSVERMAJOR);
+               app->osversion_minor   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_OSVERMINOR);
+               app->osversion_release = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_OSVERREL);
+               app->osversion_build   = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_OSVERBUILD);
        }
 
-       pElem = hRoot.FirstChild( "associationitem3" ).FirstChildElement("name").Element();
-       if ( pElem )
+       int i; //For now, we need to keep track of the index of categories.
+       //Categories:
+       if ( (pElem = hRoot.FirstChildElement(PND_PXML_NODENAME_CATS).Element()) ) //First, enter the "categories" node.
        {
-               app->associationitem3_name = strdup(pElem->GetText());
-       }
+               i = 0;
 
-       pElem = hRoot.FirstChild( "associationitem3" ).FirstChildElement("filetype").Element();
-       if ( pElem )
-       {
-               app->associationitem3_filetype = strdup(pElem->GetText());
-       }
+               //Goes through all the top-level categories and their sub-categories. i helps limit these to 2.
+               for (pElem = pElem->FirstChildElement(PND_PXML_ENAME_CAT); pElem && i < 2; 
+                       pElem = pElem->NextSiblingElement(PND_PXML_ENAME_CAT), i++)
+               {
+                       //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.
+                       switch (i)
+                       {
+                       case 0: //first category counts as the main cat for now
+                               app->main_category = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_CATNAME);
+                               break;
 
-       pElem = hRoot.FirstChild( "associationitem3" ).FirstChildElement("parameter").Element();
-       if ( pElem )
-       {
-               app->associationitem3_parameter = strdup(pElem->GetText());
-       }
+                       case 1: //...second as the alternative
+                               app->altcategory = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_CATNAME);
+                       }
 
-       pElem=hRoot.FirstChild("clockspeed").Element();
-       if (pElem)
-       {       
-               app->clockspeed = strdup(pElem->GetText());
+                       TiXmlElement *pSubCatElem; //the sub-elements for a main category.
+                       int j = 0; //the subcategory index within this category
+
+                       //Goes through all the subcategories within this category. j helps limit these to 2.
+                       for (pSubCatElem = pElem->FirstChildElement(PND_PXML_ENAME_SUBCAT); pSubCatElem && j < 2;
+                               pSubCatElem = pSubCatElem->NextSiblingElement(PND_PXML_ENAME_SUBCAT), j++)
+                       {
+                               char *subcat = pnd_pxml_get_attribute(pSubCatElem, PND_PXML_ATTRNAME_SUBCATNAME);
+                               if (!(subcat)) continue;
+
+                               //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.
+                               switch (j & (i << 1))
+                               {
+                               case 0:
+                                       app->subcategory1 = subcat;
+                                       break;
+                               case 1:
+                                       app->subcategory2 = subcat;
+                                       break;
+                               case 2:
+                                       app->altsubcategory1 = subcat;
+                                       break;
+                               case 3:
+                                       app->altsubcategory2 = subcat;
+                               }
+                       }
+               }
        }
 
-       pElem=hRoot.FirstChild("background").Element();
-       if (pElem)
-       {       
-               app->background = strdup(pElem->GetText());
+       //All file associations:
+       //Step into the associations node
+       if ( (pElem = hRoot.FirstChild(PND_PXML_NODENAME_ASSOCS).Element()) )
+       {
+               i = 0;
+               //Go through all associations. i serves as index; since the format only supports 3 associations we need to keep track of the number.
+               for (pElem = pElem->FirstChildElement(PND_PXML_ENAME_ASSOC); pElem && i < 3; 
+                       pElem = pElem->NextSiblingElement(PND_PXML_ENAME_ASSOC), i++)
+               {
+                       char *name = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_ASSOCNAME);
+                       char *filetype = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_ASSOCFTYPE);
+                       char *paramter = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_ASSOCARGS);
+
+                       if (!(name && filetype && paramter)) continue;
+
+                       switch(i) //TODO: same problem here: only 3 associations supported
+                       {
+                               case 0:
+                               {
+                                       app->associationitem1_name      = name;
+                                       app->associationitem1_filetype  = filetype;
+                                       app->associationitem1_parameter = paramter;
+                                       break;
+                               }
+                               case 1:
+                               {
+                                       app->associationitem2_name      = name;
+                                       app->associationitem2_filetype  = filetype;
+                                       app->associationitem2_parameter = paramter;
+                                       break;
+                               }
+                               case 2:
+                               {
+                                       app->associationitem3_name      = name;
+                                       app->associationitem3_filetype  = filetype;
+                                       app->associationitem3_parameter = paramter;
+                               }
+                       }
+               }
        }
 
-       pElem=hRoot.FirstChild("startdir").Element();
+       //Performance related things (aka: Clockspeed XD):
+       pElem = hRoot.FirstChild(PND_PXML_ENAME_CLOCK).Element();
        if (pElem)
        {       
-               app->startdir = strdup(pElem->GetText());
+               app->clockspeed = pnd_pxml_get_attribute(pElem, PND_PXML_ATTRNAME_CLOCKFREQ);
        }
 
-       return ( 1 );
+       return (1);
 }
 
 } // extern C