summaryrefslogtreecommitdiffstats
path: root/src/db-artwork-writer.c
diff options
context:
space:
mode:
authorJorg Schuler <jcsjcs@users.sourceforge.net>2005-09-22 18:16:50 +0000
committerJorg Schuler <jcsjcs@users.sourceforge.net>2005-09-22 18:16:50 +0000
commit32fdf71f74fee4e0774d7ae5167447c70cd83b7c (patch)
treedd5f9c7eb78249d02e1a80a8a7447c2bcc0fd4c9 /src/db-artwork-writer.c
parentfba33d9b3d03cb92df15904c045fcfe93d251fa4 (diff)
downloadlibgpod-tmz-32fdf71f74fee4e0774d7ae5167447c70cd83b7c.tar.gz
libgpod-tmz-32fdf71f74fee4e0774d7ae5167447c70cd83b7c.tar.xz
libgpod-tmz-32fdf71f74fee4e0774d7ae5167447c70cd83b7c.zip
* applied Christophe Fergeau's patch which adds cover art writing
support to libgpod -> bump to version 104 git-svn-id: https://gtkpod.svn.sf.net/svnroot/gtkpod/libgpod/trunk@1098 f01d2545-417e-4e96-918e-98f8d0dbbcb6
Diffstat (limited to 'src/db-artwork-writer.c')
-rw-r--r--src/db-artwork-writer.c769
1 files changed, 769 insertions, 0 deletions
diff --git a/src/db-artwork-writer.c b/src/db-artwork-writer.c
new file mode 100644
index 0000000..cb97b7a
--- /dev/null
+++ b/src/db-artwork-writer.c
@@ -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;
+}