? Makefile
? Makefile.in
? aclocal.m4
? autom4te.cache
? compile
? config.guess
? config.h
? config.h.in
? config.log
? config.status
? config.sub
? configure
? db-artwork-debug.c
? db-artwork-debug.h
? depcomp
? install-sh
? interdiff.diff
? intltool-extract
? intltool-extract.in
? intltool-merge
? intltool-merge.in
? intltool-update
? intltool-update.in
? libgpod-1.0.pc
? libgpod-artworkdb-write-support-v2.diff
? libgpod-artworkdb-write-support-v3.diff
? libgpod-artworkdb-write-support-v4.diff
? libgpod-artworkdb-write-support.diff
? libgpod-v1-v2.diff
? libtool
? ltmain.sh
? missing
? stamp-h1
? po/Makefile
? po/Makefile.in
? po/POTFILES
? po/de.gmo
? po/fr.gmo
? po/he.gmo
? po/it.gmo
? po/ja.gmo
? po/sv.gmo
? src/.deps
? src/.libs
? src/Makefile
? src/Makefile.in
? src/db-artwork-debug.lo
? src/db-artwork-parser.lo
? src/db-artwork-writer.lo
? src/db-image-parser.lo
? src/db-parse-context.lo
? src/itdb_itunesdb.lo
? src/itdb_playlist.lo
? src/itdb_track.lo
? src/ithumb-writer.lo
? src/libgpod.la
? tests/.deps
? tests/.libs
? tests/Makefile
? tests/Makefile.in
? tests/test-itdb
? tests/test-thumbnails
? tests/test-write-thumbnails
Index: src/Makefile.am
===================================================================
RCS file: /cvsroot/gtkpod/libgpod/src/Makefile.am,v
retrieving revision 1.2
diff -p -u -r1.2 Makefile.am
--- src/Makefile.am	19 Sep 2005 10:30:50 -0000	1.2
+++ src/Makefile.am	22 Sep 2005 07:42:32 -0000
@@ -1,4 +1,9 @@
 lib_LTLIBRARIES = libgpod.la
+if HAVE_GDKPIXBUF
+ARTWORKDB_WRITER_FILES=	db-artwork-writer.c ithumb-writer.c
+else
+ARTWORKDB_WRITER_FILES=
+endif
 
 libgpod_la_SOURCES = \
 	itdb.h           \
@@ -13,7 +18,8 @@ libgpod_la_SOURCES = \
 	db-artwork-debug.c  \
 	db-artwork-debug.h  \
 	db-image-parser.c   \
-	db-image-parser.h
+	db-image-parser.h   \
+	$(ARTWORKDB_WRITER_FILES)
 
 
 libgpod_la_headers = itdb.h
