Blob Blame History Raw
/*
|  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 Itdb_ArtworkFormat 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
 * Itdb_ArtworkFormat) 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 ;)
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <glib-object.h>
#include <stdlib.h>
#include <string.h>

#include "itdb_device.h"
#include "itdb_plist.h"
#include "itdb_sysinfo_extended_parser.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;
        int shadowdb_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;
	gboolean sqlite_db;
};

static gint64 get_int64 (GHashTable *dict, const char *key)
{
        GValue *val;

        val = g_hash_table_lookup (dict, key);
        if (val == NULL) {
                return 0;
        }
        if (!G_VALUE_HOLDS_INT64 (val)) {
                return 0;
        }
        return g_value_get_int64 (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_INT64,   G_STRUCT_OFFSET (SysInfoIpodProperties, max_transfer_speed) },
    { "FamilyID",                      G_TYPE_INT64,   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_INT64,   G_STRUCT_OFFSET (SysInfoIpodProperties, updater_family_id) },
    { "VisibleBuildID",                G_TYPE_STRING,  G_STRUCT_OFFSET (SysInfoIpodProperties, visible_build_id) },
    { "OEMID",                         G_TYPE_INT64,   G_STRUCT_OFFSET (SysInfoIpodProperties, oem_id) },
    { "OEMU",                          G_TYPE_INT64,   G_STRUCT_OFFSET (SysInfoIpodProperties, oem_u) },
    { "DBVersion",                     G_TYPE_INT64,   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_INT64,   G_STRUCT_OFFSET (SysInfoIpodProperties, update_method) },
    { "MaxFWBlocks",                   G_TYPE_INT64,   G_STRUCT_OFFSET (SysInfoIpodProperties, max_fw_blocks) },
    { "FWPartSize",                    G_TYPE_INT64,   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_INT64,   G_STRUCT_OFFSET (SysInfoIpodProperties, max_thumb_file_size) },
    { "RAM",                           G_TYPE_INT64,   G_STRUCT_OFFSET (SysInfoIpodProperties, ram) },
    { "HotPlugState",                  G_TYPE_INT64,   G_STRUCT_OFFSET (SysInfoIpodProperties, hotplug_state) },
    { "BatteryPollInterval",           G_TYPE_INT64,   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_INT64,   G_STRUCT_OFFSET (SysInfoIpodProperties, max_file_size_in_gb) },
    { "MaxTracks",                     G_TYPE_INT64,   G_STRUCT_OFFSET (SysInfoIpodProperties, max_tracks) },
    { "GamesPlatformID",               G_TYPE_INT64,   G_STRUCT_OFFSET (SysInfoIpodProperties, games_platform_id) },
    { "GamesPlatformVersion",          G_TYPE_INT64,   G_STRUCT_OFFSET (SysInfoIpodProperties, games_platform_version) },
    { "RentalClockBias",               G_TYPE_INT64,   G_STRUCT_OFFSET (SysInfoIpodProperties, rental_clock_bias) },
    { "SQLiteDB",                      G_TYPE_BOOLEAN, G_STRUCT_OFFSET (SysInfoIpodProperties, sqlite_db) },
    { "ShadowDBVersion",               G_TYPE_INT64,   G_STRUCT_OFFSET (SysInfoIpodProperties, shadowdb_version) },
    { NULL,                            G_TYPE_NONE,    0 }
};

static const DictFieldMapping sysinfo_image_format_fields_mapping[] = {
    { "FormatId",         G_TYPE_INT64,   G_STRUCT_OFFSET (Itdb_ArtworkFormat, format_id) },
    { "DisplayWidth",     G_TYPE_INT64,   G_STRUCT_OFFSET (Itdb_ArtworkFormat, display_width) },
    { "RenderWidth",      G_TYPE_INT64,   G_STRUCT_OFFSET (Itdb_ArtworkFormat, width) },
    { "RenderHeight",     G_TYPE_INT64,   G_STRUCT_OFFSET (Itdb_ArtworkFormat, height) },
/*  PixelFormat needs to be converted to ItdbThumbFormat, this is special-cased
 *  in g_value_to_image_format */
/*  { "PixelFormat",      G_TYPE_STRING,  G_STRUCT_OFFSET (Itdb_ArtworkFormat, format) },*/
    { "Interlaced",       G_TYPE_BOOLEAN, G_STRUCT_OFFSET (Itdb_ArtworkFormat, interlaced) },
    { "Crop",             G_TYPE_BOOLEAN, G_STRUCT_OFFSET (Itdb_ArtworkFormat, crop) },
