Blob Blame History Raw
/*
 *  Copyright (C) 2005-2007 Christophe Fergeau
 *
 * 
 *  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!
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "itdb.h"
#include "itdb_private.h"
#include "itdb_endianness.h"
#include "db-artwork-debug.h"
#include "db-artwork-parser.h"
#include "db-image-parser.h"
#include "db-itunes-parser.h"
#include "db-parse-context.h"
#include <glib/gi18n-lib.h>
#include <glib/gstdio.h>

typedef int (*ParseListItem)(DBParseContext *ctx, GError *error);


static int
parse_mhif (DBParseContext *ctx, GError *error)
{
	MhifHeader *mhif;

	mhif = db_parse_context_get_m_header (ctx, MhifHeader, "mhif");
	if (mhif == NULL) {
		return -1;
	}
	dump_mhif (mhif);
	db_parse_context_set_total_len (ctx, get_gint32 (mhif->total_len, ctx->byte_order));
	return 0;
}

static int
parse_mhia (DBParseContext *ctx, Itdb_PhotoAlbum *photo_album, GError *error)
{
	MhiaHeader *mhia;
	guint32 image_id;

	mhia = db_parse_context_get_m_header (ctx, MhiaHeader, "mhia");
	if (mhia == NULL) {
		return -1;
	}
	dump_mhia (mhia);
	image_id = get_gint32 (mhia->image_id, ctx->byte_order);
	photo_album->members = g_list_append (photo_album->members,
					      GUINT_TO_POINTER(image_id));
	db_parse_context_set_total_len (ctx,
					get_gint32_db (ctx->db, mhia->total_len));
	return 0;
}

static char *
get_utf16_string (void* buffer, gint length, guint byte_order)
{
	char *result;
	gunichar2 *tmp;
	int i;
	/* Byte-swap the utf16 characters if necessary (I'm relying
	 * on gcc to optimize most of this code away on LE platforms)
	 */
	tmp = g_memdup (buffer, length);
	for (i = 0; i < length/2; i++) {
		tmp[i] = get_gint16 (tmp[i], byte_order);
	}
	result = g_utf16_to_utf8 (tmp, length/2, NULL, NULL, NULL);
	g_free (tmp);

	return result;
	
}

struct ParsedMhodString {
        enum MhodArtworkType mhod_type;
        char *mhod_string;
};

static struct ParsedMhodString *
parse_mhod_string (DBParseContext *ctx, GError *error)
{
	struct ParsedMhodString *result;
	ArtworkDB_MhodHeaderString *mhod_string;
	ArtworkDB_MhodHeader *mhod;
	gint len;
	mhod = db_parse_context_get_m_header (ctx, ArtworkDB_MhodHeader, "mhod");
	if (mhod == NULL) {
		return NULL;
	}
	db_parse_context_set_total_len (ctx, get_gint32 (mhod->total_len, ctx->byte_order));

	if (get_gint32 (mhod->total_len, ctx->byte_order) < sizeof (ArtworkDB_MhodHeaderString)){
		return NULL;
	}

	result = g_new0 (struct ParsedMhodString, 1);
	if (result == NULL) {
		return NULL;
	}

	mhod_string = (ArtworkDB_MhodHeaderString*)mhod;
	result->mhod_type = get_gint16 (mhod_string->type, ctx->byte_order);
	len = get_gint32 (mhod_string->string_len, ctx->byte_order);
	switch (mhod_string->encoding) {
	case 2:
		result->mhod_string = get_utf16_string ((gunichar2 *)mhod_string->string,
							len, ctx->byte_order);
		break;
	case 0:
	case 1: 
		result->mhod_string = g_strndup (mhod_string->string, len);
		break;
	default:
		g_warning (_("Unexpected mhod string type: %d\n"),
			   mhod_string->encoding);
		break;
	}
	dump_mhod_string (mhod_string);
	return result;
}

static int
parse_mhod_3 (DBParseContext *ctx,
	      Itdb_Thumb_Ipod_Item *thumb, GError *error)
{
	struct ParsedMhodString *mhod;
	mhod = parse_mhod_string (ctx, error);
	if (mhod == NULL) {
		return -1;
	}
	if (mhod->mhod_type != MHOD_ARTWORK_TYPE_FILE_NAME) {
		g_free (mhod->mhod_string);
		g_free (mhod);
		return -1;
	}

	thumb->filename = mhod->mhod_string;
	g_free (mhod);
	return 0;
}

