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