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 #include <config.h>
26 #include "itdb.h"
27 #include "itdb_device.h"
28 #include "itdb_private.h"
29 #include "db-artwork-parser.h"
30
31 #if HAVE_GDKPIXBUF
32 #include <gdk-pixbuf/gdk-pixbuf.h>
33
34 #include "db-artwork-debug.h"
35 #include "db-itunes-parser.h"
36 #include "db-image-parser.h"
37 #include "itdb_endianness.h"
38
39 #include <glib/gstdio.h>
40
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <string.h>
44 #ifdef HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif
47 #include <sys/stat.h>
48 #include <stdio.h>
49 #include <sys/types.h>
50
51 #define DEFAULT_GSTRING_SIZE 128*1024
52
53 struct iPodSharedDataBuffer {
54 GString *data;
55 char *filename;
56 int ref_count;
57 };
58
59 struct _iPodBuffer {
60 struct iPodSharedDataBuffer *shared;
61 off_t offset;
62 guint byte_order;
63 DbType db_type;
64 };
65
66 typedef struct _iPodBuffer iPodBuffer;
67
68 static gboolean
ipod_gstring_flush(struct iPodSharedDataBuffer * shared,GError ** error)69 ipod_gstring_flush (struct iPodSharedDataBuffer *shared, GError **error)
70 {
71 gboolean success;
72
73 success = g_file_set_contents (shared->filename,
74 shared->data->str, shared->data->len,
75 error);
76 if (!success) {
77 return FALSE;
78 }
79 g_string_free (shared->data, TRUE);
80 g_free (shared->filename);
81 g_free (shared);
82
83 return TRUE;
84 }
85
86 static void
ipod_buffer_destroy(iPodBuffer * buffer)87 ipod_buffer_destroy (iPodBuffer *buffer)
88 {
89 buffer->shared->ref_count--;
90 if (buffer->shared->ref_count == 0) {
91 ipod_gstring_flush (buffer->shared, NULL);
92 }
93 g_free (buffer);
94 }
95
96
97 static void *
ipod_buffer_get_pointer(iPodBuffer * buffer)98 ipod_buffer_get_pointer (iPodBuffer *buffer)
99 {
100 if (buffer->shared->data->str == NULL) {
101 return NULL;
102 }
103 g_assert (buffer->offset < buffer->shared->data->len);
104 return &((unsigned char *)buffer->shared->data->str)[buffer->offset];
105 }
106
107 static void
ipod_buffer_maybe_grow(iPodBuffer * buffer,off_t size)108 ipod_buffer_maybe_grow (iPodBuffer *buffer, off_t size)
109 {
110 g_string_set_size (buffer->shared->data,
111 buffer->shared->data->len + size);
112 }
113
114 static iPodBuffer *
ipod_buffer_get_sub_buffer(iPodBuffer * buffer,off_t offset)115 ipod_buffer_get_sub_buffer (iPodBuffer *buffer, off_t offset)
116 {
117 iPodBuffer *sub_buffer;
118
119 g_assert (buffer->offset + offset <= buffer->shared->data->len);
120
121 sub_buffer = g_new0 (iPodBuffer, 1);
122 if (sub_buffer == NULL) {
123 return NULL;
124 }
125 sub_buffer->shared = buffer->shared;
126 sub_buffer->offset = buffer->offset + offset;
127 sub_buffer->byte_order = buffer->byte_order;
128 sub_buffer->db_type = buffer->db_type;
129
130 buffer->shared->ref_count++;
131
132 return sub_buffer;
133 }
134
135 static iPodBuffer *
ipod_buffer_new(const char * filename,guint byte_order,DbType db_type)136 ipod_buffer_new (const char *filename, guint byte_order, DbType db_type)
137 {
138 struct iPodSharedDataBuffer *shared;
139 iPodBuffer *buffer;
140
141 shared = g_new0 (struct iPodSharedDataBuffer, 1);
142 if (shared == NULL) {
143 return NULL;
144 }
145 shared->filename = g_strdup (filename);
146 shared->data = g_string_sized_new (DEFAULT_GSTRING_SIZE);
147 shared->ref_count = 1;
148
149 buffer = g_new0 (iPodBuffer, 1);
150 if (buffer == NULL) {
151 g_free (shared->filename);
152 g_string_free (shared->data, TRUE);
153 g_free (shared);
154 return NULL;
155 }
156 buffer->shared = shared;
157 buffer->byte_order = byte_order;
158 buffer->db_type = db_type;
159
160 return buffer;
161 }
162
163 enum MhsdType {
164 MHSD_TYPE_MHLI = 1,
165 MHSD_TYPE_MHLA = 2,
166 MHSD_TYPE_MHLF = 3
167 };
168
169 #define RETURN_SIZE_FOR(id, size) if (strncmp (id, header_id, 4) == 0) return (size)
170
171
172
173 /* Returns the "real" size for a header, ie the size iTunes uses for it
174 * (padding included)
175 */
176 static int
get_padded_header_size(gchar header_id[4])177 get_padded_header_size (gchar header_id[4])
178 {
179 RETURN_SIZE_FOR ("mhni", 0x4c);
180 RETURN_SIZE_FOR ("mhii", 0x98);
181 RETURN_SIZE_FOR ("mhsd", 0x60);
182 RETURN_SIZE_FOR ("mhfd", 0x84);
183 RETURN_SIZE_FOR ("mhli", 0x5c);
184 RETURN_SIZE_FOR ("mhla", 0x5c);
185 RETURN_SIZE_FOR ("mhlf", 0x5c);
186 RETURN_SIZE_FOR ("mhif", 0x7c);
187 RETURN_SIZE_FOR ("mhba", 0x94);
188
189 return 0;
190 }
191
192 static void *
init_header(iPodBuffer * buffer,gchar _header_id[4],guint header_len)193 init_header (iPodBuffer *buffer, gchar _header_id[4], guint header_len)
194 {
195 MHeader *mh;
196 int padded_size;
197 gchar *header_id;
198
199 padded_size = get_padded_header_size (_header_id);
200 if (padded_size != 0) {
201 header_len = padded_size;
202 }
203 g_assert (header_len > sizeof (MHeader));
204 ipod_buffer_maybe_grow (buffer, header_len);
205
206 mh = (MHeader*)ipod_buffer_get_pointer (buffer);
207 if (mh == NULL) {
208 return NULL;
209 }
210 memset (mh, 0, header_len);
211
212 header_id = g_strndup (_header_id, 4);
213 if (buffer->byte_order == G_BIG_ENDIAN) {
214 g_strreverse (header_id);
215 }
216 strncpy ((char *)mh->header_id, header_id, 4);
217 mh->header_len = get_gint32 (header_len, buffer->byte_order);
218
219 g_free (header_id);
220 return mh;
221 }
222
223
224 static int
write_mhod_type_1(gchar * string,iPodBuffer * buffer)225 write_mhod_type_1 (gchar *string, iPodBuffer *buffer)
226 {
227 ArtworkDB_MhodHeaderString *mhod;
228 unsigned int total_bytes;
229 int len;
230 int padding;
231
232 g_assert (string != NULL);
233
234 total_bytes = sizeof (ArtworkDB_MhodHeaderString);
235 mhod = (ArtworkDB_MhodHeaderString *)init_header (buffer, "mhod",
236 total_bytes);
237 if (mhod == NULL) {
238 return -1;
239 }
240 mhod->total_len = get_gint32 (total_bytes, buffer->byte_order);
241 /* Modify header length, since iTunes only puts the length of
242 * MhodHeader in header_len
243 */
244 mhod->header_len = get_gint32 (sizeof (ArtworkDB_MhodHeader), buffer->byte_order);
245 mhod->encoding = get_gint32 (0x01, buffer->byte_order);
246 len = strlen (string);
247 mhod->string_len = get_gint32 (len, buffer->byte_order);
248
249 padding = 4 - ( (total_bytes + len) % 4 );
250 if (padding == 4)
251 padding = 0;
252 mhod->padding_len = padding;
253 mhod->type = get_gint16 (0x01, buffer->byte_order);
254
255 /* Make sure we have enough free space to write the string */
256 ipod_buffer_maybe_grow (buffer, len + padding);
257 mhod = ipod_buffer_get_pointer (buffer);
258 if (mhod == NULL) {
259 return -1;
260 }
261 memcpy (mhod->string, string, len);
262 total_bytes += len + padding;
263 mhod->total_len = get_gint32 (total_bytes, buffer->byte_order);
264
265 dump_mhod_string (mhod);
266
267 return total_bytes;
268 }
269
270 static int
write_mhod_type_3(gchar * string,iPodBuffer * buffer)271 write_mhod_type_3 (gchar *string, iPodBuffer *buffer)
272 {
273 ArtworkDB_MhodHeaderString *mhod;
274 unsigned int total_bytes;
275 glong len;
276 const gint g2l = sizeof (gunichar2);
277 gunichar2 *utf16, *strp;
278 int i, padding;
279
280 g_assert (string != NULL);
281
282 total_bytes = sizeof (ArtworkDB_MhodHeaderString);
283 mhod = (ArtworkDB_MhodHeaderString *) init_header (buffer, "mhod",
284 total_bytes);
285 if (mhod == NULL) {
286 return -1;
287 }
288 mhod->total_len = get_gint32 (total_bytes, buffer->byte_order);
289 /* Modify header length, since iTunes only puts the length of
290 * MhodHeader in header_len
291 */
292 mhod->header_len = get_gint32 (sizeof (ArtworkDB_MhodHeader),
293 buffer->byte_order);
294 mhod->type = get_gint16 (3, buffer->byte_order);
295
296 /* FIXME: Tidy this up, combine cases more */
297 /* Some magic: endianess-reversed (BE) mobile phones use UTF8
298 * (version 1) with padding, standard iPods (LE) use UTF16
299 * (version 2).*/
300 switch (buffer->byte_order)
301 {
302 case G_LITTLE_ENDIAN:
303 utf16 = g_utf8_to_utf16 (string, -1, NULL, &len, NULL);
304 if (utf16 == NULL) {
305 return -1;
306 }
307
308 mhod->encoding = 2; /* 8 bit field, no need to byteswap */
309
310 /* number of bytes of the string encoded in UTF-16 */
311 mhod->string_len = get_gint32 (g2l * len, buffer->byte_order);
312 padding = 4 - ( (total_bytes + g2l*len) % 4 );
313 if (padding == 4)
314 padding = 0;
315 mhod->padding_len = padding; /* 8 bit field, no need to byteswap */
316
317 total_bytes += g2l*len + padding;
318
319 /* Make sure we have enough free space to write the string */
320 ipod_buffer_maybe_grow (buffer, g2l*len + padding);
321 mhod = ipod_buffer_get_pointer (buffer);
322 if (mhod == NULL) {
323 g_free (utf16);
324 return -1;
325 }
326 strp = (gunichar2 *)mhod->string;
327 for (i = 0; i < len; i++) {
328 strp[i] = get_gint16 (utf16[i], buffer->byte_order);
329 }
330 g_free (utf16);
331 memset (mhod->string + g2l*len, 0, padding);
332 break;
333 case G_BIG_ENDIAN:
334 mhod->encoding = 1; /* 8 bit field, no need to byteswap */
335 /* FIXME: len isn't initialized */
336 mhod->string_len = get_gint32 (len, buffer->byte_order);
337 /* pad string if necessary */
338 /* e.g. len = 7 bytes, len%4 = 3, 4-3=1 -> requires 1 byte
339 padding */
340 padding = 4 - ( (total_bytes + len) % 4 );
341 if (padding == 4)
342 padding = 0;
343 mhod->padding_len = padding; /* 8 bit field, no need to byteswap */
344
345 /* Make sure we have enough free space to write the string */
346 ipod_buffer_maybe_grow (buffer, len+padding);
347 mhod = ipod_buffer_get_pointer (buffer);
348 if (mhod == NULL) {
349 return -1;
350 }
351 memcpy (mhod->string, string, len);
352 memset (mhod->string + len, 0, padding);
353 total_bytes += (len+padding);
354 }
355 mhod->total_len = get_gint32 (total_bytes, buffer->byte_order);
356
357 dump_mhod_string (mhod);
358
359 return total_bytes;
360 }
361
362 static int
write_mhni(Itdb_DB * db,Itdb_Thumb_Ipod_Item * item,iPodBuffer * buffer)363 write_mhni (Itdb_DB *db, Itdb_Thumb_Ipod_Item *item, iPodBuffer *buffer)
364 {
365 MhniHeader *mhni;
366 unsigned int total_bytes;
367 int bytes_written;
368 iPodBuffer *sub_buffer;
369 const Itdb_ArtworkFormat *format;
370
371 if (item == NULL) {
372 return -1;
373 }
374
375 mhni = (MhniHeader *)init_header (buffer, "mhni",
376 sizeof (MhniHeader));
377 if (mhni == NULL) {
378 return -1;
379 }
380 total_bytes = get_gint32 (mhni->header_len,
381 buffer->byte_order);
382 mhni->total_len = get_gint32 (total_bytes,
383 buffer->byte_order);
384 format = item->format;
385 mhni->format_id = get_gint32 (format->format_id, buffer->byte_order);
386 mhni->image_width = get_gint16 (item->width, buffer->byte_order);
387 mhni->image_height = get_gint16 (item->height, buffer->byte_order);
388 mhni->image_size = get_gint32 (item->size, buffer->byte_order);
389 mhni->ithmb_offset = get_gint32 (item->offset, buffer->byte_order);
390 mhni->vertical_padding = get_gint16 (item->vertical_padding,
391 buffer->byte_order);
392 mhni->horizontal_padding = get_gint16 (item->horizontal_padding,
393 buffer->byte_order);
394
395 sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes);
396 if (sub_buffer == NULL) {
397 return -1;
398 }
399 bytes_written = write_mhod_type_3 (item->filename, sub_buffer);
400 ipod_buffer_destroy (sub_buffer);
401 if (bytes_written == -1) {
402 return -1;
403 }
404 total_bytes += bytes_written;
405 mhni = ipod_buffer_get_pointer (buffer);
406 mhni->total_len = get_gint32 (total_bytes, buffer->byte_order);
407 /* Only update number of children when all went well to try to get
408 * something somewhat consistent when there are errors
409 */
410 mhni->num_children = get_gint32 (1, buffer->byte_order);
411
412 dump_mhni (mhni);
413
414 return total_bytes;
415 }
416
417 static int
write_mhod(Itdb_DB * db,Itdb_Thumb_Ipod_Item * thumb,iPodBuffer * buffer)418 write_mhod (Itdb_DB *db, Itdb_Thumb_Ipod_Item *thumb, iPodBuffer *buffer)
419 {
420 ArtworkDB_MhodHeader *mhod;
421 unsigned int total_bytes;
422 int bytes_written;
423 iPodBuffer *sub_buffer;
424
425 if (thumb == NULL) {
426 return -1;
427 }
428
429 mhod = (ArtworkDB_MhodHeader *)
430 init_header (buffer, "mhod",
431 sizeof (ArtworkDB_MhodHeader));
432 if (mhod == NULL) {
433 return -1;
434 }
435 total_bytes = sizeof (ArtworkDB_MhodHeader);
436 mhod->total_len = get_gint32 (total_bytes, buffer->byte_order);
437 mhod->type = get_gint16 (MHOD_TYPE_LOCATION, buffer->byte_order);
438 sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes);
439 if (sub_buffer == NULL) {
440 return -1;
441 }
442 bytes_written = write_mhni (db, thumb, sub_buffer);
443 ipod_buffer_destroy (sub_buffer);
444 if (bytes_written == -1) {
445 return -1;
446 }
447 total_bytes += bytes_written;
448 mhod = ipod_buffer_get_pointer (buffer);
449 mhod->total_len = get_gint32 (total_bytes, buffer->byte_order);
450
451 dump_mhod (mhod);
452
453 return total_bytes;
454 }
455
456 static int
write_mhii(Itdb_DB * db,void * data,iPodBuffer * buffer)457 write_mhii (Itdb_DB *db, void *data, iPodBuffer *buffer)
458 {
459 MhiiHeader *mhii;
460 unsigned int total_bytes;
461 int bytes_written;
462 int num_children;
463 const GList *it = NULL;
464 Itdb_Track *song;
465 Itdb_Artwork *artwork;
466 guint64 mactime;
467 Itdb_Device *device = db_get_device (db);
468
469 mhii = (MhiiHeader *)init_header (buffer, "mhii", sizeof (MhiiHeader));
470 if (mhii == NULL) {
471 return -1;
472 }
473 total_bytes = get_gint32 (mhii->header_len, buffer->byte_order);
474
475 switch( buffer->db_type) {
476 case DB_TYPE_ITUNES:
477 song = (Itdb_Track *)data;
478 artwork = song->artwork;
479 mhii->song_id = get_gint64 (song->dbid, buffer->byte_order);
480 break;
481 case DB_TYPE_PHOTO:
482 artwork = (Itdb_Artwork *)data;
483 mhii->song_id = get_gint64 (artwork->id + 2, buffer->byte_order);
484 break;
485 default:
486 g_return_val_if_reached (-1);
487 }
488 mhii->image_id = get_guint32 (artwork->id, buffer->byte_order);
489 mhii->unknown4 = get_gint32 (artwork->unk028, buffer->byte_order);
490 mhii->rating = get_gint32 (artwork->rating, buffer->byte_order);
491 mhii->unknown6 = get_gint32 (artwork->unk036, buffer->byte_order);
492
493 mactime = device_time_time_t_to_mac (device, artwork->creation_date);
494 mhii->orig_date = get_guint32 (mactime, buffer->byte_order);
495
496 mactime = device_time_time_t_to_mac (device, artwork->digitized_date);
497 mhii->digitized_date = get_guint32 (mactime, buffer->byte_order);
498
499 mhii->orig_img_size = get_gint32 (artwork->artwork_size, buffer->byte_order);
500 num_children = 0;
501 /* Before trying to write the artwork or photo database, the ithmb
502 * files have been written, which will have converted all thumbnails
503 * attached to the tracks to ITDB_THUMB_TYPE_IPOD thumbnails.
504 */
505 g_assert (artwork->thumbnail->data_type == ITDB_THUMB_TYPE_IPOD);
506 for (it=itdb_thumb_ipod_get_thumbs ((Itdb_Thumb_Ipod *)artwork->thumbnail);
507 it!=NULL;
508 it=it->next)
509 {
510 iPodBuffer *sub_buffer;
511 Itdb_Thumb_Ipod_Item *thumb;
512
513 thumb = (Itdb_Thumb_Ipod_Item *)it->data;
514 if (thumb->format == NULL) {
515 /* skip this thumb */
516 continue;
517 }
518
519 mhii->num_children = get_gint32 (num_children,
520 buffer->byte_order);
521 mhii->total_len = get_gint32 (total_bytes, buffer->byte_order);
522 sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes);
523 if (sub_buffer == NULL) {
524 return -1;
525 }
526 bytes_written = write_mhod (db, thumb, sub_buffer);
527 ipod_buffer_destroy (sub_buffer);
528 if (bytes_written == -1) {
529 return -1;
530 }
531 total_bytes += bytes_written;
532 mhii = ipod_buffer_get_pointer (buffer);
533 num_children++;
534 }
535
536 mhii->num_children = get_gint32 (num_children, buffer->byte_order);
537 mhii->total_len = get_gint32 (total_bytes, buffer->byte_order);
538
539 dump_mhii (mhii);
540
541 return total_bytes;
542 }
543
544 static int
write_mhli(Itdb_DB * db,iPodBuffer * buffer)545 write_mhli (Itdb_DB *db, iPodBuffer *buffer )
546 {
547 GList *it = NULL;
548 MhliHeader *mhli;
549 unsigned int total_bytes;
550 int num_thumbs;
551
552 mhli = (MhliHeader *)init_header (buffer, "mhli", sizeof (MhliHeader));
553 if (mhli == NULL) {
554 return -1;
555 }
556
557 num_thumbs = 0;
558 total_bytes = get_gint32 (mhli->header_len, buffer->byte_order);
559 switch (buffer->db_type) {
560 case DB_TYPE_PHOTO:
561 it = db_get_photodb(db)->photos;
562 break;
563 case DB_TYPE_ITUNES:
564 it = db_get_itunesdb(db)->tracks;
565 break;
566 default:
567 g_return_val_if_reached (-1);
568 }
569 while (it != NULL) {
570 Itdb_Track *song;
571 int bytes_written;
572 iPodBuffer *sub_buffer;
573 if (buffer->db_type == DB_TYPE_ITUNES) {
574 song = (Itdb_Track*)it->data;
575 if (!song->artwork->thumbnail || (song->artwork->dbid == 0)) {
576 it = it->next;
577 continue;
578 }
579 }
580 sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes);
581 if (sub_buffer == NULL) {
582 break;
583 }
584 bytes_written = write_mhii (db, it->data, sub_buffer);
585 ipod_buffer_destroy (sub_buffer);
586 if (bytes_written != -1) {
587 num_thumbs++;
588 total_bytes += bytes_written;
589 }
590 it = it->next;
591 }
592 mhli = ipod_buffer_get_pointer (buffer);
593 mhli->num_children = get_gint32 (num_thumbs, buffer->byte_order);
594 dump_mhl ((MhlHeader *)mhli, "mhli");
595
596 return total_bytes;
597 }
598
599 static int
write_mhia(gint image_id,iPodBuffer * buffer)600 write_mhia (gint image_id, iPodBuffer *buffer)
601 {
602 MhiaHeader *mhia;
603 unsigned int total_bytes;
604
605
606 mhia = (MhiaHeader *)init_header (buffer, "mhia", 40);
607 if (mhia == NULL) {
608 return -1;
609 }
610
611 mhia->total_len = mhia->header_len;
612 mhia->image_id = get_gint32 (image_id, buffer->byte_order);
613 total_bytes = get_gint32 (mhia->header_len, buffer->byte_order);
614 dump_mhia( mhia );
615 return total_bytes;
616 }
617
618 static int
write_mhba(Itdb_PhotoAlbum * album,iPodBuffer * buffer)619 write_mhba (Itdb_PhotoAlbum *album, iPodBuffer *buffer)
620 {
621 GList *it;
622 MhbaHeader *mhba;
623 iPodBuffer *sub_buffer;
624 unsigned int total_bytes;
625 unsigned int bytes_written;
626
627 mhba = (MhbaHeader *)init_header (buffer, "mhba", sizeof (MhbaHeader));
628 if (mhba == NULL) {
629 return -1;
630 }
631 mhba->num_mhods = get_gint32(1, buffer->byte_order);
632 mhba->num_mhias = get_gint32(g_list_length (album->members),
633 buffer->byte_order);
634 mhba->album_id = get_gint32(album->album_id, buffer->byte_order);
635 mhba->unk024 = get_gint32(album->unk024, buffer->byte_order);
636 mhba->unk028 = get_gint16(album->unk028, buffer->byte_order);
637 mhba->album_type = album->album_type;
638 mhba->playmusic = album->playmusic;
639 mhba->repeat = album->repeat;
640 mhba->random = album->random;
641 mhba->show_titles = album->show_titles;
642 mhba->transition_direction = album->transition_direction;
643 mhba->slide_duration = get_gint32(album->slide_duration,
644 buffer->byte_order);
645 mhba->transition_duration = get_gint32(album->transition_duration,
646 buffer->byte_order);
647 mhba->unk044 = get_gint32(album->unk044, buffer->byte_order);
648 mhba->unk048 = get_gint32(album->unk048, buffer->byte_order);
649 mhba->song_id = get_gint64(album->song_id, buffer->byte_order);
650 mhba->prev_album_id = get_gint32(album->prev_album_id,
651 buffer->byte_order);
652
653 total_bytes = get_gint32 (mhba->header_len, buffer->byte_order);
654
655 /* FIXME: Write other mhods */
656 /* Write album title */
657 sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes);
658 if (sub_buffer == NULL) {
659 return -1;
660 }
661 bytes_written = write_mhod_type_1 (album->name, sub_buffer);
662 ipod_buffer_destroy (sub_buffer);
663 if (bytes_written == -1) {
664 return -1;
665 }
666 total_bytes += bytes_written;
667
668 for (it = album->members; it != NULL; it = it->next) {
669 Itdb_Artwork *photo = it->data;
670 g_return_val_if_fail (photo, -1);
671
672 sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes);
673 if (sub_buffer == NULL) {
674 return -1;
675 }
676 bytes_written = write_mhia (photo->id, sub_buffer);
677 ipod_buffer_destroy (sub_buffer);
678 if (bytes_written == -1) {
679 return -1;
680 }
681 total_bytes += bytes_written;
682 }
683 mhba = ipod_buffer_get_pointer (buffer);
684 mhba->total_len = get_gint32( total_bytes, buffer->byte_order );
685 dump_mhba ( mhba );
686 return total_bytes;
687 }
688
689 static int
write_mhla(Itdb_DB * db,iPodBuffer * buffer)690 write_mhla (Itdb_DB *db, iPodBuffer *buffer)
691 {
692 GList *it;
693 MhlaHeader *mhla;
694 iPodBuffer *sub_buffer;
695 unsigned int total_bytes;
696
697 mhla = (MhlaHeader *)init_header (buffer, "mhla", sizeof (MhlaHeader));
698 if (mhla == NULL) {
699 return -1;
700 }
701 total_bytes = get_gint32 (mhla->header_len, buffer->byte_order);
702 if (buffer->db_type == DB_TYPE_PHOTO) {
703 unsigned int bytes_written;
704 unsigned int num_children = 0;
705 for (it = db_get_photodb(db)->photoalbums; it != NULL; it = it->next) {
706 Itdb_PhotoAlbum *album = (Itdb_PhotoAlbum *)it->data;
707
708 sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes);
709 if (sub_buffer == NULL) {
710 return -1;
711 }
712 bytes_written = write_mhba (album, sub_buffer);
713 ipod_buffer_destroy (sub_buffer);
714 if (bytes_written == -1) {
715 return -1;
716 }
717 total_bytes += bytes_written;
718 mhla = ipod_buffer_get_pointer (buffer);
719 num_children++;
720 mhla->num_children = get_gint32 (num_children,
721 buffer->byte_order);
722 }
723 }
724
725 dump_mhl ((MhlHeader *)mhla, "mhla");
726
727 return total_bytes;
728 }
729
730 static int
write_mhif(Itdb_DB * db,iPodBuffer * buffer,const Itdb_ArtworkFormat * img_info)731 write_mhif (Itdb_DB *db, iPodBuffer *buffer,
732 const Itdb_ArtworkFormat *img_info)
733 {
734 MhifHeader *mhif;
735
736 mhif = (MhifHeader *)init_header (buffer, "mhif", sizeof (MhifHeader));
737 if (mhif == NULL) {
738 return -1;
739 }
740 mhif->total_len = mhif->header_len;
741
742 mhif->format_id = get_gint32 (img_info->format_id,
743 buffer->byte_order);
744 mhif->image_size = get_gint32 (img_info->height * img_info->width * 2,
745 buffer->byte_order);
746
747 dump_mhif (mhif);
748
749 return get_gint32 (mhif->header_len, buffer->byte_order);
750 }
751
752 static int
write_mhlf(Itdb_DB * db,iPodBuffer * buffer)753 write_mhlf (Itdb_DB *db, iPodBuffer *buffer)
754 {
755 MhlfHeader *mhlf;
756 unsigned int total_bytes;
757 int bytes_written;
758 GList *formats;
759 GList *it;
760 unsigned int num_children;
761
762 mhlf = (MhlfHeader *)init_header (buffer, "mhlf", sizeof (MhlfHeader));
763 if (mhlf == NULL) {
764 return -1;
765 }
766
767 total_bytes = get_gint32 (mhlf->header_len, buffer->byte_order);
768 num_children = 0;
769 mhlf->num_files = get_gint32 (num_children, buffer->byte_order);
770
771 formats = NULL;
772 switch (buffer->db_type) {
773 case DB_TYPE_ITUNES:
774 formats = itdb_device_get_cover_art_formats(db_get_device(db));
775 break;
776 case DB_TYPE_PHOTO:
777 formats = itdb_device_get_photo_formats(db_get_device(db));
778 break;
779 }
780 if (formats == NULL) {
781 return total_bytes;
782 }
783
784 for (it = formats; it != NULL; it = it->next) {
785 const Itdb_ArtworkFormat *format;
786 iPodBuffer *sub_buffer;
787
788 format = (const Itdb_ArtworkFormat *)it->data;
789 sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes);
790 if (sub_buffer == NULL) {
791 g_list_free (formats);
792 return -1;
793 }
794
795 bytes_written = write_mhif (db, sub_buffer, format);
796
797 ipod_buffer_destroy (sub_buffer);
798 if (bytes_written == -1) {
799 return -1;
800 }
801 total_bytes += bytes_written;
802 mhlf = ipod_buffer_get_pointer (buffer);
803
804 num_children++;
805 /* Only update number of children when all went well to try
806 * to get something somewhat consistent when there are errors
807 */
808 mhlf->num_files = get_gint32 (num_children, buffer->byte_order);
809 }
810 dump_mhl ((MhlHeader *)mhlf, "mhlf");
811 g_list_free (formats);
812
813 return total_bytes;
814 }
815
816
817 static int
write_mhsd(Itdb_DB * db,iPodBuffer * buffer,enum MhsdType type)818 write_mhsd (Itdb_DB *db, iPodBuffer *buffer, enum MhsdType type)
819 {
820 ArtworkDB_MhsdHeader *mhsd;
821 unsigned int total_bytes;
822 int bytes_written;
823 iPodBuffer *sub_buffer;
824
825 g_assert (type >= MHSD_TYPE_MHLI);
826 g_assert (type <= MHSD_TYPE_MHLF);
827 mhsd = (ArtworkDB_MhsdHeader *)init_header (buffer, "mhsd", sizeof (ArtworkDB_MhsdHeader));
828 if (mhsd == NULL) {
829 return -1;
830 }
831 total_bytes = get_gint32 (mhsd->header_len, buffer->byte_order);
832 mhsd->total_len = get_gint32 (total_bytes, buffer->byte_order);
833 mhsd->index = get_gint16 (type, buffer->byte_order);
834 bytes_written = -1;
835
836 sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes);
837 if (sub_buffer == NULL) {
838 return -1;
839 }
840 switch (type) {
841 case MHSD_TYPE_MHLI:
842 bytes_written = write_mhli (db, sub_buffer);
843 break;
844 case MHSD_TYPE_MHLA:
845 bytes_written = write_mhla (db, sub_buffer);
846 break;
847 case MHSD_TYPE_MHLF:
848 bytes_written = write_mhlf (db, sub_buffer);
849 break;
850 }
851 ipod_buffer_destroy (sub_buffer);
852 if (bytes_written == -1) {
853 return -1;
854 } else {
855 total_bytes += bytes_written;
856 mhsd = ipod_buffer_get_pointer (buffer);
857 mhsd->total_len = get_gint32 (total_bytes, buffer->byte_order);
858 }
859
860 dump_mhsd (mhsd);
861
862 return total_bytes;
863 }
864
865 static int
write_mhfd(Itdb_DB * db,iPodBuffer * buffer,int id_max)866 write_mhfd (Itdb_DB *db, iPodBuffer *buffer, int id_max)
867 {
868 MhfdHeader *mhfd;
869 unsigned int total_bytes;
870 int bytes_written;
871 int i;
872
873
874 mhfd = (MhfdHeader *)init_header (buffer, "mhfd", sizeof (MhfdHeader));
875 if (mhfd == NULL) {
876 return -1;
877 }
878 total_bytes = get_gint32 (mhfd->header_len, buffer->byte_order);
879 mhfd->total_len = get_gint32 (total_bytes, buffer->byte_order);
880 switch (buffer->db_type) {
881 case DB_TYPE_PHOTO:
882 mhfd->unknown2 = get_gint32 (2, buffer->byte_order);
883 break;
884 case DB_TYPE_ITUNES:
885 mhfd->unknown2 = get_gint32 (2, buffer->byte_order);
886 break;
887 }
888 mhfd->next_id = get_gint32 (id_max, buffer->byte_order);
889 mhfd->unknown_flag1 = 2;
890 for (i = 1 ; i <= 3; i++) {
891 iPodBuffer *sub_buffer;
892
893 sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes);
894 if (sub_buffer == NULL) {
895 continue;
896 }
897 bytes_written = write_mhsd (db, sub_buffer, i);
898 ipod_buffer_destroy (sub_buffer);
899 if (bytes_written == -1) {
900 return -1;
901 }
902 total_bytes += bytes_written;
903 mhfd = ipod_buffer_get_pointer (buffer);
904 mhfd->total_len = get_gint32 (total_bytes, buffer->byte_order);
905 mhfd->num_children = get_gint32 (i, buffer->byte_order);
906 }
907
908 dump_mhfd (mhfd);
909
910 return total_bytes;
911 }
912
913 /* renumber the artwork IDs for all tracks containing artwork and with
914 an ID of != 0 */
915 /* if the iPod does not support sparse artwork, renumber consecutively
916 and all artwork */
917 /* returns the highest assigned ID or 0 if no IDs were used */
918 static guint32
ipod_artwork_db_set_ids(Itdb_iTunesDB * db)919 ipod_artwork_db_set_ids (Itdb_iTunesDB *db)
920 {
921 GList *gl;
922 const guint32 min_id = 0x64;
923 guint32 cur_id;
924
925 cur_id = min_id;
926
927 if (itdb_device_supports_sparse_artwork (db->device))
928 {
929 GHashTable *id_hash;
930
931 id_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
932
933 for (gl = db->tracks; gl != NULL; gl = gl->next)
934 {
935 Itdb_Track *song;
936 Itdb_Artwork *artwork;
937
938 song = gl->data;
939 g_return_val_if_fail (song, -1);
940
941 artwork = song->artwork;
942 g_return_val_if_fail (artwork, -1);
943
944 if (itdb_track_has_thumbnails (song) && (artwork->id != 0))
945 {
946 gpointer orig_key;
947 gpointer orig_val;
948
949 if (g_hash_table_lookup_extended (id_hash, GINT_TO_POINTER (artwork->id),
950 &orig_key, &orig_val))
951 { /* ID was encountered before */
952 artwork->id = GPOINTER_TO_INT (orig_val);
953 artwork->dbid = 0;
954 }
955 else
956 { /* first time we see this ID */
957 g_hash_table_insert (id_hash, GINT_TO_POINTER (artwork->id),
958 GINT_TO_POINTER (cur_id));
959 artwork->id = cur_id++;
960 artwork->dbid = song->dbid;
961 }
962 song->mhii_link = artwork->id;
963 }
964 else
965 {
966 song->mhii_link = 0;
967 }
968 }
969 g_hash_table_destroy (id_hash);
970 }
971 else
972 { /* iPod does not support sparse artwork -- just renumber */
973 for (gl = db->tracks; gl != NULL; gl = gl->next)
974 {
975 Itdb_Track *song;
976
977 song = gl->data;
978 g_return_val_if_fail (song, -1);
979 g_return_val_if_fail (song->artwork, -1);
980
981 song->mhii_link = 0;
982
983 if (itdb_track_has_thumbnails (song))
984 {
985 song->artwork->id = cur_id++;
986 song->artwork->dbid = song->dbid;
987 }
988 song->mhii_link = song->artwork->id;
989 }
990 }
991
992 if (cur_id == min_id)
993 return 0;
994 else
995 return cur_id-1;
996 }
997
998
999
1000 /* for all tracks with new artwork (id == 0) do the following:
1001 - try to identify if this artwork is identical to previous artwork
1002 (within the same album)
1003 - if no: assign new ID
1004 - if yes: assign same ID
1005
1006 Returns the highest ID used.
1007 */
1008 static guint32
ipod_artwork_mark_new_doubles(Itdb_iTunesDB * itdb,guint max_id)1009 ipod_artwork_mark_new_doubles (Itdb_iTunesDB *itdb, guint max_id)
1010 {
1011 GList *gl;
1012 GHashTable *hash_file, *hash_memory, *hash_pixbuf;
1013
1014 hash_file = g_hash_table_new_full (g_str_hash, g_str_equal,
1015 g_free, NULL);
1016 hash_memory = g_hash_table_new_full (g_str_hash, g_str_equal,
1017 g_free, NULL);
1018 hash_pixbuf = g_hash_table_new_full (g_str_hash, g_str_equal,
1019 g_free, NULL);
1020
1021 for (gl=itdb->tracks; gl; gl=gl->next)
1022 {
1023 Itdb_Artwork *artwork;
1024 Itdb_Track *track;
1025
1026 track= gl->data;
1027 g_return_val_if_fail (track, max_id);
1028 artwork = track->artwork;
1029 g_return_val_if_fail (artwork, max_id);
1030
1031 if ((artwork->id == 0) && itdb_track_has_thumbnails (track))
1032 {
1033 const gchar *checkstring;
1034 GHashTable *hash=NULL;
1035 gpointer orig_val = NULL;
1036 Itdb_Thumb *thumb = artwork->thumbnail;
1037 GChecksum *checksum = g_checksum_new (G_CHECKSUM_SHA1);
1038
1039 /* use the album name as part of the checksum */
1040 if (track->album && *track->album)
1041 {
1042 g_checksum_update (checksum,
1043 (guchar *)track->album, strlen (track->album));
1044 }
1045
1046 switch (thumb->data_type)
1047 {
1048 case ITDB_THUMB_TYPE_MEMORY:
1049 {
1050 Itdb_Thumb_Memory *mthumb = (Itdb_Thumb_Memory *)thumb;
1051 g_checksum_update (checksum,
1052 mthumb->image_data, mthumb->image_data_len);
1053 hash = hash_memory;
1054 break;
1055 }
1056 case ITDB_THUMB_TYPE_PIXBUF:
1057 {
1058 Itdb_Thumb_Pixbuf *pthumb = (Itdb_Thumb_Pixbuf *)thumb;
1059 g_return_val_if_fail (pthumb->pixbuf, max_id);
1060 g_checksum_update (checksum,
1061 gdk_pixbuf_get_pixels (pthumb->pixbuf),
1062 gdk_pixbuf_get_height (pthumb->pixbuf) * gdk_pixbuf_get_rowstride (pthumb->pixbuf));
1063 hash = hash_pixbuf;
1064 break;
1065 }
1066 case ITDB_THUMB_TYPE_FILE:
1067 {
1068 Itdb_Thumb_File *fthumb = (Itdb_Thumb_File *)thumb;
1069 g_return_val_if_fail (fthumb->filename, max_id);
1070 g_checksum_update (checksum,
1071 (guchar *)fthumb->filename,
1072 strlen (fthumb->filename));
1073 hash = hash_file;
1074 break;
1075 }
1076 case ITDB_THUMB_TYPE_INVALID:
1077 /* programming error */
1078 g_print ("encountered invalid thumb.\n");
1079 g_return_val_if_reached (max_id);
1080 break;
1081 case ITDB_THUMB_TYPE_IPOD:
1082 /* thumbs on the iPod are definitely not expected to
1083 have an ID of 0 */
1084 g_print ("encountered iPod thumb with ID = 0.\n");
1085 g_return_val_if_reached (max_id);
1086 break;
1087 }
1088
1089 checkstring = g_checksum_get_string (checksum);
1090 if (g_hash_table_lookup_extended (hash, checkstring, NULL, &orig_val))
1091 { /* same artwork was used before */
1092 Itdb_Artwork *previous_artwork = orig_val;
1093 artwork->id = previous_artwork->id;
1094 artwork->dbid = 0;
1095 }
1096 else
1097 { /* first occurence of this artwork */
1098 artwork->id = ++max_id;
1099 artwork->dbid = track->dbid;
1100 g_hash_table_insert (hash, g_strdup (checkstring), artwork);
1101 }
1102 track->mhii_link = artwork->id;
1103 g_checksum_free (checksum);
1104 }
1105 }
1106
1107 g_hash_table_destroy (hash_memory);
1108 g_hash_table_destroy (hash_file);
1109 g_hash_table_destroy (hash_pixbuf);
1110
1111 return max_id;
1112 }
1113
1114
1115 /* returns the highest ID used */
itdb_prepare_thumbnails(Itdb_iTunesDB * itdb)1116 static guint32 itdb_prepare_thumbnails (Itdb_iTunesDB *itdb)
1117 {
1118 gint max_id;
1119
1120 /* first renumber the old thumbnails to make sure we don't get too
1121 * high */
1122 max_id = ipod_artwork_db_set_ids (itdb);
1123
1124 if (itdb_device_supports_sparse_artwork (itdb->device))
1125 {
1126 /* go through all newly added artwork and pass out new IDs. the
1127 same ID will be assigned to identical artwork within one album */
1128 max_id = ipod_artwork_mark_new_doubles (itdb, max_id);
1129
1130 /* set the IDs again to make sure they are in the right order */
1131 max_id = ipod_artwork_db_set_ids (itdb);
1132 }
1133
1134 return max_id;
1135 }
1136
1137
1138
1139
1140 G_GNUC_INTERNAL int
ipod_write_artwork_db(Itdb_iTunesDB * itdb)1141 ipod_write_artwork_db (Itdb_iTunesDB *itdb)
1142 {
1143 iPodBuffer *buf;
1144 int bytes_written;
1145 char *filename;
1146 int id_max;
1147 Itdb_DB db;
1148 int status;
1149
1150 db.db_type = DB_TYPE_ITUNES;
1151 db.db.itdb = itdb;
1152
1153 id_max = itdb_prepare_thumbnails (itdb);
1154
1155 /* First, let's write the .ithmb files, this will create the
1156 * various thumbnails as well */
1157
1158 status = itdb_write_ithumb_files (&db);
1159 if (status != 0) {
1160 return -1;
1161 }
1162
1163 filename = ipod_db_get_artwork_db_path (itdb_get_mountpoint (itdb));
1164 if (filename == NULL) {
1165 /* FIXME: the iTunesDB will be inconsistent wrt artwork_count
1166 * it might be better to 0 out this field in all tracks
1167 * when we encounter an error
1168 */
1169 return -1;
1170 }
1171 buf = ipod_buffer_new (filename, itdb->device->byte_order, DB_TYPE_ITUNES);
1172 if (buf == NULL) {
1173 g_print ("Couldn't create %s\n", filename);
1174 g_free (filename);
1175 return -1;
1176 }
1177 bytes_written = write_mhfd (&db, buf, id_max);
1178
1179 /* Refcount of the shared buffer should drop to 0 and this should
1180 * sync buffered data to disk
1181 */
1182 ipod_buffer_destroy (buf);
1183
1184 if (bytes_written == -1) {
1185 g_print ("Failed to save %s\n", filename);
1186 /* FIXME: maybe should unlink the file we may have created */
1187 g_free (filename);
1188 return -1;
1189 }
1190 g_free (filename);
1191 return 0;
1192 }
1193
1194 int
ipod_write_photo_db(Itdb_PhotoDB * photodb)1195 ipod_write_photo_db (Itdb_PhotoDB *photodb)
1196 {
1197 iPodBuffer *buf;
1198 int bytes_written;
1199 char *filename;
1200 int id_max;
1201 Itdb_DB db;
1202 int status;
1203
1204 db.db_type = DB_TYPE_PHOTO;
1205 db.db.photodb = photodb;
1206
1207 filename = ipod_db_get_photos_db_path (db_get_mountpoint (&db));
1208
1209 status = itdb_write_ithumb_files (&db);
1210 if (status != 0) {
1211 return -1;
1212 }
1213
1214 if (filename == NULL) {
1215 return -1;
1216 }
1217 buf = ipod_buffer_new (filename, photodb->device->byte_order, DB_TYPE_PHOTO);
1218 if (buf == NULL) {
1219 g_print ("Couldn't create %s\n", filename);
1220 g_free (filename);
1221 return -1;
1222 }
1223 id_max = itdb_get_max_photo_id( photodb );
1224 bytes_written = write_mhfd (&db, buf, id_max+1);
1225
1226 /* Refcount of the shared buffer should drop to 0 and this should
1227 * sync buffered data to disk
1228 */
1229 ipod_buffer_destroy (buf);
1230
1231 if (bytes_written == -1) {
1232 g_print ("Failed to save %s\n", filename);
1233 /* FIXME: maybe should unlink the file we may have created */
1234 g_free (filename);
1235 return -1;
1236 }
1237 g_free (filename);
1238
1239 return 0;
1240 }
1241 #else
1242 G_GNUC_INTERNAL int
ipod_write_artwork_db(Itdb_iTunesDB * itdb)1243 ipod_write_artwork_db (Itdb_iTunesDB *itdb)
1244 {
1245 return -1;
1246 }
1247
1248 int
ipod_write_photo_db(Itdb_PhotoDB * photodb)1249 ipod_write_photo_db (Itdb_PhotoDB *photodb)
1250 {
1251 return -1;
1252 }
1253 #endif
1254