static int
parse_photo_mhni (DBParseContext *ctx, Itdb_Thumb_Ipod *thumbs, GError *error)
{
	MhniHeader *mhni;
	DBParseContext *mhod_ctx; 
	Itdb_Thumb_Ipod_Item *thumb;

	mhni = db_parse_context_get_m_header (ctx, MhniHeader, "mhni");
	if (mhni == NULL) {
		return -1;
	}
	db_parse_context_set_total_len (ctx, get_gint32 (mhni->total_len, ctx->byte_order));
	dump_mhni (mhni);

	thumb = ipod_image_new_from_mhni (mhni, ctx->db);
	if (thumb == NULL) {
		return 0;
	}
    
        itdb_thumb_ipod_add (thumbs, thumb);

	mhod_ctx = db_parse_context_get_sub_context (ctx, ctx->header_len);
	if (mhod_ctx == NULL) {
	  return -1;
	}
	parse_mhod_3 (mhod_ctx, thumb, error);
	g_free (mhod_ctx);

	return 0;
}

static int
parse_photo_mhod (DBParseContext *ctx, Itdb_Thumb_Ipod *thumbs, GError *error)
{
	ArtworkDB_MhodHeader *mhod;
	DBParseContext *mhni_ctx;
	gint32 type;

	mhod = db_parse_context_get_m_header (ctx, ArtworkDB_MhodHeader, "mhod");
	if (mhod == NULL) {
		return -1;
	}
	db_parse_context_set_total_len (ctx, get_gint32 (mhod->total_len, ctx->byte_order));

	type = get_gint16 (mhod->type, ctx->byte_order);

        dump_mhod (mhod);

	/* if this is a container... */
	if (type == MHOD_ARTWORK_TYPE_THUMBNAIL) {
		mhni_ctx = db_parse_context_get_sub_context (ctx, ctx->header_len);
		if (mhni_ctx == NULL) {
			return -1;
		}
		parse_photo_mhni (mhni_ctx, thumbs, NULL);
		g_free (mhni_ctx);
	}

	return 0;
}

static int
parse_mhii (DBParseContext *ctx, GError *error)
{
	MhiiHeader *mhii;
	DBParseContext *mhod_ctx;
	int num_children;
	off_t cur_offset;
	Itdb_Artwork *artwork;
	Itdb_PhotoDB *photodb;
	guint64 mactime;
	Itdb_Device *device = db_get_device (ctx->db);
        Itdb_Thumb_Ipod *thumbs;
            
	mhii = db_parse_context_get_m_header (ctx, MhiiHeader, "mhii");
	if (mhii == NULL)
	{
	    return -1;
	}
	db_parse_context_set_total_len (ctx, get_gint32 (mhii->total_len, ctx->byte_order));
	dump_mhii (mhii);

	artwork = itdb_artwork_new ();

	artwork->id = get_gint32 (mhii->image_id, ctx->byte_order);
	artwork->unk028 = get_gint32 (mhii->unknown4, ctx->byte_order);
	artwork->rating = get_gint32 (mhii->rating, ctx->byte_order);
	artwork->unk036 = get_gint32 (mhii->unknown6, ctx->byte_order);
	mactime = get_gint32 (mhii->orig_date, ctx->byte_order);
	artwork->creation_date = device_time_mac_to_time_t (device, mactime);
	mactime = get_gint32 (mhii->digitized_date, ctx->byte_order);
	artwork->digitized_date = device_time_mac_to_time_t (device, mactime);
	artwork->artwork_size = get_gint32 (mhii->orig_img_size, ctx->byte_order);
	artwork->dbid = get_gint64 (mhii->song_id, ctx->byte_order);

        thumbs = (Itdb_Thumb_Ipod *)itdb_thumb_ipod_new ();
        artwork->thumbnail = (Itdb_Thumb *)thumbs;
	cur_offset = ctx->header_len;
	mhod_ctx = db_parse_context_get_sub_context (ctx, cur_offset);
	num_children = get_gint32 (mhii->num_children, ctx->byte_order);
	while ((num_children > 0) && (mhod_ctx != NULL))
	{
	    parse_photo_mhod (mhod_ctx, thumbs, NULL);
	    num_children--;
	    cur_offset += mhod_ctx->total_len;
	    g_free (mhod_ctx);
	    mhod_ctx = db_parse_context_get_sub_context (ctx, cur_offset);
	}
        g_free (mhod_ctx);

	switch (ctx->db->db_type)
	{
	case DB_TYPE_PHOTO:
	    photodb = db_get_photodb (ctx->db);
	    g_return_val_if_fail (photodb, -1);
	    photodb->photos = g_list_append (photodb->photos, artwork);
	    break;
	case DB_TYPE_ITUNES:
	    g_return_val_if_fail (ctx->artwork!=NULL, -1);
	    *ctx->artwork = g_list_prepend (*ctx->artwork, artwork);
	    break;
	default:
	    g_return_val_if_reached (-1);
	}

	return 0;
}