/* AlignRowBytes is an older version of RowBytesAlignment, it's equivalent
 * to a value of 4 for RowBytesAlignemnt */
/*    { "AlignRowBytes",    G_TYPE_BOOLEAN, G_STRUCT_OFFSET (Itdb_ArtworkFormat, align_row_bytes) },*/
    { "Rotation",         G_TYPE_INT64,   G_STRUCT_OFFSET (Itdb_ArtworkFormat, rotation) },
/* BackColor needs to be converted to a gint, this is special-cased
 * in g_value_to_image_format */
/*    { "BackColor",        G_TYPE_INT64   G_STRUCT_OFFSET (Itdb_ArtworkFormat, back_color) }, */
    { "ColorAdjustment",  G_TYPE_INT64,   G_STRUCT_OFFSET (Itdb_ArtworkFormat, color_adjustment) },
    { "GammaAdjustment",  G_TYPE_DOUBLE,  G_STRUCT_OFFSET (Itdb_ArtworkFormat, gamma) },
    { "AssociatedFormat", G_TYPE_INT64,   G_STRUCT_OFFSET (Itdb_ArtworkFormat, associated_format) },
    { "RowBytesAlignment", G_TYPE_INT64,   G_STRUCT_OFFSET (Itdb_ArtworkFormat, row_bytes_alignment) },
    { 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_INT64: {
                gint *field;
                field = G_STRUCT_MEMBER_P (struct_ptr, it->offset);
                *field = get_int64 (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 (Itdb_ArtworkFormat *format)
{
    free_struct (sysinfo_image_format_fields_mapping, format);
}

void itdb_sysinfo_properties_free (SysInfoIpodProperties *props)
{
    g_return_if_fail (props != NULL);
    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_INT64: {
                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 (Itdb_ArtworkFormat *format)
{
    dump_struct (sysinfo_image_format_fields_mapping, format);
    g_print ("PixelFormat: %d\n", format->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 gboolean
set_pixel_format (Itdb_ArtworkFormat *img_spec, GHashTable *dict)
{
    char *pixel_format;

    pixel_format = get_string (dict, "PixelFormat");
    if (pixel_format == NULL) {
        return FALSE;
    }

    if (strcmp (pixel_format, "32767579" /* 2vuy */) == 0) {
        img_spec->format = THUMB_FORMAT_UYVY_BE;
    } else if (strcmp (pixel_format, "42353635" /* B565 */) == 0) {
        img_spec->format = THUMB_FORMAT_RGB565_BE;
    } else if (strcmp (pixel_format, "4C353635" /* L565 */) == 0) {
        img_spec->format = THUMB_FORMAT_RGB565_LE;
    } else if (strcmp (pixel_format, "79343230" /* y420 */) == 0) {
        img_spec->format = THUMB_FORMAT_I420_LE;
    } else if (strcmp (pixel_format, "4C353535" /* L555 */) == 0) {
	if (g_hash_table_lookup (dict, "PixelOrder") != NULL) {
	    img_spec->format = THUMB_FORMAT_REC_RGB555_LE;
	} else {
	    img_spec->format = THUMB_FORMAT_RGB555_LE;
	}
    } else {
        g_free (pixel_format);
        return FALSE;
    }
    g_hash_table_remove (dict, "PixelFormat");
    g_hash_table_remove (dict, "PixelOrder");
    g_free (pixel_format);
    return TRUE;
}

static void set_back_color (Itdb_ArtworkFormat *img_spec, GHashTable *dict)
{
    char *back_color_str;
    guint back_color;
    gint i;

    memset (img_spec->back_color, 0, sizeof (img_spec->back_color));;
    back_color_str = get_string (dict, "BackColor");
    if (back_color_str == NULL) {
        return;
    }
    back_color = strtoul (back_color_str, NULL, 16);
    for (i = 3; i >= 0; i--) {
        img_spec->back_color[(guchar)i] = back_color & 0xff;
        back_color = back_color >> 8;
    }
    g_hash_table_remove (dict, "BackColor");
    g_free (back_color_str);
}

static Itdb_ArtworkFormat *g_value_to_image_format (GValue *value)
{
    GHashTable *dict;
    Itdb_ArtworkFormat *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 (Itdb_ArtworkFormat, 1);
    if (img_spec == NULL) {
        return NULL;
    }

    if (!set_pixel_format (img_spec, dict)) {
        g_free (img_spec);
        return NULL;
    }
    set_back_color (img_spec, dict);

    dict_to_struct (dict, sysinfo_image_format_fields_mapping, img_spec);

    if (get_boolean (dict, "AlignRowBytes")
            && (img_spec->row_bytes_alignment == 0)) {
        /* at least the nano3g has the AlignRowBytes key with no
         * RowBytesAlignment key.
         */
        img_spec->row_bytes_alignment = 4;
    }

    return img_spec;
}

static GList *parse_one_formats_list (GHashTable *sysinfo_dict, 
                                      const char *key)
{
    GValue *to_parse;
    GList *formats = NULL;
    GArray *array;
    gint i;

    to_parse = g_hash_table_lookup (sysinfo_dict, key);
    if (to_parse == NULL) {
        return NULL;
    }
    if (!G_VALUE_HOLDS (to_parse, G_TYPE_ARRAY)) {
        return NULL;
    }
    array = (GArray*)g_value_get_boxed (to_parse);
    for (i = 0; i < array->len; i++) {
        Itdb_ArtworkFormat *format;
	/* SysInfoExtended on the iPhone has <string> fields in the artwork
	 * format array in addition to the hash we parse
	 */
	if (!G_VALUE_HOLDS (&g_array_index (array, GValue, i), G_TYPE_HASH_TABLE)) {
	    continue;
	}
	format = g_value_to_image_format (&g_array_index (array, GValue, i));
	if (format != NULL) {
		formats = g_list_prepend (formats, format);
	}
    } 
    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");
    if (props->artwork_formats == NULL) {
	props->artwork_formats = parse_one_formats_list (sysinfo_dict,
							 "AlbumArt2");
    }
    props->photo_formats = parse_one_formats_list (sysinfo_dict,
                                                   "ImageSpecifications");
    if (props->photo_formats == NULL) {
	props->photo_formats = parse_one_formats_list (sysinfo_dict,
						       "ImageSpecifications2");
    }
    props->chapter_image_formats = parse_one_formats_list (sysinfo_dict,
                                                           "ChapterImageSpecs");
    if (props->chapter_image_formats == NULL) {
	props->chapter_image_formats = parse_one_formats_list (sysinfo_dict,
							       "ChapterImageSpecs2");
    }
    dict_to_struct (sysinfo_dict,
                    sysinfo_ipod_properties_fields_mapping,
                    props);

    return props;
}

/**
 * itdb_sysinfo_extended_parse:
 * @filename:   name of the SysInfoExtended file to parse
 * @error:      return location for a #GError
 *
 * itdb_sysinfo_extended_parse() parses a SysInfoExtended file into a
 * #SysInfoIpodProperties structure. This structure contains a lot of
 * information about the iPod properties (artwork format supported,
 * podcast capabilities, ...) which can be queried using the
 * appropriate accessors.
 *
 * Returns: a newly allocated #SysInfoIpodProperties which must be
 * freed after use, or NULL if an error occurred during the parsing
 */
SysInfoIpodProperties *itdb_sysinfo_extended_parse (const char *filename,
                                                    GError **error)
{
    GValue *parsed_doc;
    SysInfoIpodProperties *props;

    g_return_val_if_fail (filename != NULL, NULL);

    parsed_doc = itdb_plist_parse_from_file (filename, error);
    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;
}

SysInfoIpodProperties *itdb_sysinfo_extended_parse_from_xml (const char *xml,
							     GError **error)
{
    GValue *parsed_doc;
    SysInfoIpodProperties *props;

    g_return_val_if_fail (xml != NULL, NULL);

    parsed_doc = itdb_plist_parse_from_memory (xml, strlen (xml), error);
    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;
}

/**
 * itdb_sysinfo_properties_get_serial_number:
 * @props: a #SysInfoIpodProperties structure
 *
 * Gets the iPod serial number from @props if it was found while parsing
 * @props. The serial number uniquely identify an ipod and it can be used
 * to determine when it was produced and its model/color, see
 * http://svn.gnome.org/viewvc/podsleuth/trunk/src/PodSleuth/PodSleuth/SerialNumber.cs?view=markup
 * for more details about what the various parts of the serial number
 * correspond to. Please avoid parsing this serial number by yourself and
 * ask for additionnal API in libgpod if you find yourself needing to parse
 * that serial number :)
 *
 * Returns: the iPod serial number, NULL if the serial number wasn't set in
 * @props. The returned string must not be modified nor freed.
 */
const char *
itdb_sysinfo_properties_get_serial_number (const SysInfoIpodProperties *props)
{
    g_return_val_if_fail (props != NULL, NULL);
    return props->serial_number;
}

/**
 * itdb_sysinfo_properties_get_firewire_id:
 * @props: a #SysInfoIpodProperties structure
 *
 * Gets the iPod firewire ID from @props if it was found while parsing
 * @props. Contrary to what its name implies, the firewire ID is also set
 * on USB iPods and is especially important on iPod Classic and Nano Video
 * since this ID (which is unique on each iPod) is needed to generate the
 * checksum that is required to write a valid iPod database on these
 * models.
 *
 * Returns: the iPod firewire ID, NULL if the serial number wasn't set in
 * @props. The returned string must not be modified nor freed.
 */
const char *
itdb_sysinfo_properties_get_firewire_id (const SysInfoIpodProperties *props)
{
    g_return_val_if_fail (props != NULL, NULL);
    return props->firewire_guid;
}

/**
 * itdb_sysinfo_properties_get_cover_art_formats:
 * @props: a #SysInfoIpodProperties structure
 *
 * Returns: a #GList of #Itdb_ArtworkFormat describing the cover art formats
 * supported by the iPod described in @props. The returned list must not be
 * modified nor freed.
 */
const GList *
itdb_sysinfo_properties_get_cover_art_formats (const SysInfoIpodProperties *props)
{
    g_return_val_if_fail (props != NULL, NULL);
    return props->artwork_formats;
}

/**
 * itdb_sysinfo_properties_get_photo_formats:
 * @props: a #SysInfoIpodProperties structure
 *
 * Returns: a #GList of #Itdb_ArtworkFormat describing the photo formats
 * supported by the iPod described in @props. The returned list must not be
 * modified nor freed.
 */
const GList *
itdb_sysinfo_properties_get_photo_formats (const SysInfoIpodProperties *props)
{
    g_return_val_if_fail (props != NULL, NULL);
    return props->photo_formats;
}

/**
 * itdb_sysinfo_properties_get_chapter_image_formats:
 * @props: a #SysInfoIpodProperties structure
 *
 * Returns: a #GList of #Itdb_ArtworkFormat describing the chapter image
 * formats supported by the iPod described in @props. The returned list must
 * not be modified nor freed.
 */
const GList *
itdb_sysinfo_properties_get_chapter_image_formats (const SysInfoIpodProperties *props)
{
    g_return_val_if_fail (props != NULL, NULL);
    return props->chapter_image_formats;
}

/**
 * itdb_sysinfo_properties_supports_sparse_artwork:
 * @props: a #SysInfoIpodProperties structure
 *
 * Sparse artwork is a way to share artwork between different iPod tracks
 * which make things more efficient space-wise. This function can be used
 * to check if the more space-efficient artwork storage can be used.
 *
 * Returns: TRUE if the iPod supports sparse artwork, FALSE if it does not
 * or if @props doesn't contain any information about sparse artwork
 */
gboolean
itdb_sysinfo_properties_supports_sparse_artwork (const SysInfoIpodProperties *props)
{
    g_return_val_if_fail (props != NULL, FALSE);

    return props->supports_sparse_artwork;
}

gboolean
itdb_sysinfo_properties_supports_podcast (const SysInfoIpodProperties *props)
{
    g_return_val_if_fail (props != NULL, FALSE);

    return props->podcasts_supported;
}

const char *
itdb_sysinfo_properties_get_firmware_version (const SysInfoIpodProperties *props)
{
    g_return_val_if_fail (props != NULL, NULL);

    return props->visible_build_id;
}

gboolean
itdb_sysinfo_properties_supports_sqlite (const SysInfoIpodProperties *props)
{
    g_return_val_if_fail (props != NULL, FALSE);

    return props->sqlite_db;
}

gint
itdb_sysinfo_properties_get_family_id (const SysInfoIpodProperties *props)
{
    g_return_val_if_fail (props != NULL, FALSE);
    return props->family_id;
}

gint
itdb_sysinfo_properties_get_db_version (const SysInfoIpodProperties *props)
{
    g_return_val_if_fail (props != NULL, FALSE);
    return props->db_version;
}

gint
itdb_sysinfo_properties_get_shadow_db_version (const SysInfoIpodProperties *props)
{
   g_return_val_if_fail (props != NULL, 0);
   return props->shadowdb_version;
}