@@ -24,5 +30,5 @@ libgpod_la_LDFLAGS = -version-info $(LIB
 libgpodincludedir = $(includedir)/gpod-1.0/gpod
 libgpodinclude_HEADERS = $(libgpod_la_headers)
 
-INCLUDES=$(LIBGPOD_CFLAGS)
-LIBS=$(LIBGPOD_LIBS)
+INCLUDES=$(LIBGPOD_CFLAGS) $(GDKPIXBUF_CFLAGS)
+LIBS=$(LIBGPOD_LIBS) $(GDKPIXBUF_LIBS) 
Index: src/db-artwork-parser.c
===================================================================
RCS file: /cvsroot/gtkpod/libgpod/src/db-artwork-parser.c,v
retrieving revision 1.1
diff -p -u -r1.1 db-artwork-parser.c
--- src/db-artwork-parser.c	19 Sep 2005 10:30:50 -0000	1.1
+++ src/db-artwork-parser.c	22 Sep 2005 07:42:32 -0000
@@ -28,7 +28,6 @@
 #include "db-image-parser.h"
 #include "db-itunes-parser.h"
 #include "db-parse-context.h"
-/*#include "image-parser.h"*/
 
 typedef int (*ParseListItem)(DBParseContext *ctx, Itdb_iTunesDB *db, GError *error);
 
@@ -89,15 +88,11 @@ parse_mhod_3 (DBParseContext *ctx, GErro
 }
 #endif
 
-#define FULL_THUMB_SIDE_LEN 0x8c
-#define NOW_PLAYING_THUMB_SIDE_LEN 0x38
-
 static int
 parse_mhni (DBParseContext *ctx, iPodSong *song, GError *error)
 {
 	MhniHeader *mhni;
-	int width;
-	int height;
+	Itdb_Image *thumb;
 
 	mhni = db_parse_context_get_m_header (ctx, MhniHeader, "mhni");
 	if (mhni == NULL) {
@@ -121,16 +116,9 @@ parse_mhni (DBParseContext *ctx, iPodSon
 		g_free (mhod_ctx);
 	}
 #else
-	width = (GINT_FROM_LE (mhni->image_dimensions) & 0xffff0000) >> 16;
-	height = (GINT_FROM_LE (mhni->image_dimensions) & 0x0000ffff);
-	
-	if ((width == FULL_THUMB_SIDE_LEN) || (width == FULL_THUMB_SIDE_LEN)) {
-		song->full_size_thumbnail = ipod_image_new_from_mhni (mhni, song->itdb->mountpoint);
-	} else if ((width == NOW_PLAYING_THUMB_SIDE_LEN) || (width == NOW_PLAYING_THUMB_SIDE_LEN)) {
-		song->now_playing_thumbnail = ipod_image_new_from_mhni (mhni, song->itdb->mountpoint);
-	} else {
-		g_print ("Unrecognized image size: %08x\n", 
-			 GINT_FROM_LE (mhni->image_dimensions));
+	thumb = ipod_image_new_from_mhni (mhni, song->itdb->mountpoint);
+	if (thumb != NULL) {
+		song->thumbnails = g_list_append (song->thumbnails, thumb);
 	}
 #endif
 	return 0;
@@ -189,7 +177,12 @@ parse_mhii (DBParseContext *ctx, Itdb_iT
 		return -1;
 	}
 
-	song->orig_image = ipod_image_new_from_mhii (mhii);
+	if (song->artwork_size != GINT_FROM_LE (mhii->orig_img_size)-1) {
+		g_warning ("iTunesDB and ArtworkDB artwork sizes don't match (%d %d)", song->artwork_size , GINT_FROM_LE (mhii->orig_img_size));
+	}
+
+	song->artwork_size = GINT_FROM_LE (mhii->orig_img_size)-1;
+	song->image_id = GINT_FROM_LE (mhii->image_id);
 #endif
 
 	cur_offset = ctx->header_len;
@@ -336,21 +329,20 @@ parse_mhfd (DBParseContext *ctx, Itdb_iT
 
 
 G_GNUC_INTERNAL char *
-ipod_db_get_artwork_db_path (Itdb_iTunesDB *db)
+ipod_db_get_artwork_db_path (const char *mount_point)
 {
-	return g_build_filename (G_DIR_SEPARATOR_S, db->mountpoint,
-				 "iPod_Control", "Artwork", "ArtworkDB", 
-				 NULL);
+	const char *paths[] = {"iPod_Control", "Artwork", "ArtworkDB", NULL};
+	return itdb_resolve_path (mount_point, paths);
 }
 
 
 static char *
 ipod_db_get_photo_db_path (const char *mount_point)
 {
+	const char *paths[] = {"Photos", "Photo Database", NULL};
 	g_return_val_if_fail (mount_point != NULL, NULL);
-	return  g_build_filename (G_DIR_SEPARATOR_S, mount_point,
-				  "Photos", "Photo Database",
-				  NULL);
+	return itdb_resolve_path (mount_point, paths);
+				  
 }
 
 
@@ -360,7 +352,7 @@ ipod_parse_artwork_db (Itdb_iTunesDB *db
 	DBParseContext *ctx;
 	char *filename;
 
-	filename = ipod_db_get_artwork_db_path (db);
+	filename = ipod_db_get_artwork_db_path (db->mountpoint);
 	ctx = db_parse_context_new_from_file (filename);	
 	g_free (filename);
 	if (ctx == NULL) {
@@ -368,11 +360,13 @@ ipod_parse_artwork_db (Itdb_iTunesDB *db
 	}	
 
 	parse_mhfd (ctx, db, NULL);
-	g_free (ctx);
+	db_parse_context_destroy (ctx, TRUE);
 	return 0;
 
  error:
-	/* FIXME: needs to destroy ctx and to release the mmap'ed memory*/
+	if (ctx != NULL) {
+		db_parse_context_destroy (ctx, TRUE);
+	}
 	return -1;
 }
 
@@ -393,6 +387,7 @@ ipod_parse_photo_db (const char *mount_p
 	}
 
 	parse_mhfd (ctx, NULL, NULL);
-	g_free (ctx);
+	db_parse_context_destroy (ctx, TRUE);
+
 	return 0;
 }
Index: src/db-artwork-parser.h
===================================================================
RCS file: /cvsroot/gtkpod/libgpod/src/db-artwork-parser.h,v
retrieving revision 1.1
diff -p -u -r1.1 db-artwork-parser.h
--- src/db-artwork-parser.h	19 Sep 2005 10:30:50 -0000	1.1
+++ src/db-artwork-parser.h	22 Sep 2005 07:42:32 -0000
@@ -30,10 +30,13 @@
 
 #define iPodSong Itdb_Track
 
-int ipod_parse_photo_db (const char *filename);
-int ipod_parse_artwork_db (Itdb_iTunesDB *db);
-int ipod_write_artwork_db (Itdb_iTunesDB *db);
+#define IPOD_THUMBNAIL_FULL_SIZE_CORRELATION_ID 1016
+#define IPOD_THUMBNAIL_NOW_PLAYING_CORRELATION_ID 1017
 
-G_GNUC_INTERNAL char *ipod_db_get_artwork_db_path (Itdb_iTunesDB *db);
+G_GNUC_INTERNAL int ipod_parse_photo_db (const char *filename);
+G_GNUC_INTERNAL int ipod_parse_artwork_db (Itdb_iTunesDB *db);
+G_GNUC_INTERNAL int ipod_write_artwork_db (Itdb_iTunesDB *db, 
+					   const char *mount_point);
 
+G_GNUC_INTERNAL char *ipod_db_get_artwork_db_path (const char *mount_point);
 #endif
Index: src/db-artwork-writer.c
===================================================================
RCS file: src/db-artwork-writer.c
diff -N src/db-artwork-writer.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/db-artwork-writer.c	22 Sep 2005 07:42:33 -0000
@@ -0,0 +1,769 @@
+/*
+ *  Copyright (C) 2005 Christophe Fergeau
+ *
+ * 
+ *  The code contained in this file is free software; you can redistribute
+ *  it and/or modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either version
+ *  2.1 of the License, or (at your option) any later version.
+ *  
+ *  This file is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this code; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ *  iTunes and iPod are trademarks of Apple
+ * 
+ *  This product is not supported/written/published by Apple!
+ *
+ */
+
+#include <config.h>
+
+#include "db-artwork-debug.h"
+#include "db-artwork-parser.h"
+#include "db-itunes-parser.h"
+#include "db-image-parser.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#define IPOD_MMAP_SIZE 2 * 1024 * 1024
+
+struct iPodMmapBuffer {
+	int fd;
+	void *mmap_area;
+	size_t size;
+	int ref_count;
+};
+
+struct _iPodBuffer {
+	struct iPodMmapBuffer *mmap;
+	off_t offset;
+};
+
+typedef struct _iPodBuffer iPodBuffer;
+
+static void
+ipod_mmap_buffer_destroy (struct iPodMmapBuffer *buf)
+{
+	munmap (buf->mmap_area, buf->size);
+	close (buf->fd);
+	g_free (buf);
+}
+
+static void
+ipod_buffer_destroy (iPodBuffer *buffer)
+{
+	buffer->mmap->ref_count--;
+	if (buffer->mmap->ref_count == 0) {
+		g_print ("Destroying mmap buffer\n");
+		ipod_mmap_buffer_destroy (buffer->mmap);
+	}
+	g_free (buffer);
+}
+
+static void *
+ipod_buffer_get_pointer (iPodBuffer *buffer) 
+{
+	return &((unsigned char *)buffer->mmap->mmap_area)[buffer->offset];
+}
+
+static int
+ipod_buffer_grow_file (struct iPodMmapBuffer *mmap_buf, off_t new_size)
+{
+	int result;
+
+	result = lseek (mmap_buf->fd, new_size, SEEK_SET);
+	if (result == (off_t)-1) {
+		g_print ("Failed to grow file to %lu: %s\n", 
+			 new_size, strerror (errno));
+		return -1;
+	}
+	result = 0;
+	result = write (mmap_buf->fd, &result, 1);
+	if (result != 1) {
+		g_print ("Failed to write a byte at %lu: %s\n", 
+			 new_size, strerror (errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+ipod_buffer_maybe_grow (iPodBuffer *buffer, off_t offset)
+{
+	void *new_address;
+
+	if (buffer->offset + offset <= buffer->mmap->size) {
+		return 0;
+	}
+
+	/* Don't allow libc to move the current mapping since this would
+	 * force us to be very careful wrt pointers in the rest of the code
+	 */
+	new_address = mremap (buffer->mmap->mmap_area, buffer->mmap->size,
+			      buffer->mmap->size + IPOD_MMAP_SIZE, 0);
+	if (new_address == MAP_FAILED) {
+		g_print ("Failed to mremap buffer: %s\n", strerror (errno));
+		return -1;
+	}
+	if (ipod_buffer_grow_file (buffer->mmap, 
+				   buffer->mmap->size + IPOD_MMAP_SIZE) != 0) {
+		return -1;
+	}
+	buffer->mmap->size += IPOD_MMAP_SIZE;
+
+	return 0;
+}
+
+static iPodBuffer *
+ipod_buffer_get_sub_buffer (iPodBuffer *buffer, off_t offset)
+{
+	iPodBuffer *sub_buffer;
+
+	if (ipod_buffer_maybe_grow (buffer, offset) != 0) {
+		return NULL;
+	}
+	sub_buffer = g_new0 (iPodBuffer, 1);
+	if (sub_buffer == NULL) {
+		return NULL;
+	}
+	sub_buffer->mmap = buffer->mmap;
+	sub_buffer->offset = buffer->offset + offset;
+
+	buffer->mmap->ref_count++;
+
+	return sub_buffer;
+}
+
+static iPodBuffer *
+ipod_buffer_new (const char *filename)
+{
+	int fd;
+	struct iPodMmapBuffer *mmap_buf;
+	iPodBuffer *buffer;
+	void *mmap_area;
+
+	fd = open (filename, O_RDWR | O_CREAT | O_TRUNC, 
+		   S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+	if (fd == -1) {
+		g_print ("Failed to open %s: %s\n", 
+			 filename, strerror (errno));
+		return NULL;
+	}
+	
+	mmap_area = mmap (0, IPOD_MMAP_SIZE, PROT_READ | PROT_WRITE,
+			  MAP_SHARED, fd, 0);
+	if (mmap_area == MAP_FAILED) {
+		g_print ("Failed to mmap %s in memory: %s\n", filename, 
+			 strerror (errno));
+		close (fd);
+		return NULL;
+	}
+	mmap_buf = g_new0 (struct iPodMmapBuffer, 1);
+	if (mmap_buf == NULL) {
+		munmap (mmap_area, IPOD_MMAP_SIZE);
+		close (fd);
+		return NULL;
+	}
+	mmap_buf->mmap_area = mmap_area;
+	mmap_buf->size = IPOD_MMAP_SIZE;
+	mmap_buf->ref_count = 1;
+	mmap_buf->fd = fd;
+
+	if (ipod_buffer_grow_file (mmap_buf, IPOD_MMAP_SIZE) != 0) {
+		ipod_mmap_buffer_destroy (mmap_buf);
+		return  NULL;
+	}
+
+	buffer = g_new0 (iPodBuffer, 1);
+	if (buffer == NULL) {
+		ipod_mmap_buffer_destroy (mmap_buf);
+		return NULL;
+	}
+	buffer->mmap = mmap_buf;
+
+	return buffer;
+}
+
+enum MhsdType {
+	MHSD_TYPE_MHLI = 1,
+	MHSD_TYPE_MHLA = 2,
+	MHSD_TYPE_MHLF = 3
+};
+
+enum iPodThumbnailType {
+	IPOD_THUMBNAIL_FULL_SIZE,
+	IPOD_THUMBNAIL_NOW_PLAYING
+};
+
+#define IPOD_THUMBNAIL_FULL_SIZE_SIZE (140*140*2)
+#define IPOD_THUMBNAIL_NOW_PLAYING_SIZE (56*56*2)
+
+
+#define RETURN_SIZE_FOR(id, size) if (strncmp (id, header_id, 4) == 0) return (size)
+
+/* Returns the "real" size for a header, ie the size iTunes uses for it 
+ * (padding included)
+ */
+static int
+get_padded_header_size (gchar header_id[4])
+{
+	RETURN_SIZE_FOR ("mhni", 0x4c);
+	RETURN_SIZE_FOR ("mhii", 0x98);
+	RETURN_SIZE_FOR ("mhsd", 0x60);
+	RETURN_SIZE_FOR ("mhfd", 0x84);
+	RETURN_SIZE_FOR ("mhli", 0x5c);
+	RETURN_SIZE_FOR ("mhla", 0x5c);
+	RETURN_SIZE_FOR ("mhlf", 0x5c);
+	RETURN_SIZE_FOR ("mhif", 0x7c);
+
+	return 0;
+}
+
+static void *
+init_header (iPodBuffer *buffer, gchar header_id[4], guint header_len)
+{
+	MHeader *mh;
+	int padded_size;
+
+	padded_size = get_padded_header_size (header_id);
+	if (padded_size != 0) {
+		header_len = padded_size;
+	}
+	g_assert (header_len > sizeof (MHeader));
+	if (ipod_buffer_maybe_grow (buffer, header_len) != 0) {
+		return NULL;
+	}
+	mh = (MHeader*)ipod_buffer_get_pointer (buffer);
+	memset (mh, 0, header_len);
+	strncpy ((char *)mh->header_id, header_id, 4);
+	mh->header_len = GINT_TO_LE (header_len);
+	
+	return mh;
+}
+
+static int 
+write_mhod_type_3 (enum iPodThumbnailType type, iPodBuffer *buffer)
+{
+	MhodHeaderArtworkType3 *mhod;
+	unsigned int total_bytes;
+	char *filename;
+	int len;
+	gunichar2 *utf16;
+	int i;
+	mhod = (MhodHeaderArtworkType3 *)init_header (buffer, "mhod", 
+						      sizeof (MhodHeaderArtworkType3));
+	if (mhod == NULL) {
+		return -1;
+	}
+	
+	total_bytes = sizeof (MhodHeaderArtworkType3);
+	mhod->total_len = GINT_TO_LE (total_bytes);
+	/* Modify header length, since iTunes only puts the length of 
+	 * MhodHeader in header_len
+	 */
+	mhod->header_len = GINT_TO_LE (sizeof (MhodHeader));
+	mhod->type = GINT_TO_LE (3);
+	mhod->mhod_version = GINT_TO_LE (2);
+	switch (type) {
+	case ITDB_IMAGE_FULL_SCREEN: 
+		filename = g_strdup_printf (":F%04u_1.ithmb",
+					    IPOD_THUMBNAIL_FULL_SIZE_CORRELATION_ID);
+		break;
+	case ITDB_IMAGE_NOW_PLAYING: 
+		filename = g_strdup_printf (":F%04u_1.ithmb",
+					    IPOD_THUMBNAIL_NOW_PLAYING_CORRELATION_ID);
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+
+	len = strlen (filename);
+
+	/* number of bytes of the string encoded in UTF-16 */
+	mhod->string_len = GINT_TO_LE (2*len);
+
+	/* Make sure we have enough free space to write the string */
+	if (ipod_buffer_maybe_grow (buffer, total_bytes + 2*len) != 0) {
+		g_free (filename);
+		return  -1;
+	}
+	utf16 = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
+	g_free (filename);
+	if (utf16 == NULL) {		
+		return -1;
+	}
+	memcpy (mhod->string, utf16, 2*len);
+	g_free (utf16);
+	for (i = 0; i < len; i++) {
+		mhod->string[i] = GINT_TO_LE (mhod->string[i]);
+	}
+	total_bytes += 2*len;
+	mhod->total_len = GINT_TO_LE (total_bytes);	
+
+	dump_mhod_type_3 (mhod);
+
+	return total_bytes;
+}
+
+static int 
+write_mhni (Itdb_Image *image, iPodBuffer *buffer)
+{
+	MhniHeader *mhni;
+	unsigned int total_bytes;
+	int bytes_written;
+	iPodBuffer *sub_buffer;
+	unsigned int dim;
+
+	if (image == NULL) {
+		return -1;
+	}
+
+	mhni = (MhniHeader *)init_header (buffer, "mhni", 
+					  sizeof (MhniHeader));
+	if (mhni == NULL) {
+		return -1;
+	}
+	total_bytes = GINT_FROM_LE (mhni->header_len);
+	mhni->total_len = GINT_TO_LE (total_bytes);
+
+
+	switch (image->type) {
+	case ITDB_IMAGE_NOW_PLAYING:
+		mhni->correlation_id = GINT_TO_LE (IPOD_THUMBNAIL_NOW_PLAYING_CORRELATION_ID);
+		break;
+	case ITDB_IMAGE_FULL_SCREEN:
+		mhni->correlation_id = GINT_TO_LE (IPOD_THUMBNAIL_FULL_SIZE_CORRELATION_ID);
+		break;
+	}
+	dim = image->width & 0xffff;
+	dim <<= 16;
+	dim |= image->height & 0xffff;
+	mhni->image_dimensions = GINT_TO_LE (dim);
+	mhni->image_size = GINT_TO_LE (image->size);
+	mhni->ithmb_offset = GINT_TO_LE (image->offset);
+
+	sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes);
+	if (sub_buffer == NULL) {
+		return  -1;
+	}
+	bytes_written = write_mhod_type_3 (image->type, sub_buffer);
+	ipod_buffer_destroy (sub_buffer);
+	if (bytes_written == -1) {
+		return -1;
+	}
+	total_bytes += bytes_written;
+	mhni->total_len = GINT_TO_LE (total_bytes);
+	/* Only update number of children when all went well to try to get
+	 * something somewhat consistent when there are errors 
+	 */
+	mhni->num_children = GINT_TO_LE (1);
+	
+	dump_mhni (mhni);
+
+	return total_bytes;
+}
+
+static int
+write_mhod (Itdb_Image *image, iPodBuffer *buffer)
+{
+	MhodHeader *mhod;
+	unsigned int total_bytes;
+	int bytes_written;
+	iPodBuffer *sub_buffer;
+
+	if (image == NULL) {
+		return -1;
+	}
+
+	mhod = (MhodHeader *)init_header (buffer, "mhod", 
+					  sizeof (MhodHeader));
+	if (mhod == NULL) {
+		return -1;
+	}
+	total_bytes = sizeof (MhodHeader);
+	mhod->total_len = GINT_TO_LE (total_bytes);
+	mhod->type = GINT_TO_LE (MHOD_TYPE_LOCATION);
+	sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes);
+	if (sub_buffer == NULL) {
+		return -1;
+	}
+	bytes_written = write_mhni (image, sub_buffer);
+	ipod_buffer_destroy (sub_buffer);
+	if (bytes_written == -1) {
+		return -1;
+	}
+	total_bytes += bytes_written;
+	mhod->total_len = GINT_TO_LE (total_bytes);
+
+	dump_mhod (mhod);
+
+	return total_bytes;
+}
+
+static int
+write_mhii (Itdb_Track *song, iPodBuffer *buffer)
+{
+	MhiiHeader *mhii;
+	unsigned int total_bytes;
+	int bytes_written;
+	int num_children;
+	GList *it;
+
+	mhii = (MhiiHeader *)init_header (buffer, "mhii", sizeof (MhiiHeader));
+	if (mhii == NULL) {
+		return -1;
+	}
+	total_bytes = GINT_FROM_LE (mhii->header_len);
+	mhii->song_id = GINT64_TO_LE (song->dbid);
+	mhii->image_id = GUINT_TO_LE (song->image_id);
+	/* Adding 1 to artwork_size since this is what iTunes 4.9 does (there
+	 * is a 1 difference between the artwork size in iTunesDB and the 
+	 * artwork size in ArtworkDB)
+	 */
+	mhii->orig_img_size = GINT_TO_LE (song->artwork_size)+1;
+	num_children = 0;
+	for (it = song->thumbnails; it != NULL; it = it->next) {
+		iPodBuffer *sub_buffer;
+		Itdb_Image *thumb;
+		
+		mhii->num_children = GINT_TO_LE (num_children);
+		mhii->total_len = GINT_TO_LE (total_bytes);
+		sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes);
+		if (sub_buffer == NULL) {
+			return -1;
+		}
+		thumb = (Itdb_Image *)it->data;
+		bytes_written = write_mhod (thumb, sub_buffer);
+		ipod_buffer_destroy (sub_buffer);
+		if (bytes_written == -1) {
+			return -1;
+		}
+		total_bytes += bytes_written;
+		num_children++;
+	}
+
+	mhii->num_children = GINT_TO_LE (num_children);
+	mhii->total_len = GINT_TO_LE (total_bytes);
+
+	dump_mhii (mhii);
+
+	return total_bytes;
+}
+
+static int
+write_mhli (Itdb_iTunesDB *db, iPodBuffer *buffer)
+{
+	GList *it;
+	MhliHeader *mhli;
+	unsigned int total_bytes;
+	int num_thumbs;
+
+	mhli = (MhliHeader *)init_header (buffer, "mhli", sizeof (MhliHeader));
+	if (mhli == NULL) {
+		return -1;
+	}
+
+	num_thumbs = 0;
+	total_bytes = GINT_FROM_LE (mhli->header_len);
+	for (it = db->tracks; it != NULL; it = it->next) {
+		Itdb_Track *song;
+		int bytes_written;
+		iPodBuffer *sub_buffer;
+
+		song = (Itdb_Track*)it->data;
+		if (song->image_id == 0) {
+			continue;
+		}
+		sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes);
+		if (sub_buffer == NULL) {
+			continue;
+		}
+		bytes_written = write_mhii (song, sub_buffer);
+		ipod_buffer_destroy (sub_buffer);
+		if (bytes_written != -1) {
+			num_thumbs++;
+			total_bytes += bytes_written;
+		}
+	}
+
+	mhli->num_children = GINT_TO_LE (num_thumbs);
+
+	dump_mhl ((MhlHeader *)mhli, "mhli");
+
+	return total_bytes;
+}
+
+static int
+write_mhla (Itdb_iTunesDB *db, iPodBuffer *buffer)
+{
+	MhlaHeader *mhla;
+
+	mhla = (MhlaHeader *)init_header (buffer, "mhla", sizeof (MhlaHeader));
+	if (mhla == NULL) {
+		return -1;
+	}
+	
+	dump_mhl ((MhlHeader *)mhla, "mhla");
+
+	return GINT_FROM_LE (mhla->header_len);
+}
+
+static int
+write_mhif (Itdb_iTunesDB *db, iPodBuffer *buffer, enum iPodThumbnailType type)
+{
+	MhifHeader *mhif;
+
+	mhif = (MhifHeader *)init_header (buffer, "mhif", sizeof (MhifHeader));
+	if (mhif == NULL) {
+		return -1;
+	}
+	mhif->total_len = mhif->header_len;
+	switch (type) {
+	case ITDB_IMAGE_FULL_SCREEN:
+		mhif->correlation_id = GINT_TO_LE (IPOD_THUMBNAIL_FULL_SIZE_CORRELATION_ID);
+		mhif->image_size = GINT_TO_LE (IPOD_THUMBNAIL_FULL_SIZE_SIZE);
+		break;
+	case ITDB_IMAGE_NOW_PLAYING:
+		mhif->correlation_id = GINT_TO_LE (IPOD_THUMBNAIL_NOW_PLAYING_CORRELATION_ID);
+		mhif->image_size = GINT_TO_LE (IPOD_THUMBNAIL_NOW_PLAYING_SIZE);
+		break;
+	}
+
+	dump_mhif (mhif);
+	
+	return GINT_FROM_LE (mhif->header_len);
+}
+
+
+static int
+write_mhlf (Itdb_iTunesDB *db, iPodBuffer *buffer)
+{
+	MhlfHeader *mhlf;
+	unsigned int total_bytes;
+	int bytes_written;
+	iPodBuffer *sub_buffer;
+
+	mhlf = (MhlfHeader *)init_header (buffer, "mhlf", sizeof (MhlfHeader));
+	if (mhlf == NULL) {
+		return -1;
+	}
+
+	total_bytes = GINT_FROM_LE (mhlf->header_len);
+
+	sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes);
+	if (sub_buffer == NULL) {
+		return -1;
+	}
+	bytes_written = write_mhif (db, sub_buffer, IPOD_THUMBNAIL_FULL_SIZE);
+	ipod_buffer_destroy (sub_buffer);
+	if (bytes_written == -1) {
+		return -1;
+	} 
+	total_bytes += bytes_written;
+	
+	/* Only update number of children when all went well to try to get
+	 * something somewhat consistent when there are errors 
+	 */
+	/* For the ArworkDB file, there are only 2 physical file storing
+	 * thumbnails: F1016_1.ithmb for the bigger thumbnails (39200 bytes)
+	 * and F1017_1.ithmb for the 'now playing' thumbnails (6272)
+	 */
+	mhlf->num_files = GINT_TO_LE (1);
+
+	sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes);
+	if (sub_buffer == NULL) {
+		return -1;
+	}
+	bytes_written = write_mhif (db, sub_buffer, IPOD_THUMBNAIL_NOW_PLAYING);
+	ipod_buffer_destroy (sub_buffer);
+	if (bytes_written == -1) {
+		return -1;
+	} 
+	total_bytes += bytes_written;
+
+	/* Only update number of children when all went well to try to get
+	 * something somewhat consistent when there are errors 
+	 */
+	/* For the ArworkDB file, there are only 2 physical file storing
+	 * thumbnails: F1016_1.ithmb for the bigger thumbnails (39200 bytes)
+	 * and F1017_1.ithmb for the 'now playing' thumbnails (6272)
+	 */
+	mhlf->num_files = GINT_TO_LE (2);
+
+	dump_mhl ((MhlHeader *)mhlf, "mhlf");
+
+	return total_bytes;
+}
+
+
+static int 
+write_mhsd (Itdb_iTunesDB *db, iPodBuffer *buffer, enum MhsdType type)
+{
+	MhsdHeader *mhsd;
+	unsigned int total_bytes;
+	int bytes_written;
+	iPodBuffer *sub_buffer;
+
+	g_assert (type >= MHSD_TYPE_MHLI);
+	g_assert (type <= MHSD_TYPE_MHLF);
+	mhsd = (MhsdHeader *)init_header (buffer, "mhsd", sizeof (MhsdHeader));
+	if (mhsd == NULL) {
+		return -1;
+	}
+	total_bytes = GINT_FROM_LE (mhsd->header_len);
+	mhsd->total_len = GINT_TO_LE (total_bytes);
+	mhsd->index = GINT_TO_LE (type);
+	bytes_written = -1;
+
+	sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes);
+	if (sub_buffer == NULL) {
+		return -1;
+	}
+	switch (type) {
+	case MHSD_TYPE_MHLI:
+		bytes_written = write_mhli (db, sub_buffer);
+		break;
+	case MHSD_TYPE_MHLA:
+		bytes_written = write_mhla (db, sub_buffer);
+		break;
+	case MHSD_TYPE_MHLF:
+		bytes_written = write_mhlf (db, sub_buffer);
+		break;
+	}
+	ipod_buffer_destroy (sub_buffer);
+	if (bytes_written == -1) {
+		return -1;
+	} else {
+		total_bytes += bytes_written;
+		mhsd->total_len = GINT_TO_LE (total_bytes);
+	}
+
+	dump_mhsd (mhsd);
+
+	return total_bytes;
+}
+
+static int
+write_mhfd (Itdb_iTunesDB *db, iPodBuffer *buffer, int id_max)
+{
+	MhfdHeader *mhfd;
+	unsigned int total_bytes;
+	int bytes_written;
+	int i;
+
+	mhfd = (MhfdHeader *)init_header (buffer, "mhfd", sizeof (MhfdHeader));
+	if (mhfd == NULL) {
+		return -1;
+	}
+	total_bytes = GINT_FROM_LE (mhfd->header_len);
+	mhfd->total_len = GINT_TO_LE (total_bytes);
+	mhfd->unknown2 = GINT_TO_LE (1);
+	mhfd->unknown4 = GINT_TO_LE (id_max);
+	mhfd->unknown7 = GINT_TO_LE (2);
+	for (i = 1 ; i <= 3; i++) {
+		iPodBuffer *sub_buffer;
+
+		sub_buffer = ipod_buffer_get_sub_buffer (buffer, total_bytes);
+		if (sub_buffer == NULL) {
+			continue;
+		}
+		bytes_written = write_mhsd (db, sub_buffer, i);
+		ipod_buffer_destroy (sub_buffer);
+		if (bytes_written == -1) {			
+			return -1;
+		}
+		total_bytes += bytes_written;
+		mhfd->total_len = GINT_TO_LE (total_bytes);
+		mhfd->num_children = GINT_TO_LE (i);
+	}
+
+	dump_mhfd (mhfd);
+
+	return total_bytes;
+}
+
+static unsigned int
+ipod_artwork_db_set_ids (Itdb_iTunesDB *db)
+{
+	GList *it;
+	unsigned int id;
+	
+	id = 64;
+	for (it = db->tracks; it != NULL; it = it->next) {
+		Itdb_Track *song;
+
+		song = (Itdb_Track *)it->data;
+		if (song->thumbnails != NULL) {
+			song->image_id = id;
+			id++;
+		}
+	}
+	
+	return id;
+}
+
+int
+ipod_write_artwork_db (Itdb_iTunesDB *db, const char *mount_point)
+{
+	iPodBuffer *buf;
+	int bytes_written;
+	int result;
+	char *filename;
+	int id_max;
+
+	/* First, let's write the .ithmb files, this will create the various 
+	 * thumbnails as well, and update the Itdb_Track items contained in
+	 * the database appropriately (ie set the 'artwork_count' and 
+	 * 'artwork_size' fields, as well as the 2 Itdb_Image fields
+	 */
+	itdb_write_ithumb_files (db, mount_point);
+	g_print ("%s\n", G_GNUC_FUNCTION);
+	/* Now we can update the ArtworkDB file */
+	id_max = ipod_artwork_db_set_ids (db);
+
+	filename = ipod_db_get_artwork_db_path (mount_point);
+	buf = ipod_buffer_new (filename);
+	if (buf == NULL) {
+		g_print ("Couldn't create %s\n", filename);
+		g_free (filename);
+		return -1;
+	}
+	bytes_written = write_mhfd (db, buf, id_max);
+
+	/* Refcount of the mmap buffer should drop to 0 and this should
+	 * sync buffered data to disk
+	 * FIXME: it's probably less error-prone to add a ipod_buffer_mmap_sync
+	 * call...
+	 */
+	ipod_buffer_destroy (buf);
+
+	if (bytes_written == -1) {
+		g_print ("Failed to save %s\n", filename);
+		g_free (filename);
+		/* FIXME: maybe should unlink the file we may have created */
+		return -1;
+	}
+
+	result = truncate (filename, bytes_written);
+	if (result != 0) {
+		g_print ("Failed to truncate %s: %s\n", 
+			 filename, strerror (errno));
+		g_free (filename);	
+		return -1;
+	}
+	g_free (filename);
+	return 0;
+}
Index: src/db-image-parser.c
===================================================================
RCS file: /cvsroot/gtkpod/libgpod/src/db-image-parser.c,v
retrieving revision 1.1
diff -p -u -r1.1 db-image-parser.c
--- src/db-image-parser.c	19 Sep 2005 10:30:50 -0000	1.1
+++ src/db-image-parser.c	22 Sep 2005 07:42:33 -0000
@@ -29,21 +29,9 @@
 #include <glib.h>
 #include <glib-object.h>
 
+#include "db-artwork-parser.h"
 #include "db-image-parser.h"
 
-#define RED_BITS   5
-#define RED_SHIFT 11
-#define RED_MASK  (((1 << RED_BITS)-1) << RED_SHIFT)
-
-#define GREEN_BITS 6
-#define GREEN_SHIFT 5
-#define GREEN_MASK (((1 << GREEN_BITS)-1) << GREEN_SHIFT)
-
-#define BLUE_BITS 5
-#define BLUE_SHIFT 0
-#define BLUE_MASK (((1 << BLUE_BITS)-1) << BLUE_SHIFT)
-
-
 static unsigned char *
 unpack_RGB_565 (gushort *pixels, unsigned int bytes_len)
 {
@@ -72,49 +60,6 @@ unpack_RGB_565 (gushort *pixels, unsigne
 	return result;
 }
 
-#if 0
-G_GNUC_UNUSED static void
-pack_RGB_565 (GdkPixbuf *pixbuf, gushort **pixels565, unsigned int *bytes_len)
-{
-	guchar *pixels;
-	gushort *result;
-	gint row_stride;
-	gint channels;
-	gint width;
-	gint height;
-	gint w;
-	gint h;
-
-	g_object_get (G_OBJECT (pixbuf), 
-		      "rowstride", &row_stride, "n-channels", &channels,
-		      "height", &height, "width", &width,
-		      "pixels", &pixels, NULL);
-
-	result = g_malloc0 (width * height * 2);
-
-	for (h = 0; h < height; h++) {
-		for (w = 0; w < width; w++) {
-			gint r;
-			gint g;
-			gint b;
-
-			r = pixels[(h*row_stride + w)*channels];
-			g = pixels[(h*row_stride + w)*channels + 1]; 
-			b = pixels[(h*row_stride + w)*channels + 2]; 
-			r >>= (8 - RED_BITS);
-			g >>= (8 - GREEN_BITS);
-			b >>= (8 - BLUE_BITS);
-			r = (r << RED_SHIFT) & RED_MASK;
-			g = (g << GREEN_SHIFT) & GREEN_MASK;
-			b = (b << BLUE_SHIFT) & BLUE_MASK;
-			result[h*height + w] =  (GINT16_TO_LE (r | g | b));
-		}
-	}
-
-	*pixels565 = result;
-	*bytes_len = width * height * 2;
-}
-#endif
 
 static unsigned char *
 get_pixel_data (Itdb_Image *image)
@@ -186,6 +131,18 @@ itdb_image_get_rgb_data (Itdb_Image *ima
 	*/
 }
 
+G_GNUC_INTERNAL char *
+ipod_image_get_ithmb_filename (const char *mount_point, gint correlation_id) 
+{
+	char *paths[] = {"iPod_Control", "Artwork", NULL, NULL};
+	char *filename;
+
+	paths[2] = g_strdup_printf ("F%04u_1.ithmb", correlation_id);
+	filename = itdb_resolve_path (mount_point, (const char **)paths);
+	g_free (paths[2]);
+	return filename;
+}
+
 G_GNUC_INTERNAL Itdb_Image *
 ipod_image_new_from_mhni (MhniHeader *mhni, const char *mount_point)
 {
@@ -195,26 +152,25 @@ ipod_image_new_from_mhni (MhniHeader *mh
 	if (img == NULL) {
 		return NULL;
 	}
-	img->filename = g_strdup_printf ("%s/iPod_Control/Artwork/F%04u_1.ithmb", mount_point, GINT_FROM_LE (mhni->correlation_id));
+	img->filename = ipod_image_get_ithmb_filename (mount_point,
+						       GINT_FROM_LE (mhni->correlation_id));
 	img->size = GINT_FROM_LE (mhni->image_size);
 	img->offset = GINT_FROM_LE (mhni->ithmb_offset);
 	img->width = (GINT_FROM_LE (mhni->image_dimensions) & 0xffff0000) >> 16;
 	img->height = (GINT_FROM_LE (mhni->image_dimensions) & 0x0000ffff);
 
-	return img;
-}
-
-G_GNUC_INTERNAL Itdb_Image *
-ipod_image_new_from_mhii (MhiiHeader *mhii)
-{
-	Itdb_Image *img;
-
-	img = g_new0 (Itdb_Image, 1);
-	if (img == NULL) {
+	if (mhni->correlation_id == IPOD_THUMBNAIL_FULL_SIZE_CORRELATION_ID) {
+		img->type = ITDB_IMAGE_FULL_SCREEN;
+	} else if (mhni->correlation_id == IPOD_THUMBNAIL_NOW_PLAYING_CORRELATION_ID)
+	{
+		img->type = ITDB_IMAGE_NOW_PLAYING;
+			
+	} else {
+		g_print ("Unrecognized image size: %08x\n", 
+			 GINT_FROM_LE (mhni->image_dimensions));
+		g_free (img);
 		return NULL;
 	}
-	img->size = GINT_FROM_LE (mhii->orig_img_size);
-	img->id = GINT_FROM_LE (mhii->image_id);
 
-	return img;	
+	return img;
 }
Index: src/db-image-parser.h
===================================================================
RCS file: /cvsroot/gtkpod/libgpod/src/db-image-parser.h,v
retrieving revision 1.1
diff -p -u -r1.1 db-image-parser.h
--- src/db-image-parser.h	19 Sep 2005 10:30:50 -0000	1.1
+++ src/db-image-parser.h	22 Sep 2005 07:42:33 -0000
@@ -28,7 +28,25 @@
 #include "db-itunes-parser.h"
 #include "itdb.h"
 
-Itdb_Image *ipod_image_new_from_mhni (MhniHeader *mhni, const char *mount_point);
-Itdb_Image *ipod_image_new_from_mhii (MhiiHeader *mhii);
+#define RED_BITS   5
+#define RED_SHIFT 11
+#define RED_MASK  (((1 << RED_BITS)-1) << RED_SHIFT)
+
+#define GREEN_BITS 6
+#define GREEN_SHIFT 5
+#define GREEN_MASK (((1 << GREEN_BITS)-1) << GREEN_SHIFT)
+
+#define BLUE_BITS 5
+#define BLUE_SHIFT 0
+#define BLUE_MASK (((1 << BLUE_BITS)-1) << BLUE_SHIFT)
+
+G_GNUC_INTERNAL Itdb_Image *ipod_image_new_from_mhni (MhniHeader *mhni, 
+						      const char *mount_point);
+G_GNUC_INTERNAL char *ipod_image_get_ithmb_filename (const char *mount_point, 
+						     gint correlation_id);
+
+G_GNUC_INTERNAL int itdb_write_ithumb_files (Itdb_iTunesDB *db, 
+					     const char *mount_point);
+
 
 #endif
Index: src/db-parse-context.c
===================================================================
RCS file: /cvsroot/gtkpod/libgpod/src/db-parse-context.c,v
retrieving revision 1.1
diff -p -u -r1.1 db-parse-context.c
--- src/db-parse-context.c	19 Sep 2005 10:30:50 -0000	1.1
+++ src/db-parse-context.c	22 Sep 2005 07:42:33 -0000
@@ -53,6 +53,16 @@ db_parse_context_new (const unsigned cha
 	return result;
 }
 
+void
+db_parse_context_destroy (DBParseContext *ctx, gboolean unmap) 
+{
+	g_return_if_fail (ctx != NULL);
+
+	if (unmap) {
+		munmap ((void*)ctx->buffer, ctx->total_len);
+	}
+	g_free (ctx);
+}
 
 static void
 db_parse_context_set_header_len (DBParseContext *ctx, off_t len)
Index: src/db-parse-context.h
===================================================================
RCS file: /cvsroot/gtkpod/libgpod/src/db-parse-context.h,v
retrieving revision 1.1
diff -p -u -r1.1 db-parse-context.h
--- src/db-parse-context.h	19 Sep 2005 10:30:50 -0000	1.1
+++ src/db-parse-context.h	22 Sep 2005 07:42:33 -0000
@@ -49,5 +49,6 @@ G_GNUC_INTERNAL DBParseContext *db_parse
 G_GNUC_INTERNAL void *db_parse_context_get_m_header_internal (DBParseContext *ctx, const char *id, off_t size) G_GNUC_INTERNAL;
 
 G_GNUC_INTERNAL DBParseContext *db_parse_context_new_from_file (const char *filename) G_GNUC_INTERNAL;
+G_GNUC_INTERNAL void db_parse_context_destroy (DBParseContext *ctx, gboolean unmap);
 
 #endif
Index: src/itdb.h
===================================================================
RCS file: /cvsroot/gtkpod/libgpod/src/itdb.h,v
retrieving revision 1.7
diff -p -u -r1.7 itdb.h
--- src/itdb.h	20 Sep 2005 13:20:32 -0000	1.7
+++ src/itdb.h	22 Sep 2005 07:42:33 -0000
@@ -307,6 +307,12 @@ typedef struct SPLRules
     GList *rules;
 } SPLRules;
 
+enum ItdbImageType {
+	ITDB_IMAGE_FULL_SCREEN,
+	ITDB_IMAGE_NOW_PLAYING
+};
+
+
 /* This structure can represent two slightly different images:
  *   - an image before it's transferred to the iPod (it will then be scaled
  *     as necessary to generate the 2 thumbnails needed by the iPod), 
@@ -320,13 +326,14 @@ typedef struct SPLRules
  *     on the iPod
  */
 struct _Itdb_Image {
-	char *filename;
+	enum ItdbImageType type;
+  	char *filename;
 	off_t offset;
 	size_t size;
 	unsigned int width;
 	unsigned int height;
-	unsigned int id; 
 };
+
 typedef struct _Itdb_Image Itdb_Image;
 
 typedef void (* ItdbUserDataDestroyFunc) (gpointer userdata);
@@ -573,9 +580,9 @@ typedef struct
   guint32 unk228, unk232, unk236, unk240;
 
   /* This is for Cover Art support */
-  Itdb_Image *full_size_thumbnail;
-  Itdb_Image *now_playing_thumbnail;
-  Itdb_Image *orig_image;
+  GList *thumbnails;
+  unsigned int image_id;
+  char *orig_image_filename;
 
   /* below is for use by application */
   guint64 usertype;
@@ -681,6 +688,9 @@ void itdb_spl_update_all (Itdb_iTunesDB 
 
 /* thumbnails functions */
 unsigned char *itdb_image_get_rgb_data (Itdb_Image *image);
+int itdb_track_set_thumbnail (Itdb_Track *song, const char *filename);
+void itdb_track_remove_thumbnail (Itdb_Track *song);
+void itdb_track_free_generated_thumbnails (Itdb_Track *track);
 
 /* time functions */
 guint64 itdb_time_get_mac_time (void);
Index: src/itdb_itunesdb.c
===================================================================
RCS file: /cvsroot/gtkpod/libgpod/src/itdb_itunesdb.c,v
retrieving revision 1.9
diff -p -u -r1.9 itdb_itunesdb.c
--- src/itdb_itunesdb.c	21 Sep 2005 15:41:30 -0000	1.9
+++ src/itdb_itunesdb.c	22 Sep 2005 07:42:36 -0000
@@ -898,6 +898,7 @@ Itdb_iTunesDB *itdb_new (void)
     itdb->version = 0x09;
     itdb->id = ((guint64)g_rand_int (grand) << 32) |
 	((guint64)g_rand_int (grand));
+    g_rand_free (grand);
     return itdb;
 }
 
@@ -3301,6 +3302,14 @@ gboolean itdb_write (Itdb_iTunesDB *itdb
 
     if (!mp) mp = itdb->mountpoint;
 
+    /* First, let's try to write the .ithmb files containing the artwork data
+     * since this operation modifies the 'artwork_count' and 'artwork_size' 
+     * field in the Itdb_Track contained in the database.
+     * Errors happening during that operation are considered non fatal since
+     * they shouldn't corrupt the main database.
+     */
+    ipod_write_artwork_db (itdb, mp);
+    
     itunes_path = itdb_resolve_path (mp, db);
     
     if(!itunes_path)
Index: src/itdb_private.h
===================================================================
RCS file: /cvsroot/gtkpod/libgpod/src/itdb_private.h,v
retrieving revision 1.2
diff -p -u -r1.2 itdb_private.h
--- src/itdb_private.h	19 Sep 2005 12:33:24 -0000	1.2
+++ src/itdb_private.h	22 Sep 2005 07:42:36 -0000
@@ -99,5 +99,6 @@ typedef struct
 } FExport;
 
 
-gboolean itdb_spl_action_known (SPLAction action);
+G_GNUC_INTERNAL gboolean itdb_spl_action_known (SPLAction action);
+
 #endif
Index: src/itdb_track.c
===================================================================
RCS file: /cvsroot/gtkpod/libgpod/src/itdb_track.c,v
retrieving revision 1.2
diff -p -u -r1.2 itdb_track.c
--- src/itdb_track.c	19 Sep 2005 09:02:52 -0000	1.2
+++ src/itdb_track.c	22 Sep 2005 07:42:36 -0000
@@ -29,6 +29,7 @@
 
 #include "itdb_private.h"
 #include <string.h>
+#include <glib/gstdio.h>
 
 /* Generate a new Itdb_Track structure */
 Itdb_Track *itdb_track_new (void)
@@ -144,6 +145,22 @@ void itdb_track_add (Itdb_iTunesDB *itdb
     else  itdb->tracks = g_list_insert (itdb->tracks, track, pos);
 }
 
+void
+itdb_track_free_generated_thumbnails (Itdb_Track *track)
+{
+    GList *it;
+
+    for (it = track->thumbnails; it != NULL; it = it->next) {
+	Itdb_Image *image;
+
+	image = (Itdb_Image *)it->data;
+	g_free (image->filename);
+	g_free (image);
+    }
+    g_list_free (track->thumbnails);
+    track->thumbnails = NULL;
+}
+
 /* Free the memory taken by @track */
 void itdb_track_free (Itdb_Track *track)
 {
@@ -158,6 +175,8 @@ void itdb_track_free (Itdb_Track *track)
     g_free (track->filetype);
     g_free (track->grouping);
     g_free (track->ipod_path);
+    itdb_track_free_generated_thumbnails (track);
+    g_free (track->orig_image_filename);
     if (track->userdata && track->userdata_destroy)
 	(*track->userdata_destroy) (track->userdata);
     g_free (track);
@@ -292,3 +311,30 @@ Itdb_Track *itdb_track_id_tree_by_id (GT
 
     return (Itdb_Track *)g_tree_lookup (idtree, &id);
 }
+
+void
+itdb_track_remove_thumbnail (Itdb_Track *song)
+{
+    itdb_track_free_generated_thumbnails (song);
+    g_free (song->orig_image_filename);
+    song->orig_image_filename = NULL;
+    song->image_id = 0;
+}
+
+
+int 
+itdb_track_set_thumbnail (Itdb_Track *song, const char *filename)
+{
+    struct stat statbuf;
+
+    g_return_val_if_fail (song != NULL, -1);
+
+    if (g_stat  (filename, &statbuf) != 0) {
+	return -1;
+    }
+    itdb_track_remove_thumbnail (song);
+    song->artwork_size = statbuf.st_size;
+    song->orig_image_filename = g_strdup (filename);
+
+    return 0;
+}
Index: src/ithumb-writer.c
===================================================================
RCS file: src/ithumb-writer.c
diff -N src/ithumb-writer.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/ithumb-writer.c	22 Sep 2005 07:42:36 -0000
@@ -0,0 +1,288 @@
+/*
+ *  Copyright (C) 2005 Christophe Fergeau
+ *
+ * 
+ *  The code contained in this file is free software; you can redistribute
+ *  it and/or modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either version
+ *  2.1 of the License, or (at your option) any later version.
+ *  
+ *  This file is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *  
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this code; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ *  iTunes and iPod are trademarks of Apple
+ * 
+ *  This product is not supported/written/published by Apple!
+ *
+ */
+
+#include "itdb.h"
+#include "itdb_private.h"
+#include "db-image-parser.h"
+
+#include <errno.h>
+#include <locale.h>
+#include <string.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#define FULL_THUMB_SIDE_LEN 0x8c
+#define NOW_PLAYING_THUMB_SIDE_LEN 0x38
+
+#define IPOD_THUMBNAIL_FULL_SIZE_CORRELATION_ID 1016
+#define IPOD_THUMBNAIL_NOW_PLAYING_CORRELATION_ID 1017
+
+struct _iThumbWriter {
+	off_t cur_offset;
+	FILE *f;
+	guint correlation_id;
+	enum ItdbImageType type;
+	int size;
+	GHashTable *cache;
+};
+typedef struct _iThumbWriter iThumbWriter;
+
+/* The iPod expect square thumbnails with 2 specific side sizes (0x38 and 0x8c
+ * respectively for small and fullscreen thumbnails), the 'size' parameter is
+ * here to specify which size we are interested in in case the pixbuf is non
+ * square
+ */
+static void
+pack_RGB_565 (GdkPixbuf *pixbuf, int size,
+	      gushort **pixels565, unsigned int *bytes_len)
+{
+	guchar *pixels;
+	gushort *result;
+	gint row_stride;
+	gint channels;
+	gint width;
+	gint height;
+	gint w;
+	gint h;
+
+	g_return_if_fail (pixels565 != NULL);
+	*pixels565 = NULL;
+	g_return_if_fail (bytes_len != NULL);
+
+	g_object_get (G_OBJECT (pixbuf), 
+		      "rowstride", &row_stride, "n-channels", &channels,
+		      "height", &height, "width", &width,
+		      "pixels", &pixels, NULL);
+	g_return_if_fail ((width <= size) && (height <= size));
+	result = g_malloc0 (size * size * 2);
+
+	for (h = 0; h < height; h++) {
+		for (w = 0; w < width; w++) {
+			gint r;
+			gint g;
+			gint b;
+
+			r = pixels[h*row_stride + w*channels];
+			g = pixels[h*row_stride + w*channels + 1]; 
+			b = pixels[h*row_stride + w*channels + 2]; 
+			r >>= (8 - RED_BITS);
+			g >>= (8 - GREEN_BITS);
+			b >>= (8 - BLUE_BITS);
+			r = (r << RED_SHIFT) & RED_MASK;
+			g = (g << GREEN_SHIFT) & GREEN_MASK;
+			b = (b << BLUE_SHIFT) & BLUE_MASK;
+			result[h*size + w] =  (GINT16_TO_LE (r | g | b));
+		}
+	}
+
+	*pixels565 = result;
+	*bytes_len = size * size * 2;
+}
+
+
+
+static Itdb_Image *
+itdb_image_dup (Itdb_Image *image)
+{
+	Itdb_Image *result;
+
+	result = g_new0 (Itdb_Image, 1);
+	if (result == NULL) {
+		return NULL;
+	}
+	result->type = image->type;
+	result->height = image->height;
+	result->width = image->width;
+	result->offset = image->offset;
+	result->size = image->size;
+
+	return result;
+}
+
+static Itdb_Image *
+ithumb_writer_write_thumbnail (iThumbWriter *writer, 
+			       const char *filename)
+{
+	GdkPixbuf *thumb;
+	gushort *pixels;
+	Itdb_Image *image;
+
+	image = g_hash_table_lookup (writer->cache, filename);
+	if (image != NULL) {
+		return itdb_image_dup (image);
+	}
+
+	image = g_new0 (Itdb_Image, 1);
+	if (image == NULL) {
+		return NULL;
+	}
+
+	thumb = gdk_pixbuf_new_from_file_at_scale (filename, writer->size, -1,
+						   TRUE, NULL);
+	if (thumb == NULL) {
+		g_free (image);
+		return NULL;
+	}
+	g_object_get (G_OBJECT (thumb), "height", &image->height, NULL);
+	if (image->height > writer->size) {
+		g_object_unref (thumb);
+		thumb = gdk_pixbuf_new_from_file_at_scale (filename, 
+							   -1, writer->size,
+							   TRUE, NULL);
+		if (thumb == NULL) {
+			g_free (image);
+			return NULL;
+		}
+	}
+	g_object_get (G_OBJECT (thumb), 
+		      "height", &image->height, 
+		      "width", &image->width,
+		      NULL);
+	image->offset = writer->cur_offset;
+	image->type = writer->type;
+	pack_RGB_565 (thumb, writer->size, &pixels, &image->size);
+	g_object_unref (G_OBJECT (thumb));
+	if (pixels == NULL) {
+		g_free (image);
+		return NULL;
+	}
+	if (fwrite (pixels, image->size, 1, writer->f) != 1) {
+		g_free (image);		
+		g_free (pixels);
+		g_print ("Error writing to file: %s\n", strerror (errno));
+		return NULL;
+	}
+	g_free (pixels);
+	writer->cur_offset += image->size;
+	g_hash_table_insert (writer->cache, g_strdup (filename), image);
+
+	return image;
+}
+
+
+#define FULL_THUMB_SIDE_LEN 0x8c
+#define NOW_PLAYING_THUMB_SIDE_LEN 0x38
+
+#define FULL_THUMB_CORRELATION_ID 1016
+#define NOW_PLAYING_THUMB_CORRELATION_ID 1017
+
+
+
+static iThumbWriter *
+ithumb_writer_new (const char *mount_point, enum ItdbImageType type, 
+		   int correlation_id, int size)
+{
+	char *filename;
+	iThumbWriter *writer;
+	writer = g_new0 (iThumbWriter, 1);
+	if (writer == NULL) {
+		return NULL;
+	}
+	writer->correlation_id = correlation_id;
+	writer->size = size;
+	writer->type = type;
+	writer->cache = g_hash_table_new_full (g_str_hash, g_str_equal, 
+					       g_free, NULL);
+	if (writer->cache == NULL) {
+		g_free (writer);
+		return NULL;
+	}
+	filename = ipod_image_get_ithmb_filename (mount_point, correlation_id);
+	writer->f = fopen (filename, "w");
+	if (writer->f == NULL) {
+		g_print ("Error opening %s: %s\n", filename, strerror (errno));
+		g_free (filename);
+		g_hash_table_destroy (writer->cache);
+		g_free (writer);
+		return NULL;
+	}
+	g_free (filename);
+	
+	return writer;
+}
+
+static void
+ithumb_writer_free (iThumbWriter *writer)
+{
+	g_return_if_fail (writer != NULL);
+	g_hash_table_destroy (writer->cache);
+	fclose (writer->f);
+	g_free (writer);
+}
+
+G_GNUC_INTERNAL int
+itdb_write_ithumb_files (Itdb_iTunesDB *db, const char *mount_point) 
+{
+	GList *it;
+	iThumbWriter *fullsize_writer;
+	iThumbWriter *nowplaying_writer;
+	g_print ("%s\n", G_GNUC_FUNCTION);
+
+	fullsize_writer = ithumb_writer_new (mount_point,
+					     ITDB_IMAGE_FULL_SCREEN,
+					     FULL_THUMB_CORRELATION_ID,
+					     FULL_THUMB_SIDE_LEN);
+	if (fullsize_writer == NULL) {
+		return -1;
+	}
+
+	nowplaying_writer = ithumb_writer_new (mount_point,
+					       ITDB_IMAGE_NOW_PLAYING,
+					       NOW_PLAYING_THUMB_CORRELATION_ID,
+					       NOW_PLAYING_THUMB_SIDE_LEN);
+	if (nowplaying_writer == NULL) {
+		ithumb_writer_free (fullsize_writer);
+		return -1;
+	}
+
+	for (it = db->tracks; it != NULL; it = it->next) {
+		Itdb_Track *song;
+		Itdb_Image *thumb;
+
+		song = (Itdb_Track *)it->data;
+		song->artwork_count = 0;
+		itdb_track_free_generated_thumbnails (song);
+		if (song->orig_image_filename == NULL) {
+			continue;
+		}
+		thumb = ithumb_writer_write_thumbnail (nowplaying_writer,
+						       song->orig_image_filename);
+		if (thumb != NULL) {
+			song->thumbnails = g_list_append (song->thumbnails, 
+							  thumb);
+			song->artwork_count++;
+		}
+		thumb = ithumb_writer_write_thumbnail (fullsize_writer,
+						       song->orig_image_filename);
+		if (thumb != NULL) {			
+			song->thumbnails = g_list_append (song->thumbnails, 
+							  thumb);
+			song->artwork_count++;
+		}		
+	}
+
+	ithumb_writer_free (nowplaying_writer);
+	ithumb_writer_free (fullsize_writer);
+
+	return 0;
+}
Index: tests/Makefile.am
===================================================================
RCS file: /cvsroot/gtkpod/libgpod/tests/Makefile.am,v
retrieving revision 1.2
diff -p -u -r1.2 Makefile.am
--- tests/Makefile.am	19 Sep 2005 10:30:50 -0000	1.2
+++ tests/Makefile.am	22 Sep 2005 07:42:36 -0000
@@ -1,5 +1,5 @@
 if HAVE_GDKPIXBUF
-TESTTHUMBS=test-thumbnails
+TESTTHUMBS=test-thumbnails test-write-thumbnails
 else
 TESTTHUMBS=
 endif
@@ -16,4 +16,8 @@ if HAVE_GDKPIXBUF
 test_thumbnails_SOURCES = test-covers.c
 test_thumbnails_CFLAGS = $(GDKPIXBUF_CFLAGS) $(AM_CFLAGS)
 test_thumbnails_LDADD = $(top_builddir)/src/libgpod.la $(GDKPIXBUF_LIBS)
+
+test_write_thumbnails_SOURCES = test-write-covers.c
+test_write_thumbnails_CFLAGS = $(GDKPIXBUF_CFLAGS) $(AM_CFLAGS)
+test_write_thumbnails_LDADD = $(top_builddir)/src/libgpod.la $(GDKPIXBUF_LIBS)
 endif
Index: tests/test-covers.c
===================================================================
RCS file: /cvsroot/gtkpod/libgpod/tests/test-covers.c,v
retrieving revision 1.1
diff -p -u -r1.1 test-covers.c
--- tests/test-covers.c	19 Sep 2005 10:30:50 -0000	1.1
+++ tests/test-covers.c	22 Sep 2005 07:42:36 -0000
@@ -36,14 +36,25 @@ ipod_image_to_gdk_pixbuf (Itdb_Image *im
 {
 	GdkPixbuf *result;
 	guchar *pixels;
+	int row_stride;
+
+	if (image->type == ITDB_IMAGE_FULL_SCREEN) {
+		row_stride = 140;
+	} else if (image->type == ITDB_IMAGE_NOW_PLAYING) {
+		row_stride = 56;
+	} else {
+		return NULL;
+	}
 
 	pixels = itdb_image_get_rgb_data (image);
 	if (pixels == NULL) {
 		return NULL;
 	}
+
 	result = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, FALSE,
 					   8, image->width, image->height,
-					   image->width * 3,
+					   /*image->width * 3, */
+					   row_stride * 3,
 					   (GdkPixbufDestroyNotify)g_free,
 					   NULL);
 	return result;
@@ -59,28 +70,31 @@ save_itdb_image (Itdb_Image *image, cons
 	if (thumb != NULL) {
 		gdk_pixbuf_save (thumb, filename, "png", NULL, NULL);
 		gdk_pixbuf_unref (thumb);
-		g_print ("Saved %s\n", filename);
+/*		g_print ("Saved %s\n", filename); */
 	}
 }
 static void
 save_song_thumbnails (Itdb_Track *song)
 {
-	if (song->full_size_thumbnail != NULL) {
+	GList *it;
+	
+	for (it = song->thumbnails; it != NULL; it = it->next) {
+		Itdb_Image *image;
 		gchar *filename;
-		
-		filename = g_strdup_printf ("/tmp/fullsize%016llx.png",
-					    song->dbid);
-		save_itdb_image (song->full_size_thumbnail,filename);
-		g_free (filename);
-	}
 
-	if (song->now_playing_thumbnail != NULL) {
-		gchar *filename;
-		
-		filename = g_strdup_printf ("/tmp/nowplaying%016llx.png", 
-					    song->dbid);
-		save_itdb_image (song->now_playing_thumbnail, filename);
-		g_free (filename);
+		image = (Itdb_Image *)it->data;
+		filename = NULL;
+		if (image->type == ITDB_IMAGE_FULL_SCREEN) {
+			filename = g_strdup_printf ("/tmp/fullsize%016llx.png",
+						    song->dbid);
+		} else if (image->type == ITDB_IMAGE_NOW_PLAYING) {
+			filename = g_strdup_printf ("/tmp/nowplaying%016llx.png", 
+						    song->dbid);
+		}
+		if (filename != NULL) {
+			save_itdb_image (image, filename);
+			g_free (filename);
+		}
 	}
 }
 
Index: tests/test-write-covers.c
===================================================================
RCS file: tests/test-write-covers.c
diff -N tests/test-write-covers.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/test-write-covers.c	22 Sep 2005 07:42:36 -0000
@@ -0,0 +1,126 @@
+/* Copyright (c) 2005, Christophe Fergeau <teuf@gnome.org>
+ * All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *    * Redistributions of source code must retain the above copyright notice, 
+ *      this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright 
+ *      notice, this list of conditions and the following disclaimer in the 
+ *      documentation and/or other materials provided with the distribution.
+ *    * Neither the name of the <ORGANIZATION> nor the names of its 
+ *      contributors may be used to endorse or promote products derived from 
+ *      this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "itdb.h"
+
+#include <locale.h>
+#include <string.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+static GList *
+get_cover_list (const char *dirname)
+{
+	GDir *dir;
+	const char *filename;
+	GList *result;
+
+	dir = g_dir_open (dirname, 0, NULL);
+	if (dir == NULL) {
+		return NULL;
+	}
+	result = NULL;
+	filename = g_dir_read_name (dir);
+	while (filename != NULL) {
+		const char *ext;
+		ext = strrchr (filename, '.');
+		if (ext != NULL) {
+			if ((g_ascii_strcasecmp (ext, ".png") == 0) 
+			    || (g_ascii_strcasecmp (ext, ".jpg") == 0)
+			    || (g_ascii_strcasecmp (ext, ".jpeg") == 0)) {
+				char *fullpath;
+				fullpath = g_build_filename (dirname, filename,
+							     NULL);
+				result = g_list_prepend (result, fullpath);
+			}
+		}
+		filename = g_dir_read_name (dir);
+	}
+	g_dir_close (dir);
+	
+	return g_list_reverse (result);
+}
+
+
+int
+main (int argc, char **argv)
+{
+	Itdb_iTunesDB *db;
+	GList *it;
+	GList *covers;
+	int nb_covers;
+
+	if (argc < 3) {
+		g_print ("Usage: %s mountpoint image-dir\n", argv[0]);
+		return 1;
+	}
+	
+	setlocale (LC_ALL, "");
+	g_type_init ();
+	covers = get_cover_list (argv[2]);
+	if (covers == NULL) {
+		g_print ("Error, %s should be a directory containing pictures\n", argv[2]);
+		return 1;
+	}
+	nb_covers = g_list_length (covers);
+	db = itdb_parse (argv[1], NULL);
+	if (db == NULL) {
+		g_print ("Error reading iPod database\n");
+		return 1;
+	}
+	for (it = db->tracks; it != NULL; it = it->next) {
+		Itdb_Track *song;
+		const char *coverpath;
+
+		song = (Itdb_Track*)it->data;
+		itdb_track_remove_thumbnail (song);
+
+		coverpath = g_list_nth_data (covers, 
+					     g_random_int_range (0, nb_covers));
+		itdb_track_set_thumbnail (song, coverpath);
+/*		g_print ("%s - %s - %s gets %s\n",  
+		song->artist, song->album, song->title, coverpath);*/
+
+	}
+/*	if (db->tracks != NULL) {
+		Itdb_Track *song;
+		const char *coverpath;
+		
+		song = (Itdb_Track *)db->tracks->data;
+		coverpath = g_list_nth_data (covers, 
+					     g_random_int_range (0, nb_covers));
+		itdb_track_remove_thumbnail (song);
+		itdb_track_set_thumbnail (song, coverpath);
+		}*/
+
+	itdb_write (db, NULL, NULL);
+	itdb_free (db);
+	g_list_foreach (covers, (GFunc)g_free, NULL);
+	g_list_free (covers);
+
+	return 0;
+}