static int
parse_mhba (DBParseContext *ctx, GError *error)
{
	MhbaHeader *mhba;
	DBParseContext *mhod_ctx;
	DBParseContext *mhia_ctx;
	Itdb_PhotoAlbum *album;
	Itdb_PhotoDB *photodb;
	int num_children;
	off_t cur_offset;

	mhba = db_parse_context_get_m_header (ctx, MhbaHeader, "mhba");
	if (mhba == NULL) {
		return -1;
	}
	db_parse_context_set_total_len (ctx, get_gint32 (mhba->total_len, ctx->byte_order));

	dump_mhba (mhba);

	album = g_new0 (Itdb_PhotoAlbum, 1);
	album->album_id = get_gint32(mhba->album_id, ctx->byte_order);
	album->unk024 = get_gint32(mhba->unk024, ctx->byte_order);
	album->unk028 = get_gint16(mhba->unk028, ctx->byte_order);
	album->album_type = mhba->album_type;
	album->playmusic = mhba->playmusic;
	album->repeat = mhba->repeat;
	album->random = mhba->random;
	album->show_titles = mhba->show_titles;
	album->transition_direction = mhba->transition_direction;
	album->slide_duration = get_gint32(mhba->slide_duration,
					   ctx->byte_order);
	album->transition_duration = get_gint32(mhba->transition_duration,
						ctx->byte_order);
	album->unk044 = get_gint32(mhba->unk044, ctx->byte_order);
	album->unk048 = get_gint32(mhba->unk048, ctx->byte_order);
	album->song_id = get_gint64(mhba->song_id, ctx->byte_order);
	album->prev_album_id = get_gint32(mhba->prev_album_id,
					  ctx->byte_order);

	cur_offset = ctx->header_len;
	num_children = get_gint32 (mhba->num_mhods, ctx->byte_order);

        mhod_ctx = db_parse_context_get_sub_context (ctx, cur_offset);
        while ((num_children > 0) && (mhod_ctx != NULL)) {
		struct ParsedMhodString *mhod;

		mhod = parse_mhod_string (mhod_ctx, error);
		if (mhod == NULL) {
			break;
		}
		switch (mhod->mhod_type)
		{  /* FIXME: type==1 is album name. type==2 seems to be
		    * the transtition type between photos,
		    * e.g. "Dissolve". Not handled yet. */
		case MHOD_ARTWORK_TYPE_ALBUM_NAME:
			g_free (album->name);
			album->name = mhod->mhod_string;
			g_free (mhod);
			break;
		default:
			g_free (mhod->mhod_string);
			g_free (mhod);
			break;
		}
		cur_offset += mhod_ctx->total_len;
		g_free (mhod_ctx);
		num_children--;
		mhod_ctx = db_parse_context_get_sub_context (ctx, cur_offset);
        }
	g_free (mhod_ctx);

	mhia_ctx = db_parse_context_get_sub_context (ctx, cur_offset);
	num_children = get_gint32 (mhba->num_mhias, ctx->byte_order);
	while ((num_children > 0) && (mhia_ctx != NULL)) {
		parse_mhia (mhia_ctx, album, NULL);
		num_children--;
		cur_offset += mhia_ctx->total_len;
		g_free (mhia_ctx);
		mhia_ctx = db_parse_context_get_sub_context (ctx, cur_offset);
	}
        g_free (mhia_ctx);
	photodb = db_get_photodb (ctx->db);
	g_return_val_if_fail (photodb, -1);
	album->photodb = photodb;
	photodb->photoalbums = g_list_append (photodb->photoalbums,
					      album);
	return 0;
}


