1 /*
2  *  Copyright (C) 2005-2007 Christophe Fergeau
3  *
4  *
5  *  The code contained in this file is free software; you can redistribute
6  *  it and/or modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either version
8  *  2.1 of the License, or (at your option) any later version.
9  *
10  *  This file is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this code; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  *  iTunes and iPod are trademarks of Apple
20  *
21  *  This product is not supported/written/published by Apple!
22  *
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28 
29 #include <stdio.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 
34 #include "itdb.h"
35 #include "itdb_private.h"
36 #include "itdb_endianness.h"
37 #include "db-artwork-debug.h"
38 #include "db-artwork-parser.h"
39 #include "db-image-parser.h"
40 #include "db-itunes-parser.h"
41 #include "db-parse-context.h"
42 #include <glib/gi18n-lib.h>
43 #include <glib/gstdio.h>
44 
45 typedef int (*ParseListItem)(DBParseContext *ctx, GError *error);
46 
47 
48 static int
parse_mhif(DBParseContext * ctx,GError * error)49 parse_mhif (DBParseContext *ctx, GError *error)
50 {
51 	MhifHeader *mhif;
52 
53 	mhif = db_parse_context_get_m_header (ctx, MhifHeader, "mhif");
54 	if (mhif == NULL) {
55 		return -1;
56 	}
57 	dump_mhif (mhif);
58 	db_parse_context_set_total_len (ctx, get_gint32 (mhif->total_len, ctx->byte_order));
59 	return 0;
60 }
61 
62 static int
parse_mhia(DBParseContext * ctx,Itdb_PhotoAlbum * photo_album,GError * error)63 parse_mhia (DBParseContext *ctx, Itdb_PhotoAlbum *photo_album, GError *error)
64 {
65 	MhiaHeader *mhia;
66 	guint32 image_id;
67 
68 	mhia = db_parse_context_get_m_header (ctx, MhiaHeader, "mhia");
69 	if (mhia == NULL) {
70 		return -1;
71 	}
72 	dump_mhia (mhia);
73 	image_id = get_gint32 (mhia->image_id, ctx->byte_order);
74 	photo_album->members = g_list_append (photo_album->members,
75 					      GUINT_TO_POINTER(image_id));
76 	db_parse_context_set_total_len (ctx,
77 					get_gint32_db (ctx->db, mhia->total_len));
78 	return 0;
79 }
80 
81 static char *
get_utf16_string(void * buffer,gint length,guint byte_order)82 get_utf16_string (void* buffer, gint length, guint byte_order)
83 {
84 	char *result;
85 	gunichar2 *tmp;
86 	int i;
87 	/* Byte-swap the utf16 characters if necessary (I'm relying
88 	 * on gcc to optimize most of this code away on LE platforms)
89 	 */
90 	tmp = g_memdup (buffer, length);
91 	for (i = 0; i < length/2; i++) {
92 		tmp[i] = get_gint16 (tmp[i], byte_order);
93 	}
94 	result = g_utf16_to_utf8 (tmp, length/2, NULL, NULL, NULL);
95 	g_free (tmp);
96 
97 	return result;
98 
99 }
100 
101 struct ParsedMhodString {
102         enum MhodArtworkType mhod_type;
103         char *mhod_string;
104 };
105 
106 static struct ParsedMhodString *
parse_mhod_string(DBParseContext * ctx,GError * error)107 parse_mhod_string (DBParseContext *ctx, GError *error)
108 {
109 	struct ParsedMhodString *result;
110 	ArtworkDB_MhodHeaderString *mhod_string;
111 	ArtworkDB_MhodHeader *mhod;
112 	gint len;
113 	mhod = db_parse_context_get_m_header (ctx, ArtworkDB_MhodHeader, "mhod");
114 	if (mhod == NULL) {
115 		return NULL;
116 	}
117 	db_parse_context_set_total_len (ctx, get_gint32 (mhod->total_len, ctx->byte_order));
118 
119 	if (get_gint32 (mhod->total_len, ctx->byte_order) < sizeof (ArtworkDB_MhodHeaderString)){
120 		return NULL;
121 	}
122 
123 	result = g_new0 (struct ParsedMhodString, 1);
124 	if (result == NULL) {
125 		return NULL;
126 	}
127 
128 	mhod_string = (ArtworkDB_MhodHeaderString*)mhod;
129 	result->mhod_type = get_gint16 (mhod_string->type, ctx->byte_order);
130 	len = get_gint32 (mhod_string->string_len, ctx->byte_order);
131 	switch (mhod_string->encoding) {
132 	case 2:
133 		result->mhod_string = get_utf16_string ((gunichar2 *)mhod_string->string,
134 							len, ctx->byte_order);
135 		break;
136 	case 0:
137 	case 1:
138 		result->mhod_string = g_strndup (mhod_string->string, len);
139 		break;
140 	default:
141 		g_warning (_("Unexpected mhod string type: %d\n"),
142 			   mhod_string->encoding);
143 		break;
144 	}
145 	dump_mhod_string (mhod_string);
146 	return result;
147 }
148 
149 static int
parse_mhod_3(DBParseContext * ctx,Itdb_Thumb_Ipod_Item * thumb,GError * error)150 parse_mhod_3 (DBParseContext *ctx,
151 	      Itdb_Thumb_Ipod_Item *thumb, GError *error)
152 {
153 	struct ParsedMhodString *mhod;
154 	mhod = parse_mhod_string (ctx, error);
155 	if (mhod == NULL) {
156 		return -1;
157 	}
158 	if (mhod->mhod_type != MHOD_ARTWORK_TYPE_FILE_NAME) {
159 		g_free (mhod->mhod_string);
160 		g_free (mhod);
161 		return -1;
162 	}
163 
164 	thumb->filename = mhod->mhod_string;
165 	g_free (mhod);
166 	return 0;
167 }
168 
169 static int
parse_photo_mhni(DBParseContext * ctx,Itdb_Thumb_Ipod * thumbs,GError * error)170 parse_photo_mhni (DBParseContext *ctx, Itdb_Thumb_Ipod *thumbs, GError *error)
171 {
172 	MhniHeader *mhni;
173 	DBParseContext *mhod_ctx;
174 	Itdb_Thumb_Ipod_Item *thumb;
175 
176 	mhni = db_parse_context_get_m_header (ctx, MhniHeader, "mhni");
177 	if (mhni == NULL) {
178 		return -1;
179 	}
180 	db_parse_context_set_total_len (ctx, get_gint32 (mhni->total_len, ctx->byte_order));
181 	dump_mhni (mhni);
182 
183 	thumb = ipod_image_new_from_mhni (mhni, ctx->db);
184 	if (thumb == NULL) {
185 		return 0;
186 	}
187 
188         itdb_thumb_ipod_add (thumbs, thumb);
189 
190 	mhod_ctx = db_parse_context_get_sub_context (ctx, ctx->header_len);
191 	if (mhod_ctx == NULL) {
192 	  return -1;
193 	}
194 	parse_mhod_3 (mhod_ctx, thumb, error);
195 	g_free (mhod_ctx);
196 
197 	return 0;
198 }
199 
200 static int
parse_photo_mhod(DBParseContext * ctx,Itdb_Thumb_Ipod * thumbs,GError * error)201 parse_photo_mhod (DBParseContext *ctx, Itdb_Thumb_Ipod *thumbs, GError *error)
202 {
203 	ArtworkDB_MhodHeader *mhod;
204 	DBParseContext *mhni_ctx;
205 	gint32 type;
206 
207 	mhod = db_parse_context_get_m_header (ctx, ArtworkDB_MhodHeader, "mhod");
208 	if (mhod == NULL) {
209 		return -1;
210 	}
211 	db_parse_context_set_total_len (ctx, get_gint32 (mhod->total_len, ctx->byte_order));
212 
213 	type = get_gint16 (mhod->type, ctx->byte_order);
214 
215         dump_mhod (mhod);
216 
217 	/* if this is a container... */
218 	if (type == MHOD_ARTWORK_TYPE_THUMBNAIL) {
219 		mhni_ctx = db_parse_context_get_sub_context (ctx, ctx->header_len);
220 		if (mhni_ctx == NULL) {
221 			return -1;
222 		}
223 		parse_photo_mhni (mhni_ctx, thumbs, NULL);
224 		g_free (mhni_ctx);
225 	}
226 
227 	return 0;
228 }
229 
230 static int
parse_mhii(DBParseContext * ctx,GError * error)231 parse_mhii (DBParseContext *ctx, GError *error)
232 {
233 	MhiiHeader *mhii;
234 	DBParseContext *mhod_ctx;
235 	int num_children;
236 	off_t cur_offset;
237 	Itdb_Artwork *artwork;
238 	Itdb_PhotoDB *photodb;
239 	guint64 mactime;
240 	Itdb_Device *device = db_get_device (ctx->db);
241         Itdb_Thumb_Ipod *thumbs;
242 
243 	mhii = db_parse_context_get_m_header (ctx, MhiiHeader, "mhii");
244 	if (mhii == NULL)
245 	{
246 	    return -1;
247 	}
248 	db_parse_context_set_total_len (ctx, get_gint32 (mhii->total_len, ctx->byte_order));
249 	dump_mhii (mhii);
250 
251 	artwork = itdb_artwork_new ();
252 
253 	artwork->id = get_gint32 (mhii->image_id, ctx->byte_order);
254 	artwork->unk028 = get_gint32 (mhii->unknown4, ctx->byte_order);
255 	artwork->rating = get_gint32 (mhii->rating, ctx->byte_order);
256 	artwork->unk036 = get_gint32 (mhii->unknown6, ctx->byte_order);
257 	mactime = get_gint32 (mhii->orig_date, ctx->byte_order);
258 	artwork->creation_date = device_time_mac_to_time_t (device, mactime);
259 	mactime = get_gint32 (mhii->digitized_date, ctx->byte_order);
260 	artwork->digitized_date = device_time_mac_to_time_t (device, mactime);
261 	artwork->artwork_size = get_gint32 (mhii->orig_img_size, ctx->byte_order);
262 	artwork->dbid = get_gint64 (mhii->song_id, ctx->byte_order);
263 
264         thumbs = (Itdb_Thumb_Ipod *)itdb_thumb_ipod_new ();
265         artwork->thumbnail = (Itdb_Thumb *)thumbs;
266 	cur_offset = ctx->header_len;
267 	mhod_ctx = db_parse_context_get_sub_context (ctx, cur_offset);
268 	num_children = get_gint32 (mhii->num_children, ctx->byte_order);
269 	while ((num_children > 0) && (mhod_ctx != NULL))
270 	{
271 	    parse_photo_mhod (mhod_ctx, thumbs, NULL);
272 	    num_children--;
273 	    cur_offset += mhod_ctx->total_len;
274 	    g_free (mhod_ctx);
275 	    mhod_ctx = db_parse_context_get_sub_context (ctx, cur_offset);
276 	}
277         g_free (mhod_ctx);
278 
279 	switch (ctx->db->db_type)
280 	{
281 	case DB_TYPE_PHOTO:
282 	    photodb = db_get_photodb (ctx->db);
283 	    g_return_val_if_fail (photodb, -1);
284 	    photodb->photos = g_list_append (photodb->photos, artwork);
285 	    break;
286 	case DB_TYPE_ITUNES:
287 	    g_return_val_if_fail (ctx->artwork!=NULL, -1);
288 	    *ctx->artwork = g_list_prepend (*ctx->artwork, artwork);
289 	    break;
290 	default:
291 	    g_return_val_if_reached (-1);
292 	}
293 
294 	return 0;
295 }
296 
297 static int
parse_mhba(DBParseContext * ctx,GError * error)298 parse_mhba (DBParseContext *ctx, GError *error)
299 {
300 	MhbaHeader *mhba;
301 	DBParseContext *mhod_ctx;
302 	DBParseContext *mhia_ctx;
303 	Itdb_PhotoAlbum *album;
304 	Itdb_PhotoDB *photodb;
305 	int num_children;
306 	off_t cur_offset;
307 
308 	mhba = db_parse_context_get_m_header (ctx, MhbaHeader, "mhba");
309 	if (mhba == NULL) {
310 		return -1;
311 	}
312 	db_parse_context_set_total_len (ctx, get_gint32 (mhba->total_len, ctx->byte_order));
313 
314 	dump_mhba (mhba);
315 
316 	album = g_new0 (Itdb_PhotoAlbum, 1);
317 	album->album_id = get_gint32(mhba->album_id, ctx->byte_order);
318 	album->unk024 = get_gint32(mhba->unk024, ctx->byte_order);
319 	album->unk028 = get_gint16(mhba->unk028, ctx->byte_order);
320 	album->album_type = mhba->album_type;
321 	album->playmusic = mhba->playmusic;
322 	album->repeat = mhba->repeat;
323 	album->random = mhba->random;
324 	album->show_titles = mhba->show_titles;
325 	album->transition_direction = mhba->transition_direction;
326 	album->slide_duration = get_gint32(mhba->slide_duration,
327 					   ctx->byte_order);
328 	album->transition_duration = get_gint32(mhba->transition_duration,
329 						ctx->byte_order);
330 	album->unk044 = get_gint32(mhba->unk044, ctx->byte_order);
331 	album->unk048 = get_gint32(mhba->unk048, ctx->byte_order);
332 	album->song_id = get_gint64(mhba->song_id, ctx->byte_order);
333 	album->prev_album_id = get_gint32(mhba->prev_album_id,
334 					  ctx->byte_order);
335 
336 	cur_offset = ctx->header_len;
337 	num_children = get_gint32 (mhba->num_mhods, ctx->byte_order);
338 
339         mhod_ctx = db_parse_context_get_sub_context (ctx, cur_offset);
340         while ((num_children > 0) && (mhod_ctx != NULL)) {
341 		struct ParsedMhodString *mhod;
342 
343 		mhod = parse_mhod_string (mhod_ctx, error);
344 		if (mhod == NULL) {
345 			break;
346 		}
347 		switch (mhod->mhod_type)
348 		{  /* FIXME: type==1 is album name. type==2 seems to be
349 		    * the transtition type between photos,
350 		    * e.g. "Dissolve". Not handled yet. */
351 		case MHOD_ARTWORK_TYPE_ALBUM_NAME:
352 			g_free (album->name);
353 			album->name = mhod->mhod_string;
354 			g_free (mhod);
355 			break;
356 		default:
357 			g_free (mhod->mhod_string);
358 			g_free (mhod);
359 			break;
360 		}
361 		cur_offset += mhod_ctx->total_len;
362 		g_free (mhod_ctx);
363 		num_children--;
364 		mhod_ctx = db_parse_context_get_sub_context (ctx, cur_offset);
365         }
366 	g_free (mhod_ctx);
367 
368 	mhia_ctx = db_parse_context_get_sub_context (ctx, cur_offset);
369 	num_children = get_gint32 (mhba->num_mhias, ctx->byte_order);
370 	while ((num_children > 0) && (mhia_ctx != NULL)) {
371 		parse_mhia (mhia_ctx, album, NULL);
372 		num_children--;
373 		cur_offset += mhia_ctx->total_len;
374 		g_free (mhia_ctx);
375 		mhia_ctx = db_parse_context_get_sub_context (ctx, cur_offset);
376 	}
377         g_free (mhia_ctx);
378 	photodb = db_get_photodb (ctx->db);
379 	g_return_val_if_fail (photodb, -1);
380 	album->photodb = photodb;
381 	photodb->photoalbums = g_list_append (photodb->photoalbums,
382 					      album);
383 	return 0;
384 }
385 
386 
387 static int
parse_mhl(DBParseContext * ctx,GError * error,const char * id,ParseListItem parse_child)388 parse_mhl (DBParseContext *ctx, GError *error,
389 	   const char *id, ParseListItem parse_child)
390 {
391 	MhlHeader *mhl;
392 	int num_children;
393 	DBParseContext *mhi_ctx;
394 	off_t cur_offset;
395 
396 	mhl = db_parse_context_get_m_header (ctx, MhlHeader, id);
397 	if (mhl == NULL) {
398 		return -1;
399 	}
400 
401 	dump_mhl (mhl, id);
402 
403 	num_children = get_gint32 (mhl->num_children, ctx->byte_order);
404 	if (num_children < 0) {
405 		return -1;
406 	}
407 
408 	cur_offset = ctx->header_len;
409 	mhi_ctx = db_parse_context_get_sub_context (ctx, cur_offset);
410 	while ((num_children > 0) && (mhi_ctx != NULL)) {
411 		if (parse_child != NULL) {
412 			parse_child (mhi_ctx, NULL);
413 		}
414 		num_children--;
415 		cur_offset += mhi_ctx->total_len;
416 		g_free (mhi_ctx);
417 		mhi_ctx = db_parse_context_get_sub_context (ctx, cur_offset);
418 	}
419         g_free (mhi_ctx);
420 
421 	return 0;
422 
423 }
424 
425 
426 static int
parse_mhsd(DBParseContext * ctx,GError ** error)427 parse_mhsd (DBParseContext *ctx, GError **error)
428 {
429 	ArtworkDB_MhsdHeader *mhsd;
430 
431 	mhsd = db_parse_context_get_m_header (ctx, ArtworkDB_MhsdHeader, "mhsd");
432 	if (mhsd == NULL) {
433 		return -1;
434 	}
435 
436 	db_parse_context_set_total_len (ctx, get_gint32 (mhsd->total_len, ctx->byte_order));
437 	dump_mhsd (mhsd);
438 	switch (get_gint16_db (ctx->db, mhsd->index)) {
439 	case MHSD_IMAGE_LIST: {
440 		DBParseContext *mhli_context;
441 		mhli_context = db_parse_context_get_next_child (ctx);
442 		parse_mhl (mhli_context, NULL, "mhli", parse_mhii);
443 		g_free (mhli_context);
444 		break;
445 	}
446 	case MHSD_ALBUM_LIST: {
447 		DBParseContext *mhla_context;
448 		mhla_context = db_parse_context_get_next_child (ctx);
449 		parse_mhl (mhla_context, NULL, "mhla", parse_mhba);
450 		g_free (mhla_context);
451 		break;
452 	}
453 	case MHSD_FILE_LIST: {
454 		DBParseContext *mhlf_context;
455 		mhlf_context = db_parse_context_get_next_child (ctx);
456 		parse_mhl (mhlf_context, NULL, "mhlf", parse_mhif);
457 		g_free (mhlf_context);
458 		break;
459 	}
460 	default:
461 		g_warning (_("Unexpected mhsd index: %d\n"),
462 			   get_gint16_db (ctx->db, mhsd->index));
463 		return -1;
464 		break;
465 	}
466 
467 	return 0;
468 }
469 
470 
471 /* Compares the two guint64 values being pointed to and returns TRUE if
472  * they are equal. It can be passed to g_hash_table_new() as the
473  *  key_equal_func parameter, when using pointers to guint64 as keys
474  * in a GHashTable.
475  */
476 static gboolean
guint64_equal(gconstpointer v1,gconstpointer v2)477 guint64_equal (gconstpointer v1, gconstpointer v2)
478 {
479     guint64 i1 = *(const guint64*)v1;
480     guint64 i2 = *(const guint64*)v2;
481     return i1 == i2;
482 }
483 
484 /* Converts a pointer to a guint64 to a hash value. It can be passed to
485  * g_hash_table_new() as the hash_func parameter, when using pointers
486  * to guint64 values as keys in a GHashTable.
487  */
488 static guint
guint64_hash(gconstpointer v)489 guint64_hash(gconstpointer v)
490 {
491     guint64 i = *(const guint64*)v;
492     return i ^ (i >> 32);
493 }
494 
495 /* Apple introduced a new way to associate artwork. The former way
496  * used the dbid to link each artwork (mhii) back to the track. The
497  * new way uses the mhii id to link from each track to the mhii. Above
498  * we only handled the former way */
499 static int
mhfd_associate_itunesdb_artwork(DBParseContext * ctx)500 mhfd_associate_itunesdb_artwork (DBParseContext *ctx)
501 {
502     GHashTable *mhii_id_hash;
503     GHashTable *song_dbid_hash;
504     Itdb_iTunesDB *itdb;
505     GList *gl;
506 
507     g_return_val_if_fail (ctx && ctx->artwork, -1);
508     itdb = db_get_itunesdb (ctx->db);
509     g_return_val_if_fail (itdb, -1);
510 
511     /* make a hash linking the dbid with the songs for faster
512        lookup */
513     song_dbid_hash = g_hash_table_new (guint64_hash, guint64_equal);
514 
515     for (gl = itdb->tracks; gl != NULL; gl = gl->next) {
516 	Itdb_Track *song = (Itdb_Track*)gl->data;
517 	g_hash_table_insert (song_dbid_hash, &song->dbid, song);
518     }
519 
520     /* make a hash linking the mhii with the artwork for faster
521        lookup */
522     mhii_id_hash = g_hash_table_new_full (g_direct_hash,
523 					  g_direct_equal,
524 					  NULL,
525 					  (GDestroyNotify)itdb_artwork_free);
526 
527     for (gl=*ctx->artwork; gl; gl=gl->next)
528     {
529 	Itdb_Track *track;
530 	Itdb_Artwork *artwork=gl->data;
531 	g_return_val_if_fail (artwork, -1);
532 
533 	g_hash_table_insert (mhii_id_hash, GINT_TO_POINTER (artwork->id), artwork);
534 
535 	/* add Artwork to track indicated by the dbid for backward
536 	   compatibility */
537 	track = g_hash_table_lookup (song_dbid_hash, &artwork->dbid);
538 	if (track == NULL)
539 	{
540 	    gchar *strval = g_strdup_printf("%" G_GINT64_FORMAT, artwork->dbid);
541 	    g_print (_("Could not find corresponding track (dbid: %s) for artwork entry.\n"), strval);
542 	    g_free (strval);
543 	}
544 	else
545 	{
546 	    itdb_artwork_free (track->artwork);
547 	    track->artwork = itdb_artwork_duplicate (artwork);
548 	}
549     }
550     /* Now go through all the tracks and add artwork where an
551      * mhii_link is available */
552     for (gl=itdb->tracks; gl; gl=gl->next)
553     {
554 	Itdb_Track *track = gl->data;
555 	g_return_val_if_fail (track, -1);
556 	if (track->mhii_link)
557 	{
558 	    Itdb_Artwork *artwork;
559 	    artwork = g_hash_table_lookup (mhii_id_hash,
560 					   GINT_TO_POINTER (track->mhii_link));
561 	    if (artwork)
562 	    {
563 		g_return_val_if_fail (track->artwork, -1);
564 		if (track->artwork->id != track->mhii_link)
565 		{
566 		    itdb_artwork_free (track->artwork);
567 		    track->artwork = itdb_artwork_duplicate (artwork);
568 		}
569 		else
570 		{
571 		    /* same artwork -- don't copy again */
572 		}
573 	    }
574 	    else
575 	    {
576 		/* The user can't do much here, so let's not worry them with
577 		 * this warning...
578 		gchar *strval = g_strdup_printf("%" G_GINT64_FORMAT, track->dbid);
579 		g_print (_("Could not find artwork entry (mhii id: %u) for track (dbid: %s).\n"), track->mhii_link, strval);
580 		g_free (strval);
581 		 */
582 
583 		/* couldn't find artwork -- make sure track data is in
584 		   a consistent state. */
585 		itdb_track_remove_thumbnails (track);
586 	    }
587 	}
588     }
589     g_hash_table_destroy (mhii_id_hash);
590     g_hash_table_destroy (song_dbid_hash);
591     /* The actual ItdbArtwork data was freed through the GHashTable
592        value_destroy_func */
593     g_list_free (*ctx->artwork);
594     ctx->artwork = NULL;
595 
596     return 0;
597 }
598 
599 
600 
601 /* Database Object */
602 static int
parse_mhfd(DBParseContext * ctx,GError ** error)603 parse_mhfd (DBParseContext *ctx, GError **error)
604 {
605 	MhfdHeader *mhfd;
606 	DBParseContext *mhsd_context;
607 	unsigned int cur_pos;
608 	gint total_len;
609 	gint32 i;
610 	GList *artwork_glist = NULL;
611 
612 	mhfd = db_parse_context_get_m_header (ctx, MhfdHeader, "mhfd");
613 	if (mhfd == NULL) {
614 		return -1;
615 	}
616 
617 	/* Sanity check */
618 	total_len = get_gint32_db (ctx->db, mhfd->total_len);
619 	g_return_val_if_fail (total_len == ctx->total_len, -1);
620 	dump_mhfd (mhfd);
621 	cur_pos = ctx->header_len;
622 
623 	if (ctx->db->db_type == DB_TYPE_ITUNES)
624 	{   /* we need to collect all artwork in this GList for
625 	       iTunesDB (see below) */
626 	    ctx->artwork = &artwork_glist;
627 	}
628 
629 	for (i=0; i<mhfd->num_children; ++i)
630 	{
631 	    /* so far all mhfd we know have 3 children, but better be
632 	       safe than sorry */
633 	    mhsd_context = db_parse_context_get_sub_context (ctx, cur_pos);
634 	    if (mhsd_context == NULL) {
635 		return -1;
636 	    }
637 	    parse_mhsd (mhsd_context, NULL);
638 	    cur_pos += mhsd_context->total_len;
639 	    g_free (mhsd_context);
640 	}
641 
642 	if (ctx->db->db_type == DB_TYPE_ITUNES)
643 	{
644 	    return mhfd_associate_itunesdb_artwork (ctx);
645 	}
646 
647 	return 0;
648 }
649 
650 
651 
652 G_GNUC_INTERNAL char *
ipod_db_get_artwork_db_path(const char * mount_point)653 ipod_db_get_artwork_db_path (const char *mount_point)
654 {
655         gchar *filename=NULL;
656 
657 	/* fail silently if no mount point given */
658 	if (!mount_point)  return NULL;
659 
660 	filename = itdb_get_artworkdb_path (mount_point);
661 
662 	/* itdb_resolve_path() only returns existing paths */
663 	if (!filename)
664 	{
665 	    gchar *artwork_dir;
666 
667 	    artwork_dir = itdb_get_artwork_dir (mount_point);
668 	    if (!artwork_dir)
669 	    {
670 		/* attempt to create Artwork dir */
671 		gchar *control_dir = itdb_get_control_dir (mount_point);
672 		if (control_dir)
673 		{
674 		    gchar *dir = g_build_filename (control_dir, "Artwork", NULL);
675 		    g_mkdir (dir, 0777);
676 		    g_free (control_dir);
677 		    g_free (dir);
678 		    artwork_dir = itdb_get_artwork_dir (mount_point);
679 		}
680 	    }
681 	    if (artwork_dir)
682 	    {
683 		filename = g_build_filename (artwork_dir,
684 					     "ArtworkDB", NULL);
685 		g_free (artwork_dir);
686 	    }
687 	}
688 
689 	return filename;
690 }
691 
692 int
ipod_parse_artwork_db(Itdb_iTunesDB * itdb)693 ipod_parse_artwork_db (Itdb_iTunesDB *itdb)
694 {
695 	DBParseContext *ctx;
696 	char *filename;
697 	Itdb_DB db;
698 
699 	db.db.itdb = itdb;
700 	db.db_type = DB_TYPE_ITUNES;
701 
702 	g_return_val_if_fail (itdb, -1);
703 
704 	if (!itdb_device_supports_artwork (itdb->device)) {
705 		return -1;
706 	}
707 	ctx = NULL;
708 	filename = ipod_db_get_artwork_db_path (itdb_get_mountpoint (itdb));
709 	if (filename == NULL) {
710 		goto error;
711 	}
712 	if (!g_file_test (filename, G_FILE_TEST_EXISTS))
713 	{
714 		goto error;
715 	}
716 
717 	ctx = db_parse_context_new_from_file (filename, &db);
718 	g_free (filename);
719 
720 	if (ctx == NULL) {
721 		goto error;
722 	}
723 
724 	parse_mhfd (ctx, NULL);
725 	db_parse_context_destroy (ctx);
726 	return 0;
727 
728  error:
729 	return -1;
730 }
731 
732 
733 G_GNUC_INTERNAL char *
ipod_db_get_photos_db_path(const char * mount_point)734 ipod_db_get_photos_db_path (const char *mount_point)
735 {
736         gchar *filename=NULL;
737 
738 	/* fail silently if no mount point given */
739 	if (!mount_point)  return NULL;
740 
741 	filename = itdb_get_photodb_path (mount_point);
742 
743 	/* itdb_resolve_path() only returns existing paths */
744 	if (!filename)
745 	{
746 	    gchar *photos_dir;
747 
748 	    photos_dir = itdb_get_photos_dir (mount_point);
749 	    if (!photos_dir)
750 	    {
751 		/* attempt to create Photos dir */
752 		gchar *dir = g_build_filename (mount_point, "Photos", NULL);
753 		g_mkdir (dir, 0777);
754 		g_free (dir);
755 		photos_dir = itdb_get_photos_dir (mount_point);
756 	    }
757 	    if (photos_dir)
758 	    {
759 		filename = g_build_filename (photos_dir,
760 					     "Photo Database", NULL);
761 		g_free (photos_dir);
762 	    }
763 	}
764 
765 	return filename;
766 }
767 
768 
769 int
ipod_parse_photo_db(Itdb_PhotoDB * photodb)770 ipod_parse_photo_db (Itdb_PhotoDB *photodb)
771 {
772 	DBParseContext *ctx;
773 	char *filename;
774 	Itdb_DB db;
775 
776 	GList *gl;
777 	GHashTable *hash;
778 
779 	db.db.photodb = photodb;
780 	db.db_type = DB_TYPE_PHOTO;
781 
782 
783 	filename = itdb_get_photodb_path (
784 	    itdb_photodb_get_mountpoint (photodb));
785 	if (filename == NULL) {
786 		return -1;
787 	}
788 
789 	ctx = db_parse_context_new_from_file (filename, &db );
790 	g_free (filename);
791 	if (ctx == NULL) {
792 		return  -1;
793 	}
794 	parse_mhfd (ctx, NULL);
795 	db_parse_context_destroy (ctx);
796 
797 	/* Now we need to replace references to artwork_ids in the
798 	 * photo albums with references to the actual artwork
799 	 * structure. Since we cannot guarantee that the list with the
800 	 * photos is read before the album list, we cannot safely do
801 	 * this at the time of reading the ids. */
802 
803 	/* Create a hash for faster lookup */
804 	hash = g_hash_table_new (g_int_hash, g_int_equal);
805 	for (gl=photodb->photos; gl; gl=gl->next)
806 	{
807 	    Itdb_Artwork *photo = gl->data;
808 	    g_return_val_if_fail (photo, -1);
809 	    g_hash_table_insert (hash, &photo->id, photo);
810 /*	    printf ("id: %d, photo: %p\n", photo->id, photo);*/
811 	}
812 	for (gl=photodb->photoalbums; gl; gl=gl->next)
813 	{
814 	    GList *glp;
815 	    Itdb_PhotoAlbum *album = gl->data;
816 	    g_return_val_if_fail (album, -1);
817 	    for (glp=album->members; glp; glp=glp->next)
818 	    {
819 		guint image_id = GPOINTER_TO_UINT (glp->data);
820 		Itdb_Artwork *photo = g_hash_table_lookup (hash, &image_id);
821 /*		printf ("id: %d, photo: %p\n", image_id, photo);*/
822 		glp->data = photo;
823 	    }
824 	}
825 	g_hash_table_destroy (hash);
826 	return 0;
827 }
828 
829