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