static int
parse_mhl (DBParseContext *ctx, GError *error, 
	   const char *id, ParseListItem parse_child)
{
	MhlHeader *mhl;
	int num_children;
	DBParseContext *mhi_ctx;
	off_t cur_offset;

	mhl = db_parse_context_get_m_header (ctx, MhlHeader, id);
	if (mhl == NULL) {
		return -1;
	}

	dump_mhl (mhl, id);

	num_children = get_gint32 (mhl->num_children, ctx->byte_order);
	if (num_children < 0) {
		return -1;
	}

	cur_offset = ctx->header_len;
	mhi_ctx = db_parse_context_get_sub_context (ctx, cur_offset);
	while ((num_children > 0) && (mhi_ctx != NULL)) {
		if (parse_child != NULL) {
			parse_child (mhi_ctx, NULL);
		}
		num_children--;
		cur_offset += mhi_ctx->total_len;
		g_free (mhi_ctx);
		mhi_ctx = db_parse_context_get_sub_context (ctx, cur_offset);
	}
        g_free (mhi_ctx);

	return 0;

}


static int 
parse_mhsd (DBParseContext *ctx, GError **error)
{
	ArtworkDB_MhsdHeader *mhsd;

	mhsd = db_parse_context_get_m_header (ctx, ArtworkDB_MhsdHeader, "mhsd");
	if (mhsd == NULL) {
		return -1;
	}

	db_parse_context_set_total_len (ctx, get_gint32 (mhsd->total_len, ctx->byte_order));
	dump_mhsd (mhsd);
	switch (get_gint16_db (ctx->db, mhsd->index)) {
	case MHSD_IMAGE_LIST: {
		DBParseContext *mhli_context;
		mhli_context = db_parse_context_get_next_child (ctx);
		parse_mhl (mhli_context, NULL, "mhli", parse_mhii);
		g_free (mhli_context);
		break;
	}
	case MHSD_ALBUM_LIST: {
		DBParseContext *mhla_context;
		mhla_context = db_parse_context_get_next_child (ctx);
		parse_mhl (mhla_context, NULL, "mhla", parse_mhba);
		g_free (mhla_context);
		break;
	}
	case MHSD_FILE_LIST: {
		DBParseContext *mhlf_context;
		mhlf_context = db_parse_context_get_next_child (ctx);
		parse_mhl (mhlf_context, NULL, "mhlf", parse_mhif);
		g_free (mhlf_context);
		break;
	}
	default:
		g_warning (_("Unexpected mhsd index: %d\n"), 
			   get_gint16_db (ctx->db, mhsd->index));
		return -1;
		break;
	}

	return 0;
}


/* Compares the two guint64 values being pointed to and returns TRUE if
 * they are equal. It can be passed to g_hash_table_new() as the
 *  key_equal_func parameter, when using pointers to guint64 as keys
 * in a GHashTable.
 */
static gboolean
guint64_equal (gconstpointer v1, gconstpointer v2)
{
    guint64 i1 = *(const guint64*)v1;
    guint64 i2 = *(const guint64*)v2;
    return i1 == i2;
}

/* Converts a pointer to a guint64 to a hash value. It can be passed to
 * g_hash_table_new() as the hash_func parameter, when using pointers
 * to guint64 values as keys in a GHashTable.
 */
static guint
guint64_hash(gconstpointer v)
{
    guint64 i = *(const guint64*)v;
    return i ^ (i >> 32);
}

/* Apple introduced a new way to associate artwork. The former way
 * used the dbid to link each artwork (mhii) back to the track. The
 * new way uses the mhii id to link from each track to the mhii. Above
 * we only handled the former way */
