summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorteuf <teuf@f01d2545-417e-4e96-918e-98f8d0dbbcb6>2008-05-25 10:38:09 +0000
committerteuf <teuf@f01d2545-417e-4e96-918e-98f8d0dbbcb6>2008-05-25 10:38:09 +0000
commite36402aead91c157dbefd577cdf463759c2aa045 (patch)
treed24c80f92bfb89240b81a2dcdc97a6520f7016fc
parente6ec5b106163bcd4857d75d2027fcc318b245f50 (diff)
downloadlibgpod-e36402aead91c157dbefd577cdf463759c2aa045.tar.gz
libgpod-e36402aead91c157dbefd577cdf463759c2aa045.tar.xz
libgpod-e36402aead91c157dbefd577cdf463759c2aa045.zip
* configure.ac: check libxml presence
* src/Makefile.am: add new files, remove obsolete ones * src/itdb_plist.h: * src/itdb_plist.c: plist parser, this parses a plist XML file to a GHashTable of GValue *. This parser should be generic, ie it doesn't know it's parsing SysInfoExtended, it only cares about it being a plist file * src/itdb_sysinfo_extended_parser.h: * src/itdb_sysinfo_extended_parser.c: convert the parsed plist data to data structures usable by libgpod * src/itdb_device.h: * src/itdb_device.c: parses SysInfoExtended in addition to SysInfo * src/itdb_sysinfo.c: this hacky parser is obsoleted by the new (much more complete) SysInfoExtended parser, so it's removed * tests/Makefile.am: * tests/test-sysinfo-extended-parsing.c: small test program for the new parser git-svn-id: https://gtkpod.svn.sf.net/svnroot/gtkpod/libgpod/trunk@1980 f01d2545-417e-4e96-918e-98f8d0dbbcb6
-rw-r--r--ChangeLog20
-rw-r--r--configure.ac11
-rw-r--r--src/Makefile.am9
-rw-r--r--src/itdb_device.c30
-rw-r--r--src/itdb_device.h5
-rw-r--r--src/itdb_plist.c326
-rw-r--r--src/itdb_plist.h42
-rw-r--r--src/itdb_sysinfo.c269
-rw-r--r--src/itdb_sysinfo_extended_parser.c486
-rw-r--r--src/itdb_sysinfo_extended_parser.h49
-rw-r--r--tests/Makefile.am3
-rw-r--r--tests/test-sysinfo-extended-parsing.c24
12 files changed, 999 insertions, 275 deletions
diff --git a/ChangeLog b/ChangeLog
index d74482e..71ae1ea 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,25 @@
2008-05-25 Christophe Fergeau <teuf at gnome.org>
+ * configure.ac: check libxml presence
+ * src/Makefile.am: add new files, remove obsolete ones
+ * src/itdb_plist.h:
+ * src/itdb_plist.c: plist parser, this parses a plist XML file to a
+ GHashTable of GValue *. This parser should be generic, ie it
+ doesn't know it's parsing SysInfoExtended, it only cares about it
+ being a plist file
+ * src/itdb_sysinfo_extended_parser.h:
+ * src/itdb_sysinfo_extended_parser.c: convert the parsed plist data
+ to data structures usable by libgpod
+ * src/itdb_device.h:
+ * src/itdb_device.c: parses SysInfoExtended in addition to SysInfo
+ * src/itdb_sysinfo.c: this hacky parser is obsoleted by the new
+ (much more complete) SysInfoExtended parser, so it's removed
+ * tests/Makefile.am:
+ * tests/test-sysinfo-extended-parsing.c: small test program for the
+ new parser
+
+2008-05-25 Christophe Fergeau <teuf at gnome.org>
+
Patch from Ian Stewart <ian.stewart at ozemail.com.au>
* src/itdb_itunesdb.c: add jump letter support (mhod53)
diff --git a/configure.ac b/configure.ac
index 9eee413..e1d7171 100644
--- a/configure.ac
+++ b/configure.ac
@@ -128,6 +128,17 @@ AC_SUBST(TAGLIB_CFLAGS)
AC_SUBST(TAGLIB_LIBS)
AM_CONDITIONAL(HAVE_TAGLIB, test x"$have_taglib" = xyes)
+dnl **************************************************
+dnl * libxml is used to parse the plist files (aka SysInfoExtended)
+dnl **************************************************
+PKG_CHECK_MODULES(LIBXML, libxml-2.0, have_libxml=yes, have_libxml=no)
+if test x"$have_libxml" = xyes; then
+ AH_TEMPLATE([HAVE_LIBXML], [Whether libxml is installed, it's only used in a test program])
+ AC_DEFINE_UNQUOTED(HAVE_LIBXML, 1)
+fi
+AC_SUBST(LIBXML_CFLAGS)
+AC_SUBST(LIBXML_LIBS)
+AM_CONDITIONAL(HAVE_LIBXML, test x"$have_libxml" = xyes)
dnl **************************************************
dnl * GDKPIXBUF is optional
diff --git a/src/Makefile.am b/src/Makefile.am
index 5c606f2..f5b65a0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -12,8 +12,9 @@ libgpod_la_SOURCES = \
itdb_itunesdb.c \
itdb_photoalbum.c \
itdb_playlist.c \
+ itdb_plist.c \
itdb_sha1.c \
- itdb_sysinfo.c \
+ itdb_sysinfo_extended_parser.c \
itdb_track.c \
ithumb-writer.c \
pixmaps.c \
@@ -32,13 +33,15 @@ noinst_HEADERS = \
db-parse-context.h \
itdb_device.h \
itdb_endianness.h \
+ itdb_plist.h \
itdb_private.h \
itdb_sha1.h \
+ itdb_sysinfo_extended_parser.h \
pixmaps.h \
sha1.h
-INCLUDES=$(LIBGPOD_CFLAGS)
-LIBS=$(LIBGPOD_LIBS)
+INCLUDES=$(LIBGPOD_CFLAGS) $(LIBXML_CFLAGS)
+LIBS=$(LIBGPOD_LIBS) $(LIBXML_LIBS)
uninstall-hook:
-rmdir --ignore-fail-on-non-empty $(DESTDIR)$(libgpodincludedir)
diff --git a/src/itdb_device.c b/src/itdb_device.c
index 3b5e7e3..4f3fe50 100644
--- a/src/itdb_device.c
+++ b/src/itdb_device.c
@@ -387,6 +387,8 @@ void itdb_device_free (Itdb_Device *device)
g_free (device->mountpoint);
if (device->sysinfo)
g_hash_table_destroy (device->sysinfo);
+ if (device->sysinfo_extended)
+ itdb_sysinfo_properties_free (device->sysinfo_extended);
g_free (device);
}
}
@@ -428,6 +430,32 @@ G_GNUC_INTERNAL guint64 device_time_time_t_to_mac (Itdb_Device *device, time_t t
else return 0;
}
+static void itdb_device_read_sysinfo_extended (Itdb_Device *device)
+{
+ const gchar *p_sysinfo_ex[] = {"SysInfoExtended", NULL};
+ gchar *dev_path, *sysinfo_ex_path;
+
+ if (device->sysinfo_extended != NULL) {
+ itdb_sysinfo_properties_free (device->sysinfo_extended);
+ device->sysinfo_extended = NULL;
+ }
+
+ dev_path = itdb_get_device_dir (device->mountpoint);
+ if (!dev_path) return;
+
+ sysinfo_ex_path = itdb_resolve_path (dev_path, p_sysinfo_ex);
+ g_free (dev_path);
+ if (!sysinfo_ex_path) return;
+ device->sysinfo_extended = itdb_sysinfo_extended_parse (sysinfo_ex_path);
+ g_free (sysinfo_ex_path);
+
+ if ((device->sysinfo != NULL) && (device->sysinfo_extended != NULL)) {
+ const char *fwid;
+ fwid = itdb_sysinfo_properties_get_firewire_id (device->sysinfo_extended);
+ g_hash_table_insert (device->sysinfo, g_strdup ("FirewireGuid"),
+ g_strdup (fwid));
+ }
+}
/**
* itdb_device_read_sysinfo:
@@ -490,7 +518,7 @@ gboolean itdb_device_read_sysinfo (Itdb_Device *device)
}
g_free (dev_path);
- itdb_device_read_sysinfo_xml (device, NULL);
+ itdb_device_read_sysinfo_extended (device);
/* indicate that sysinfo is identical to what is on the iPod */
device->sysinfo_changed = FALSE;
diff --git a/src/itdb_device.h b/src/itdb_device.h
index c21be94..0f4a8a7 100644
--- a/src/itdb_device.h
+++ b/src/itdb_device.h
@@ -40,6 +40,7 @@
#endif
#include "itdb.h"
+#include "itdb_sysinfo_extended_parser.h"
#include <glib.h>
@@ -85,6 +86,8 @@ struct _Itdb_Device
*/
GHashTable *sysinfo; /* hash with value/key pairs of all entries
* in Device/SysInfo */
+ SysInfoIpodProperties *sysinfo_extended; /* parsed content of
+ * SysInfoExtended, can be NULL */
gboolean sysinfo_changed; /* Has the sysinfo hash been changed by
the user (itdb_set_sysinfo) */
gint timezone_shift; /* difference in seconds between the current
@@ -109,8 +112,6 @@ struct _Itdb_ArtworkFormat
G_GNUC_INTERNAL const Itdb_ArtworkFormat *itdb_device_get_artwork_formats (Itdb_Device *device);
G_GNUC_INTERNAL gint itdb_device_musicdirs_number (Itdb_Device *device);
G_GNUC_INTERNAL void itdb_device_autodetect_endianess (Itdb_Device *device);
-G_GNUC_INTERNAL gboolean itdb_device_read_sysinfo_xml (Itdb_Device *device,
- GError **error);
G_GNUC_INTERNAL guint64 itdb_device_get_firewire_id (Itdb_Device *device);
G_END_DECLS
diff --git a/src/itdb_plist.c b/src/itdb_plist.c
new file mode 100644
index 0000000..bb6d89d
--- /dev/null
+++ b/src/itdb_plist.c
@@ -0,0 +1,326 @@
+/*
+| Copyright (C) 2008 Christophe Fergeau <teuf@gnome.org>
+| Part of the gtkpod project.
+|
+| URL: http://www.gtkpod.org/
+| URL: http://gtkpod.sourceforge.net/
+|
+| The code contained in this file is free software; you can redistribute
+| it and/or modify it under the terms of the GNU Lesser General Public
+| License as published by the Free Software Foundation; either version
+| 2.1 of the License, or (at your option) any later version.
+|
+| This file is distributed in the hope that it will be useful,
+| but WITHOUT ANY WARRANTY; without even the implied warranty of
+| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+| Lesser General Public License for more details.
+|
+| You should have received a copy of the GNU Lesser General Public
+| License along with this code; if not, write to the Free Software
+| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+|
+| iTunes and iPod are trademarks of Apple
+|
+| This product is not supported/written/published by Apple!
+|
+| $Id$
+*/
+
+/* This file implements a generic plist parser. A plist file is an
+ * Apple-specific XML format which is used to serialize (simple) data
+ * structures to disk, see http://en.wikipedia.org/wiki/Property_list
+ *
+ * This parser should handle most plist files, with those limitations :
+ * - no support for <date> tags
+ * - no support for <array> containing "unamed" elements (ie with no
+ * <key> tag)
+ *
+ * The plist file is parsed using libxml, and the parsed result is stored
+ * in GValue. The types are mapped in the following way:
+ * - <string> => G_TYPE_STRING (char*)
+ * - <real> => G_TYPE_DOUBLE (double)
+ * - <integer> => G_TYPE_INT (gint)
+ * - <true/>, <false/> => G_TYPE_BOOLEAN (gboolean)
+ * - <data> => G_TYPE_GSTRING (GString *)
+ * - <array> => G_TYPE_HASH_TABLE (GHashTable *)
+ * - <dict> => G_TYPE_HASH_TABLE (GHashTable *)
+ */
+#include <stdio.h>
+#include <string.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include "itdb_plist.h"
+
+#ifdef DEBUG_SYSINFO_PARSING
+#define DEBUG g_print
+#else
+#define DEBUG(...)
+#endif
+
+static GValue *parse_node (xmlNode *a_node);
+
+static void
+value_free (GValue *val)
+{
+ g_value_unset (val);
+ g_free (val);
+}
+
+static GValue *
+parse_integer(xmlNode *a_node)
+{
+ char *str_val;
+ gint int_val;
+ GValue *value;
+
+ str_val = (char *)xmlNodeGetContent(a_node);
+ int_val = strtol (str_val, NULL, 0);
+ xmlFree (str_val);
+
+ value = g_new0(GValue, 1);
+ g_value_init(value, G_TYPE_INT);
+ g_value_set_int (value, int_val);
+
+ return value;
+}
+
+static GValue *
+parse_string(xmlNode *a_node)
+{
+ char *str_val;
+ GValue *value;
+
+ str_val = (char *)xmlNodeGetContent(a_node);
+
+ value = g_new0(GValue, 1);
+ g_value_init(value, G_TYPE_STRING);
+ g_value_set_string (value, str_val);
+
+ xmlFree (str_val);
+
+ return value;
+}
+
+static GValue *
+parse_real(xmlNode *a_node)
+{
+ char *str_val;
+ gfloat double_val;
+ GValue *value;
+
+ str_val = (char *)xmlNodeGetContent(a_node);
+ double_val = g_ascii_strtod (str_val, NULL);
+ xmlFree (str_val);
+
+ value = g_new0(GValue, 1);
+ g_value_init(value, G_TYPE_DOUBLE);
+ g_value_set_double (value, double_val);
+
+ return value;
+}
+
+static GValue *
+parse_boolean (xmlNode *a_node)
+{
+ gboolean bool_val;
+ GValue *value;
+
+ if (strcmp ((char *)a_node->name, "true") == 0) {
+ bool_val = TRUE;
+ } else if (strcmp ((char *)a_node->name, "false") == 0) {
+ bool_val = FALSE;
+ } else {
+ DEBUG ("unexpected boolean value\n");
+ return NULL;
+ }
+
+ value = g_new0 (GValue, 1);
+ g_value_init (value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (value, bool_val);
+
+ return value;
+}
+
+static GValue *
+parse_data (xmlNode *a_node)
+{
+ char *str_val;
+ guchar *raw_data;
+ gsize len;
+ GString *data_val;
+ GValue *value;
+
+ str_val = (char *)xmlNodeGetContent(a_node);
+ raw_data = g_base64_decode (str_val, &len);
+ xmlFree (str_val);
+ data_val = g_string_new_len ((char *)raw_data, len);
+ g_free (raw_data);
+
+ value = g_new0(GValue, 1);
+ g_value_init(value, G_TYPE_GSTRING);
+ g_value_take_boxed (value, data_val);
+
+ return value;
+}
+
+static xmlNode *
+parse_one_dict_entry (xmlNode *a_node, GHashTable *dict)
+{
+ xmlNode *cur_node = a_node;
+ xmlChar *key_name;
+ GValue *value;
+
+ while ((cur_node != NULL) && (xmlStrcmp(cur_node->name, (xmlChar *)"key") != 0)) {
+ if (!xmlNodeIsText (cur_node)) {
+ DEBUG ("skipping %s\n", cur_node->name);
+ }
+ cur_node = cur_node->next;
+ }
+ if (cur_node == NULL) {
+ return NULL;
+ }
+ key_name = xmlNodeGetContent(cur_node);
+ cur_node = cur_node->next;
+ while ((cur_node != NULL) && xmlNodeIsText(cur_node)) {
+ cur_node = cur_node->next;
+ }
+ if (cur_node == NULL) {
+ DEBUG ("<key> %s with no corresponding value node", key_name);
+ xmlFree (key_name);
+ return NULL;
+ }
+
+ value = parse_node (cur_node);
+ if (value != NULL) {
+ g_hash_table_insert (dict, g_strdup ((char *)key_name), value);
+ }
+ xmlFree (key_name);
+
+ return cur_node->next;
+}
+
+static GValue *
+parse_dict (xmlNode *a_node)
+{
+ xmlNode *cur_node = a_node->children;
+ GValue *value;
+ GHashTable *dict;
+
+ dict = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify)value_free);
+
+ while (cur_node != NULL) {
+ cur_node = parse_one_dict_entry (cur_node, dict);
+ }
+
+ value = g_new0 (GValue, 1);
+ value = g_value_init (value, G_TYPE_HASH_TABLE);
+ g_value_take_boxed (value, dict);
+
+ return value;
+}
+
+typedef GValue *(*ParseCallback) (xmlNode *);
+struct Parser {
+ const char * const type_name;
+ ParseCallback parser;
+};
+
+static struct Parser parsers[] = { {"integer", parse_integer},
+ {"real", parse_real},
+ {"string", parse_string},
+ {"true", parse_boolean},
+ {"false", parse_boolean},
+ {"data", parse_data},
+ {"dict", parse_dict},
+ {"array", parse_dict},
+ {NULL, NULL} };
+
+static GValue *parse_node (xmlNode *a_node)
+{
+ guint i = 0;
+ g_return_val_if_fail (a_node != NULL, FALSE);
+ while (parsers[i].type_name != NULL) {
+ if (xmlStrcmp (a_node->name, (xmlChar *)parsers[i].type_name) == 0) {
+ if (parsers[i].parser != NULL) {
+ return parsers[i].parser (a_node);
+ }
+ }
+ i++;
+ }
+ DEBUG ("no parser for <%s>\n", a_node->name);
+ return NULL;
+}
+
+static GValue *
+itdb_plist_parse (xmlNode * a_node)
+{
+ xmlNode *cur_node;
+ if (a_node == NULL) {
+ DEBUG ("empty file\n");
+ return NULL;
+ }
+ if (xmlStrcmp (a_node->name, (xmlChar *)"plist") != 0) {
+ DEBUG ("not a plist file\n");
+ return NULL;
+ }
+ cur_node = a_node->xmlChildrenNode;
+ while ((cur_node != NULL) && (xmlNodeIsText (cur_node))) {
+ cur_node = cur_node->next;
+ }
+ if (cur_node != NULL) {
+ return parse_node (cur_node);
+ }
+ return NULL;
+}
+
+GValue *
+itdb_plist_parse_from_file (const char *filename)
+{
+ xmlDoc *doc = NULL;
+ xmlNode *root_element = NULL;
+ GValue *parsed_doc;
+
+ doc = xmlReadFile(filename, NULL, 0);
+
+ if (doc == NULL) {
+ printf("error: could not parse file %s\n", filename);
+ return NULL;
+ }
+
+ root_element = xmlDocGetRootElement(doc);
+
+ parsed_doc = itdb_plist_parse (root_element);
+
+ xmlFreeDoc(doc);
+ xmlCleanupParser();
+
+ return parsed_doc;
+}
+
+GValue *
+itdb_plist_parse_from_memory (const char *data, gsize len)
+{
+ xmlDoc *doc = NULL;
+ xmlNode *root_element = NULL;
+ GValue *parsed_doc;
+
+ doc = xmlReadMemory(data, len, "noname.xml", NULL, 0);
+
+ if (doc == NULL) {
+ printf("error: could not parse data from memory\n");
+ return NULL;
+ }
+
+ root_element = xmlDocGetRootElement(doc);
+
+ parsed_doc = itdb_plist_parse (root_element);
+
+ xmlFreeDoc(doc);
+ xmlCleanupParser();
+
+ return parsed_doc;
+}
diff --git a/src/itdb_plist.h b/src/itdb_plist.h
new file mode 100644
index 0000000..50b5618
--- /dev/null
+++ b/src/itdb_plist.h
@@ -0,0 +1,42 @@
+/*
+| Copyright (C) 2008 Christophe Fergeau <teuf@gnome.org>
+| Part of the gtkpod project.
+|
+| URL: http://www.gtkpod.org/
+| URL: http://gtkpod.sourceforge.net/
+|
+| The code contained in this file is free software; you can redistribute
+| it and/or modify it under the terms of the GNU Lesser General Public
+| License as published by the Free Software Foundation; either version
+| 2.1 of the License, or (at your option) any later version.
+|
+| This file is distributed in the hope that it will be useful,
+| but WITHOUT ANY WARRANTY; without even the implied warranty of
+| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+| Lesser General Public License for more details.
+|
+| You should have received a copy of the GNU Lesser General Public
+| License along with this code; if not, write to the Free Software
+| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+|
+| iTunes and iPod are trademarks of Apple
+|
+| This product is not supported/written/published by Apple!
+|
+| $Id$
+*/
+
+#ifndef __ITDB_PLIST_H__
+#define __ITDB_PLIST_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+G_GNUC_INTERNAL GValue *itdb_plist_parse_from_file (const char *filename);
+G_GNUC_INTERNAL GValue *itdb_plist_parse_from_memory (const char *data,
+ gsize len);
+
+G_END_DECLS
+
+#endif
diff --git a/src/itdb_sysinfo.c b/src/itdb_sysinfo.c
deleted file mode 100644
index 45a4057..0000000
--- a/src/itdb_sysinfo.c
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
-| Copyright (C) 2002-2007 Jorg Schuler <jcsjcs at users sourceforge net>
-| Part of the gtkpod project.
-|
-| URL: http://www.gtkpod.org/
-| URL: http://gtkpod.sourceforge.net/
-|
-| Part of this code is based on ipod-device.c from the libipoddevice
-| project (http://64.14.94.162/index.php/Libipoddevice).
-|
-| The code contained in this file is free software; you can redistribute
-| it and/or modify it under the terms of the GNU Lesser General Public
-| License as published by the Free Software Foundation; either version
-| 2.1 of the License, or (at your option) any later version.
-|
-| This file is distributed in the hope that it will be useful,
-| but WITHOUT ANY WARRANTY; without even the implied warranty of
-| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-| Lesser General Public License for more details.
-|
-| You should have received a copy of the GNU Lesser General Public
-| License along with this code; if not, write to the Free Software
-| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-| USA
-|
-| iTunes and iPod are trademarks of Apple
-|
-| This product is not supported/written/published by Apple!
-|
-| $Id$
-*/
-
-#include <string.h>
-
-#include <glib.h>
-
-#include "itdb_device.h"
-
-enum ParsingState {
- LOOKING_FOR_KEY_TAG,
- PARSING_KEY_TAG,
- LOOKING_FOR_FW_DATA,
- PARSING_FW_DATA,
- DONE
-};
-
-struct _SysInfoParseContext {
- enum ParsingState state;
- char *text;
- Itdb_Device *device;
-};
-typedef struct _SysInfoParseContext SysInfoParseContext;
-
-
-static void parse_start_element (GMarkupParseContext *context,
- const gchar *element_name,
- const gchar **attribute_names,
- const gchar **attribute_values,
- gpointer user_data,
- GError **error)
-{
- SysInfoParseContext *sysinfo = (SysInfoParseContext *)user_data;
-
-#ifdef DEBUG
- g_print ("start: %s\n", element_name);
-#endif
- switch (sysinfo->state) {
- case DONE:
- break;
- case LOOKING_FOR_FW_DATA:
- if (strcmp (element_name, "string") == 0) {
- sysinfo->state = PARSING_FW_DATA;
- break;
- }
- /* Fallback to default case if we didn't find the tag we expected */
- case LOOKING_FOR_KEY_TAG:
- default:
- if (strcmp (element_name, "key") == 0) {
- sysinfo->state = PARSING_KEY_TAG;
- } else {
- sysinfo->state = LOOKING_FOR_KEY_TAG;
- }
- break;
- }
-}
-
-static void parse_end_element (GMarkupParseContext *context,
- const gchar *element_name,
- gpointer user_data,
- GError **error)
-{
- SysInfoParseContext *sysinfo = (SysInfoParseContext *)user_data;
-
-#ifdef DEBUG
- g_print ("end: %s\n", element_name);
-#endif
-
- switch (sysinfo->state) {
- case PARSING_KEY_TAG:
- if (sysinfo->text == NULL) {
- sysinfo->state = LOOKING_FOR_KEY_TAG;
- break;
- }
- if (strcmp (sysinfo->text, "FireWireGUID") == 0) {
- sysinfo->state = LOOKING_FOR_FW_DATA;
- } else {
- sysinfo->state = LOOKING_FOR_KEY_TAG;
- }
- g_free (sysinfo->text);
- sysinfo->text = NULL;
- break;
- case PARSING_FW_DATA:
- if (sysinfo->text == NULL) {
- sysinfo->state = LOOKING_FOR_KEY_TAG;
- }
- /* Use FirewireGuid instead of FireWireGUID to be coherent with what
- * the SysInfo file used to contain
- */
- g_hash_table_insert (sysinfo->device->sysinfo,
- g_strdup ("FirewireGuid"),
- sysinfo->text);
- sysinfo->text = NULL;
- sysinfo->state = DONE;
- break;
- case LOOKING_FOR_KEY_TAG:
- case LOOKING_FOR_FW_DATA:
- case DONE:
- break;
-
- }
-}
-
-static void parse_text (GMarkupParseContext *context,
- const gchar *text,
- gsize text_len,
- gpointer user_data,
- GError **error)
-{
-#ifdef DEBUG
- char *str = g_strndup (text, text_len);
- g_print ("text: %s\n", str);
- g_free (str);
-#endif
-
- SysInfoParseContext *sysinfo = (SysInfoParseContext *)user_data;
- switch (sysinfo->state) {
- case DONE:
- break;
- case PARSING_FW_DATA:
- case PARSING_KEY_TAG:
- g_free (sysinfo->text);
- sysinfo->text = g_strndup (text, text_len);
- break;
- default:
- g_free (sysinfo->text);
- sysinfo->text = NULL;
- break;
- }
-}
-
-static void parse_error (GMarkupParseContext *context,
- GError *error,
- gpointer user_data)
-{
- gint line;
- gint row;
- g_markup_parse_context_get_position (context, &line, &row);
- g_warning ("error at line %i row %i", line, row);
-}
-
-static char *
-get_sysinfo_extended_path (Itdb_Device *device)
-{
- const gchar *p_sysinfo[] = {"SysInfoExtended", NULL};
- gchar *dev_path, *sysinfo_path;
-
- dev_path = itdb_get_device_dir (device->mountpoint);
-
- if (!dev_path) {
- return NULL;
- }
-
- sysinfo_path = itdb_resolve_path (dev_path, p_sysinfo);
- g_free (dev_path);
- return sysinfo_path;
-}
-
-static void init_gmarkup_parser (GMarkupParser *parser)
-{
- parser->start_element = parse_start_element;
- parser->end_element = parse_end_element;
- parser->text = parse_text;
- parser->error = parse_error;
-}
-
-
-static gboolean parse_sysinfo_xml (Itdb_Device *device,
- const char *xml, gsize len,
- GError **error)
-{
- GMarkupParseContext *ctx;
- GMarkupParser parser = {0, };
- gboolean success;
-
- SysInfoParseContext sysinfo = {0, };
- sysinfo.state = LOOKING_FOR_KEY_TAG;
- sysinfo.device = device;
-
- init_gmarkup_parser (&parser);
- ctx = g_markup_parse_context_new (&parser, 0, &sysinfo, NULL);
- success = g_markup_parse_context_parse (ctx, xml, len, error);
- if (!success) {
- g_markup_parse_context_free (ctx);
- return FALSE;
- }
- success = g_markup_parse_context_end_parse (ctx, error);
- if (!success) {
- g_markup_parse_context_free (ctx);
- return FALSE;
- }
- g_markup_parse_context_free (ctx);
-
- return TRUE;
-}
-
-/**
- * itdb_device_read_sysinfo_xml:
- * @device: an #Itdb_Device
- * @error: return location for a #GError or NULL
- *
- * Parses a SysInfoExtended file located in iPod_Control/Device on the
- * passed-in @device. This file contains a detailed description of the iPod
- * capabilities/characteristics. In particular, it contains the iPod Firewire
- * ID which is necessary to build the checksum of the iTunesDB.
- * That file is automatically parsed when @device mount point is set
- * if it can be found.
- *
- * If a SysInfoExtended file could be successfully read, TRUE is returned.
- * Otherwise it returns FALSE and sets @error.
- *
- * Return value: TRUE on success, FALSE if an error occurred
- *
- **/
-gboolean
-itdb_device_read_sysinfo_xml (Itdb_Device *device, GError **error)
-{
- gboolean success;
- char *sysinfo_path;
- char *xml_data;
- gsize len;
-
- sysinfo_path = get_sysinfo_extended_path (device);
- if (sysinfo_path == NULL) {
- g_set_error (error, ITDB_FILE_ERROR, ITDB_FILE_ERROR_NOTFOUND,
- "Couldn't find SysInfoExtended file");
- return FALSE;
- }
- success = g_file_get_contents (sysinfo_path, &xml_data, &len, error);
- g_free (sysinfo_path);
-
- if (!success) {
- return FALSE;
- }
-
- success = parse_sysinfo_xml (device, xml_data, len, error);
- g_free (xml_data);
-
- return success;
-}
diff --git a/src/itdb_sysinfo_extended_parser.c b/src/itdb_sysinfo_extended_parser.c
new file mode 100644
index 0000000..84af19d
--- /dev/null
+++ b/src/itdb_sysinfo_extended_parser.c
@@ -0,0 +1,486 @@
+/*
+| Copyright (C) 2008 Christophe Fergeau <teuf@gnome.org>
+| Part of the gtkpod project.
+|
+| URL: http://www.gtkpod.org/
+| URL: http://gtkpod.sourceforge.net/
+|
+| The code contained in this file is free software; you can redistribute
+| it and/or modify it under the terms of the GNU Lesser General Public
+| License as published by the Free Software Foundation; either version
+| 2.1 of the License, or (at your option) any later version.
+|
+| This file is distributed in the hope that it will be useful,
+| but WITHOUT ANY WARRANTY; without even the implied warranty of
+| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+| Lesser General Public License for more details.
+|
+| You should have received a copy of the GNU Lesser General Public
+| License along with this code; if not, write to the Free Software
+| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+|
+| iTunes and iPod are trademarks of Apple
+|
+| This product is not supported/written/published by Apple!
+|
+| $Id$
+*/
+
+/*
+ * The code in this file is used to convert the output of the plist parser
+ * (a GValue *) and to convert it to data structures usable by libgpod.
+ * This is that code which interprets the generic parsed plist data as a
+ * SysInfoExtended file. The SysInfoExtended data is used to fill a
+ * SysInfoIpodProperties structure and several SysInfoImageFormat structs.
+ *
+ * I tried to make the filling of the structures quite generic, if some
+ * field isn't parsed (which is quite possible since I gathered the various
+ * fields names using a few sample files), all is needed to add it is to
+ * add a field to the appropriate structure (SysInfoIpodProperties or
+ * SysInfoImageFormat) and to add that field to the appropriate
+ * _fields_mapping structure. Those _fields_mapping structures are then
+ * used to convert from a GValue to the struct, but they are also used by
+ * the _dump and _free functions, so there's no need to modify them when
+ * you add a new field.
+ *
+ * If DEBUG_PARSING is defined when building that file, the fields that
+ * were found in the SysInfoExtended file but which were not used to build
+ * the data structures defined in that file will be dumped to stdout. It's
+ * normal to get a few unhandled fields, I left out on purpose a few <dict>
+ * because I was too lazy to parse them ;)
+ */
+#include <glib-object.h>
+#include "itdb_sysinfo_extended_parser.h"
+#include "itdb_plist.h"
+
+struct _SysInfoIpodProperties {
+ char *build_id;
+ char *connected_bus;
+ gint max_transfer_speed;
+ gint family_id;
+ char *product_type;
+ char *firewire_guid;
+ char *firewire_version;
+ GList *artwork_formats;
+ GList *photo_formats;
+ GList *chapter_image_formats;
+ gboolean podcasts_supported;
+ char *min_itunes_version;
+ gboolean playlist_folders_supported;
+ char *serial_number;
+ gint updater_family_id;
+ char *visible_build_id;
+ gint oem_id;
+ gint oem_u;
+ gint db_version;
+ char *min_build_id;
+ char *language;
+ gboolean voice_memos_supported;
+ gint update_method;
+ gint max_fw_blocks;
+ gint fw_part_size;
+ gboolean auto_reboot_after_firmware_update;
+ char *volume_format;
+ gboolean forced_disk_mode;
+ gboolean bang_folder;
+ gboolean corrupt_data_partition;
+ gboolean corrupt_firmware_partition;
+ gboolean can_flash_backlight;
+ gboolean can_hibernate;
+ gboolean came_with_cd;
+ gboolean supports_sparse_artwork;
+ gint max_thumb_file_size;
+ gint ram;
+ gint hotplug_state;
+ gint battery_poll_interval;
+ gboolean sort_fields_supported;
+ gboolean vcard_with_jpeg_supported;
+ gint max_file_size_in_gb;
+ gint max_tracks;
+ gint games_platform_id;
+ gint games_platform_version;
+ gint rental_clock_bias;
+};
+
+struct _SysInfoImageFormat {
+ gint format_id;
+ gint display_width;
+ gint render_width;
+ gint render_height;
+ char *pixel_format;
+ gboolean interlaced;
+ gboolean crop;
+ gboolean align_row_bytes;
+ gint rotation;
+ char *back_color;
+ gint color_adjustment;
+ gdouble gamma;
+ gint associated_format;
+};
+typedef struct _SysInfoImageFormat SysInfoImageFormat;
+
+static gint get_int (GHashTable *dict, const char *key)
+{
+ GValue *val;
+
+ val = g_hash_table_lookup (dict, key);
+ if (val == NULL) {
+ return 0;
+ }
+ if (!G_VALUE_HOLDS_INT (val)) {
+ return 0;
+ }
+ return g_value_get_int (val);
+}
+
+static gdouble get_double (GHashTable *dict, const char *key)
+{
+ GValue *val;
+
+ val = g_hash_table_lookup (dict, key);
+ if (val == NULL) {
+ return 0;
+ }
+ if (!G_VALUE_HOLDS_DOUBLE (val)) {
+ return 0;
+ }
+ return g_value_get_double (val);
+}
+
+static gboolean get_boolean (GHashTable *dict, const char *key)
+{
+ GValue *val;
+
+ val = g_hash_table_lookup (dict, key);
+ if (val == NULL) {
+ return FALSE;
+ }
+ if (!G_VALUE_HOLDS_BOOLEAN (val)) {
+ return FALSE;
+ }
+ return g_value_get_boolean (val);
+}
+
+static char *get_string (GHashTable *dict, const char *key)
+{
+ GValue *val;
+
+ val = g_hash_table_lookup (dict, key);
+ if (val == NULL) {
+ return NULL;
+ }
+ if (!G_VALUE_HOLDS_STRING (val)) {
+ return NULL;
+ }
+ return g_value_dup_string (val);
+}
+
+struct _DictFieldMapping {
+ const char* name;
+ GType type;
+ guint offset;
+};
+
+typedef struct _DictFieldMapping DictFieldMapping;
+static const DictFieldMapping sysinfo_ipod_properties_fields_mapping[] = {
+ { "BuildID", G_TYPE_STRING, G_STRUCT_OFFSET (SysInfoIpodProperties, build_id) },
+ { "ConnectedBus", G_TYPE_STRING, G_STRUCT_OFFSET (SysInfoIpodProperties, connected_bus) },
+ { "MaxTransferSpeed", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoIpodProperties, max_transfer_speed) },
+ { "FamilyID", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoIpodProperties, family_id) },
+ { "ProductType", G_TYPE_STRING, G_STRUCT_OFFSET (SysInfoIpodProperties, product_type) },
+ { "FireWireGUID", G_TYPE_STRING, G_STRUCT_OFFSET (SysInfoIpodProperties, firewire_guid) },
+ { "FireWireVersion", G_TYPE_STRING, G_STRUCT_OFFSET (SysInfoIpodProperties, firewire_version) },
+ { "PodcastsSupported", G_TYPE_BOOLEAN, G_STRUCT_OFFSET (SysInfoIpodProperties, podcasts_supported) },
+ { "MinITunesVersion", G_TYPE_STRING, G_STRUCT_OFFSET (SysInfoIpodProperties, min_itunes_version) },
+ { "PlaylistFoldersSupported", G_TYPE_BOOLEAN, G_STRUCT_OFFSET (SysInfoIpodProperties, playlist_folders_supported) },
+ { "SerialNumber", G_TYPE_STRING, G_STRUCT_OFFSET (SysInfoIpodProperties, serial_number) },
+ { "UpdaterFamilyID", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoIpodProperties, updater_family_id) },
+ { "VisibleBuildID", G_TYPE_STRING, G_STRUCT_OFFSET (SysInfoIpodProperties, visible_build_id) },
+ { "OEMID", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoIpodProperties, oem_id) },
+ { "OEMU", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoIpodProperties, oem_u) },
+ { "DBVersion", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoIpodProperties, db_version) },
+ { "MinBuildID", G_TYPE_STRING, G_STRUCT_OFFSET (SysInfoIpodProperties, min_build_id) },
+ { "Language", G_TYPE_STRING, G_STRUCT_OFFSET (SysInfoIpodProperties, language) },
+ { "VoiceMemosSupported", G_TYPE_BOOLEAN, G_STRUCT_OFFSET (SysInfoIpodProperties, voice_memos_supported) },
+ { "UpdateMethod", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoIpodProperties, update_method) },
+ { "MaxFWBlocks", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoIpodProperties, max_fw_blocks) },
+ { "FWPartSize", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoIpodProperties, fw_part_size) },
+ { "AutoRebootAfterFirmwareUpdate", G_TYPE_BOOLEAN, G_STRUCT_OFFSET (SysInfoIpodProperties, auto_reboot_after_firmware_update) },
+ { "VolumeFormat", G_TYPE_STRING, G_STRUCT_OFFSET (SysInfoIpodProperties, volume_format) },
+ { "ForcedDiskMode", G_TYPE_BOOLEAN, G_STRUCT_OFFSET (SysInfoIpodProperties, forced_disk_mode) },
+ { "BangFolder", G_TYPE_BOOLEAN, G_STRUCT_OFFSET (SysInfoIpodProperties, bang_folder) },
+ { "CorruptDataPartition", G_TYPE_BOOLEAN, G_STRUCT_OFFSET (SysInfoIpodProperties, corrupt_data_partition) },
+ { "CorruptFirmwarePartition", G_TYPE_BOOLEAN, G_STRUCT_OFFSET (SysInfoIpodProperties, corrupt_firmware_partition) },
+ { "CanFlashBacklight", G_TYPE_BOOLEAN, G_STRUCT_OFFSET (SysInfoIpodProperties, can_flash_backlight) },
+ { "CanHibernate", G_TYPE_BOOLEAN, G_STRUCT_OFFSET (SysInfoIpodProperties, can_hibernate) },
+ { "CameWithCD", G_TYPE_BOOLEAN, G_STRUCT_OFFSET (SysInfoIpodProperties, came_with_cd) },
+ { "SupportsSparseArtwork", G_TYPE_BOOLEAN, G_STRUCT_OFFSET (SysInfoIpodProperties, supports_sparse_artwork) },
+ { "MaxThumbFileSize", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoIpodProperties, max_thumb_file_size) },
+ { "RAM", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoIpodProperties, ram) },
+ { "HotPlugState", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoIpodProperties, hotplug_state) },
+ { "BatteryPollInterval", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoIpodProperties, battery_poll_interval) },
+ { "SortFieldsSupported", G_TYPE_BOOLEAN, G_STRUCT_OFFSET (SysInfoIpodProperties, sort_fields_supported) },
+ { "vCardWithJPEGSupported", G_TYPE_BOOLEAN, G_STRUCT_OFFSET (SysInfoIpodProperties, vcard_with_jpeg_supported) },
+ { "MaxFileSizeInGB", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoIpodProperties, max_file_size_in_gb) },
+ { "MaxTracks", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoIpodProperties, max_tracks) },
+ { "GamesPlatformID", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoIpodProperties, games_platform_id) },
+ { "GamesPlatformVersion", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoIpodProperties, games_platform_version) },
+ { "RentalClockBias", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoIpodProperties, rental_clock_bias) },
+ { NULL, G_TYPE_NONE, 0 }
+};
+
+static const DictFieldMapping sysinfo_image_format_fields_mapping[] = {
+ { "FormatId", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoImageFormat, format_id) },
+ { "DisplayWidth", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoImageFormat, display_width) },
+ { "RenderWidth", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoImageFormat, render_width) },
+ { "RenderHeight", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoImageFormat, render_height) },
+ { "PixelFormat", G_TYPE_STRING, G_STRUCT_OFFSET (SysInfoImageFormat, pixel_format) },
+ { "Interlaced", G_TYPE_BOOLEAN, G_STRUCT_OFFSET (SysInfoImageFormat, interlaced) },
+ { "Crop", G_TYPE_BOOLEAN, G_STRUCT_OFFSET (SysInfoImageFormat, crop) },
+ { "AlignRowBytes", G_TYPE_BOOLEAN, G_STRUCT_OFFSET (SysInfoImageFormat, align_row_bytes) },
+ { "Rotation", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoImageFormat, rotation) },
+ { "BackColor", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoImageFormat, back_color) },
+ { "ColorAdjustment", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoImageFormat, color_adjustment) },
+ { "GammaAdjustment", G_TYPE_DOUBLE, G_STRUCT_OFFSET (SysInfoImageFormat, gamma) },
+ { "AssociatedFormat", G_TYPE_INT, G_STRUCT_OFFSET (SysInfoImageFormat, associated_format) },
+ { NULL, G_TYPE_NONE, 0 }
+};
+
+#ifdef DEBUG_PARSING
+static void dump_key_name (gpointer key, gpointer val, gpointer data)
+{
+ g_print ("%s ", (char *)key);
+}
+#endif
+
+static void dict_to_struct (GHashTable *dict,
+ const DictFieldMapping *mapping,
+ void *struct_ptr)
+{
+ const DictFieldMapping *it = mapping;
+ g_return_if_fail (it != NULL);
+ while (it->name != NULL) {
+ switch (it->type) {
+ case G_TYPE_INT: {
+ gint *field;
+ field = G_STRUCT_MEMBER_P (struct_ptr, it->offset);
+ *field = get_int (dict, it->name);
+ break;
+ }
+
+ case G_TYPE_BOOLEAN: {
+ gboolean *field;
+ field = G_STRUCT_MEMBER_P (struct_ptr, it->offset);
+ *field = get_boolean (dict, it->name);
+ break;
+ }
+
+ case G_TYPE_STRING: {
+ gchar **field;
+ field = G_STRUCT_MEMBER_P (struct_ptr, it->offset);
+ *field = get_string (dict, it->name);
+ break;
+ }
+
+ case G_TYPE_DOUBLE: {
+ gdouble *field;
+ field = G_STRUCT_MEMBER_P (struct_ptr, it->offset);
+ *field = get_double (dict, it->name);
+ break;
+ }
+ }
+ g_hash_table_remove (dict, it->name);
+ ++it;
+ }
+#ifdef DEBUG_PARSING
+ if (g_hash_table_size (dict) != 0) {
+ g_print ("Unused keys:\n");
+ g_hash_table_foreach (dict, dump_key_name, NULL);
+ g_print ("\n");
+ }
+#endif
+}
+
+static void free_struct (const DictFieldMapping *mapping,
+ void *struct_ptr)
+{
+ const DictFieldMapping *it = mapping;
+ while (it->name != NULL) {
+ if (it->type == G_TYPE_STRING) {
+ gchar **field;
+ field = G_STRUCT_MEMBER_P (struct_ptr, it->offset);
+ g_free (*field);
+ }
+ ++it;
+ }
+ g_free (struct_ptr);
+}
+
+static void free_image_format (SysInfoImageFormat *format)
+{
+ free_struct (sysinfo_image_format_fields_mapping, format);
+}
+
+void itdb_sysinfo_properties_free (SysInfoIpodProperties *props)
+{
+ g_list_foreach (props->artwork_formats, (GFunc)free_image_format, NULL);
+ g_list_free (props->artwork_formats);
+ g_list_foreach (props->photo_formats, (GFunc)free_image_format, NULL);
+ g_list_free (props->photo_formats);
+ g_list_foreach (props->chapter_image_formats, (GFunc)free_image_format, NULL);
+ g_list_free (props->chapter_image_formats);
+ free_struct (sysinfo_ipod_properties_fields_mapping, props);
+}
+
+static void dump_struct (const DictFieldMapping *mapping,
+ void *struct_ptr)
+{
+ const DictFieldMapping *it = mapping;
+ g_return_if_fail (it != NULL);
+ while (it->name != NULL) {
+ switch (it->type) {
+ case G_TYPE_INT: {
+ gint *field;
+ field = G_STRUCT_MEMBER_P (struct_ptr, it->offset);
+ g_print ("%s: %d\n", it->name, *field);
+ break;
+ }
+
+ case G_TYPE_BOOLEAN: {
+ gboolean *field;
+ field = G_STRUCT_MEMBER_P (struct_ptr, it->offset);
+ g_print ("%s: %s\n", it->name, (*field)?"true":"false");
+ break;
+ }
+
+ case G_TYPE_STRING: {
+ gchar **field;
+ field = G_STRUCT_MEMBER_P (struct_ptr, it->offset);
+ g_print ("%s: %s\n", it->name, *field);
+ break;
+ }
+
+ case G_TYPE_DOUBLE: {
+ gdouble *field;
+ field = G_STRUCT_MEMBER_P (struct_ptr, it->offset);
+ g_print ("%s: %f\n", it->name, *field);
+ break;
+ }
+ }
+ ++it;
+ }
+}
+
+static void dump_image_format (SysInfoImageFormat *format)
+{
+ dump_struct (sysinfo_image_format_fields_mapping, format);
+}
+
+void itdb_sysinfo_properties_dump (SysInfoIpodProperties *props)
+{
+ dump_struct (sysinfo_ipod_properties_fields_mapping, props);
+ g_list_foreach (props->artwork_formats, (GFunc)dump_image_format, NULL);
+ g_list_foreach (props->photo_formats, (GFunc)dump_image_format, NULL);
+ g_list_foreach (props->chapter_image_formats, (GFunc)dump_image_format, NULL);
+}
+
+static SysInfoImageFormat *g_value_to_image_format (GValue *value)
+{
+ GHashTable *dict;
+ SysInfoImageFormat *img_spec;
+
+ g_return_val_if_fail (G_VALUE_HOLDS (value, G_TYPE_HASH_TABLE), NULL);
+ dict = g_value_get_boxed (value);
+ g_return_val_if_fail (dict != NULL, NULL);
+
+ img_spec = g_new0 (SysInfoImageFormat, 1);
+ if (img_spec == NULL) {
+ return NULL;
+ }
+
+ dict_to_struct (dict, sysinfo_image_format_fields_mapping, img_spec);
+
+ return img_spec;
+}
+
+static void process_one (gpointer key, gpointer value, gpointer user_data)
+{
+ GList **img_formats = user_data;
+ SysInfoImageFormat *format;
+
+ format = g_value_to_image_format (value);
+ if (format != NULL) {
+ *img_formats = g_list_prepend (*img_formats, format);
+ }
+}
+static GList *parse_one_formats_list (GHashTable *sysinfo_dict,
+ const char *key)
+{
+ GValue *to_parse;
+ GList *formats = NULL;
+
+ to_parse = g_hash_table_lookup (sysinfo_dict, key);
+ if (to_parse == NULL) {
+ return NULL;
+ }
+ if (!G_VALUE_HOLDS (to_parse, G_TYPE_HASH_TABLE)) {
+ return NULL;
+ }
+ g_hash_table_foreach (g_value_get_boxed (to_parse), process_one, &formats);
+ g_hash_table_remove (sysinfo_dict, key);
+ return formats;
+}
+
+static SysInfoIpodProperties *g_value_to_ipod_properties (GValue *value)
+{
+ GHashTable *sysinfo_dict;
+ SysInfoIpodProperties *props;
+
+ g_return_val_if_fail (G_VALUE_HOLDS (value, G_TYPE_HASH_TABLE), NULL);
+ sysinfo_dict = g_value_get_boxed (value);
+
+ props = g_new0 (SysInfoIpodProperties, 1);
+ props->artwork_formats = parse_one_formats_list (sysinfo_dict,
+ "AlbumArt");
+ props->photo_formats = parse_one_formats_list (sysinfo_dict,
+ "ImageSpecifications");
+ props->chapter_image_formats = parse_one_formats_list (sysinfo_dict,
+ "ChapterImageSpecs");
+ dict_to_struct (sysinfo_dict,
+ sysinfo_ipod_properties_fields_mapping,
+ props);
+
+ return props;
+}
+
+SysInfoIpodProperties *itdb_sysinfo_extended_parse (const char *filename)
+{
+ GValue *parsed_doc;
+ SysInfoIpodProperties *props;
+
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ parsed_doc = itdb_plist_parse_from_file (filename);
+ if (parsed_doc == NULL) {
+ return NULL;
+ }
+ props = g_value_to_ipod_properties (parsed_doc);
+ g_value_unset (parsed_doc);
+ g_free (parsed_doc);
+
+ return props;
+}
+
+const char *
+itdb_sysinfo_properties_get_serial_number (const SysInfoIpodProperties *props)
+{
+ g_return_val_if_fail (props != NULL, NULL);
+ return props->serial_number;
+}
+
+const char *
+itdb_sysinfo_properties_get_firewire_id (const SysInfoIpodProperties *props)
+{
+ g_return_val_if_fail (props != NULL, NULL);
+ return props->firewire_guid;
+}
diff --git a/src/itdb_sysinfo_extended_parser.h b/src/itdb_sysinfo_extended_parser.h
new file mode 100644
index 0000000..df067f5
--- /dev/null
+++ b/src/itdb_sysinfo_extended_parser.h
@@ -0,0 +1,49 @@
+/*
+| Copyright (C) 2008 Christophe Fergeau <teuf@gnome.org>
+| Part of the gtkpod project.
+|
+| URL: http://www.gtkpod.org/
+| URL: http://gtkpod.sourceforge.net/
+|
+| The code contained in this file is free software; you can redistribute
+| it and/or modify it under the terms of the GNU Lesser General Public
+| License as published by the Free Software Foundation; either version
+| 2.1 of the License, or (at your option) any later version.
+|
+| This file is distributed in the hope that it will be useful,
+| but WITHOUT ANY WARRANTY; without even the implied warranty of
+| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+| Lesser General Public License for more details.
+|
+| You should have received a copy of the GNU Lesser General Public
+| License along with this code; if not, write to the Free Software
+| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+|
+| iTunes and iPod are trademarks of Apple
+|
+| This product is not supported/written/published by Apple!
+|
+| $Id$
+*/
+#ifndef __ITDB_SYSINFO_EXTENDED_PARSER_H__
+#define __ITDB_SYSINFO_EXTENDED_PARSER_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _SysInfoIpodProperties SysInfoIpodProperties;
+
+void itdb_sysinfo_properties_dump (SysInfoIpodProperties *props);
+SysInfoIpodProperties *itdb_sysinfo_extended_parse (const char *filename);
+void itdb_sysinfo_properties_free (SysInfoIpodProperties *props);
+
+const char *
+itdb_sysinfo_properties_get_serial_number (const SysInfoIpodProperties *props);
+
+const char *
+itdb_sysinfo_properties_get_firewire_id (const SysInfoIpodProperties *props);
+
+G_END_DECLS
+
+#endif
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 17c9cb7..f9e003b 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -37,9 +37,12 @@ test_checksum_SOURCES = test-checksum.c
test_firewire_id_SOURCES = test-fw-id.c
+test_sysinfo_extended_parsing_SOURCES = test-sysinfo-extended-parsing.c
+
get_timezone_SOURCES = get-timezone.c
noinst_PROGRAMS=test-itdb test-ls test-checksum test-firewire-id \
+ test-sysinfo-extended-parsing \
$(TESTTHUMBS) $(TESTTAGLIB) $(TESTMISC)
INCLUDES=$(LIBGPOD_CFLAGS) -I$(top_srcdir)/src -DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\"
diff --git a/tests/test-sysinfo-extended-parsing.c b/tests/test-sysinfo-extended-parsing.c
new file mode 100644
index 0000000..86731d1
--- /dev/null
+++ b/tests/test-sysinfo-extended-parsing.c
@@ -0,0 +1,24 @@
+#include "itdb_sysinfo_extended_parser.h"
+
+#include <glib.h>
+#include <glib-object.h>
+
+int main (int argc, char **argv)
+{
+ SysInfoIpodProperties *props;
+
+ if (argc != 2)
+ return(1);
+
+ g_type_init ();
+ props = itdb_sysinfo_extended_parse (argv[1]);
+ if (props == NULL) {
+ g_print ("Couldn't parse %s\n", argv[1]);
+ }
+ itdb_sysinfo_properties_dump (props);
+ itdb_sysinfo_properties_free (props);
+ props = NULL;
+
+ return 0;
+}
+