static int
mhfd_associate_itunesdb_artwork (DBParseContext *ctx)
{
    GHashTable *mhii_id_hash;
    GHashTable *song_dbid_hash;
    Itdb_iTunesDB *itdb;
    GList *gl;

    g_return_val_if_fail (ctx && ctx->artwork, -1);
    itdb = db_get_itunesdb (ctx->db);
    g_return_val_if_fail (itdb, -1);

    /* make a hash linking the dbid with the songs for faster
       lookup */
    song_dbid_hash = g_hash_table_new (guint64_hash, guint64_equal);

    for (gl = itdb->tracks; gl != NULL; gl = gl->next) {
	Itdb_Track *song = (Itdb_Track*)gl->data;
	g_hash_table_insert (song_dbid_hash, &song->dbid, song);
    }

    /* make a hash linking the mhii with the artwork for faster
       lookup */
    mhii_id_hash = g_hash_table_new_full (g_direct_hash,
					  g_direct_equal,
					  NULL,
					  (GDestroyNotify)itdb_artwork_free);

    for (gl=*ctx->artwork; gl; gl=gl->next)
    {
	Itdb_Track *track;
	Itdb_Artwork *artwork=gl->data;
	g_return_val_if_fail (artwork, -1);

	g_hash_table_insert (mhii_id_hash, GINT_TO_POINTER (artwork->id), artwork);

	/* add Artwork to track indicated by the dbid for backward
	   compatibility */
	track = g_hash_table_lookup (song_dbid_hash, &artwork->dbid);
	if (track == NULL)
	{
	    gchar *strval = g_strdup_printf("%" G_GINT64_FORMAT, artwork->dbid);
	    g_print (_("Could not find corresponding track (dbid: %s) for artwork entry.\n"), strval);
	    g_free (strval);
	}
	else
	{
	    itdb_artwork_free (track->artwork);
	    track->artwork = itdb_artwork_duplicate (artwork);
	}
    }
    /* Now go through all the tracks and add artwork where an
     * mhii_link is available */
    for (gl=itdb->tracks; gl; gl=gl->next)
    {
	Itdb_Track *track = gl->data;
	g_return_val_if_fail (track, -1);
	if (track->mhii_link)
	{
	    Itdb_Artwork *artwork;
	    artwork = g_hash_table_lookup (mhii_id_hash,
					   GINT_TO_POINTER (track->mhii_link));
	    if (artwork)
	    {
		g_return_val_if_fail (track->artwork, -1);
		if (track->artwork->id != track->mhii_link)
		{
		    itdb_artwork_free (track->artwork);
		    track->artwork = itdb_artwork_duplicate (artwork);
		}
		else
		{
		    /* same artwork -- don't copy again */
		}
	    }
	    else
	    {
		/* The user can't do much here, so let's not worry them with
		 * this warning...
		gchar *strval = g_strdup_printf("%" G_GINT64_FORMAT, track->dbid);
		g_print (_("Could not find artwork entry (mhii id: %u) for track (dbid: %s).\n"), track->mhii_link, strval);
		g_free (strval);
		 */

		/* couldn't find artwork -- make sure track data is in
		   a consistent state. */
		itdb_track_remove_thumbnails (track);
	    }
	}
    }
    g_hash_table_destroy (mhii_id_hash);
    g_hash_table_destroy (song_dbid_hash);
    /* The actual ItdbArtwork data was freed through the GHashTable
       value_destroy_func */
    g_list_free (*ctx->artwork);
    ctx->artwork = NULL;

    return 0;
}
	


/* Database Object */
static int
parse_mhfd (DBParseContext *ctx, GError **error)
{
	MhfdHeader *mhfd;
	DBParseContext *mhsd_context;
	unsigned int cur_pos;
	gint total_len;
	gint32 i;
	GList *artwork_glist = NULL;

	mhfd = db_parse_context_get_m_header (ctx, MhfdHeader, "mhfd");
	if (mhfd == NULL) {
		return -1;
	}

	/* Sanity check */
	total_len = get_gint32_db (ctx->db, mhfd->total_len);
	g_return_val_if_fail (total_len == ctx->total_len, -1);
	dump_mhfd (mhfd);
	cur_pos = ctx->header_len;

	if (ctx->db->db_type == DB_TYPE_ITUNES)
	{   /* we need to collect all artwork in this GList for
	       iTunesDB (see below) */
	    ctx->artwork = &artwork_glist;
	}

	for (i=0; i<mhfd->num_children; ++i)
	{
	    /* so far all mhfd we know have 3 children, but better be
	       safe than sorry */
	    mhsd_context = db_parse_context_get_sub_context (ctx, cur_pos);
	    if (mhsd_context == NULL) {
		return -1;
	    }
	    parse_mhsd (mhsd_context, NULL);
	    cur_pos += mhsd_context->total_len;
	    g_free (mhsd_context);
	}

	if (ctx->db->db_type == DB_TYPE_ITUNES)
	{
	    return mhfd_associate_itunesdb_artwork (ctx);
	}

	return 0;
}



G_GNUC_INTERNAL char *
ipod_db_get_artwork_db_path (const char *mount_point)
{
        gchar *filename=NULL;

	/* fail silently if no mount point given */
	if (!mount_point)  return NULL;

	filename = itdb_get_artworkdb_path (mount_point);

	/* itdb_resolve_path() only returns existing paths */
	if (!filename)
	{
	    gchar *artwork_dir;

	    artwork_dir = itdb_get_artwork_dir (mount_point);
	    if (!artwork_dir)
	    {
		/* attempt to create Artwork dir */
		gchar *control_dir = itdb_get_control_dir (mount_point);
		if (control_dir)
		{
		    gchar *dir = g_build_filename (control_dir, "Artwork", NULL);
		    g_mkdir (dir, 0777);
		    g_free (control_dir);
		    g_free (dir);
		    artwork_dir = itdb_get_artwork_dir (mount_point);
		}
	    }
	    if (artwork_dir)
	    {
		filename = g_build_filename (artwork_dir,
					     "ArtworkDB", NULL);
		g_free (artwork_dir);
	    }
	}

	return filename;
}

int
ipod_parse_artwork_db (Itdb_iTunesDB *itdb)
{
	DBParseContext *ctx;
	char *filename;
	Itdb_DB db;

	db.db.itdb = itdb;
	db.db_type = DB_TYPE_ITUNES;

	g_return_val_if_fail (itdb, -1);

	if (!itdb_device_supports_artwork (itdb->device)) {
		return -1;
	}
	ctx = NULL;
	filename = ipod_db_get_artwork_db_path (itdb_get_mountpoint (itdb));
	if (filename == NULL) {
		goto error;
	}
	if (!g_file_test (filename, G_FILE_TEST_EXISTS))
	{
		goto error;
	}

	ctx = db_parse_context_new_from_file (filename, &db);
	g_free (filename);

	if (ctx == NULL) {
		goto error;
	}	

	parse_mhfd (ctx, NULL);
	db_parse_context_destroy (ctx);
	return 0;

 error:
	return -1;
}


G_GNUC_INTERNAL char *
ipod_db_get_photos_db_path (const char *mount_point)
{
        gchar *filename=NULL;

	/* fail silently if no mount point given */
	if (!mount_point)  return NULL;

	filename = itdb_get_photodb_path (mount_point);

	/* itdb_resolve_path() only returns existing paths */
	if (!filename)
	{
	    gchar *photos_dir;

	    photos_dir = itdb_get_photos_dir (mount_point);
	    if (!photos_dir)
	    {
		/* attempt to create Photos dir */
		gchar *dir = g_build_filename (mount_point, "Photos", NULL);
		g_mkdir (dir, 0777);
		g_free (dir);
		photos_dir = itdb_get_photos_dir (mount_point);
	    }
	    if (photos_dir)
	    {
		filename = g_build_filename (photos_dir,
					     "Photo Database", NULL);
		g_free (photos_dir);
	    }
	}

	return filename;
}


int
ipod_parse_photo_db (Itdb_PhotoDB *photodb)
{
	DBParseContext *ctx;
	char *filename;
	Itdb_DB db;

	GList *gl;
	GHashTable *hash;

	db.db.photodb = photodb;
	db.db_type = DB_TYPE_PHOTO;


	filename = itdb_get_photodb_path (
	    itdb_photodb_get_mountpoint (photodb));
	if (filename == NULL) {
		return -1;
	}       

	ctx = db_parse_context_new_from_file (filename, &db );
	g_free (filename);
	if (ctx == NULL) {
		return  -1;
	}
	parse_mhfd (ctx, NULL);
	db_parse_context_destroy (ctx);

	/* Now we need to replace references to artwork_ids in the
	 * photo albums with references to the actual artwork
	 * structure. Since we cannot guarantee that the list with the
	 * photos is read before the album list, we cannot safely do
	 * this at the time of reading the ids. */

	/* Create a hash for faster lookup */
	hash = g_hash_table_new (g_int_hash, g_int_equal);
	for (gl=photodb->photos; gl; gl=gl->next)
	{
	    Itdb_Artwork *photo = gl->data;
	    g_return_val_if_fail (photo, -1);
	    g_hash_table_insert (hash, &photo->id, photo);
/*	    printf ("id: %d, photo: %p\n", photo->id, photo);*/
	}
	for (gl=photodb->photoalbums; gl; gl=gl->next)
	{
	    GList *glp;
	    Itdb_PhotoAlbum *album = gl->data;
	    g_return_val_if_fail (album, -1);
	    for (glp=album->members; glp; glp=glp->next)
	    {
		guint image_id = GPOINTER_TO_UINT (glp->data);
		Itdb_Artwork *photo = g_hash_table_lookup (hash, &image_id);
/*		printf ("id: %d, photo: %p\n", image_id, photo);*/
		glp->data = photo;
	    }
	}
	g_hash_table_destroy (hash);
	return 0;
}