diff options
author | Jorg Schuler <jcsjcs@users.sourceforge.net> | 2005-09-19 10:30:50 +0000 |
---|---|---|
committer | Jorg Schuler <jcsjcs@users.sourceforge.net> | 2005-09-19 10:30:50 +0000 |
commit | 7278b29a031119ce8da1ea6022de8b70b49ee577 (patch) | |
tree | b98944ba47d504e70fdb04bf7f858c7be365b4d7 /src | |
parent | db92f98a83f98715538b6c91c0dfd6694d9ef079 (diff) | |
download | libgpod-tmz-7278b29a031119ce8da1ea6022de8b70b49ee577.tar.gz libgpod-tmz-7278b29a031119ce8da1ea6022de8b70b49ee577.tar.xz libgpod-tmz-7278b29a031119ce8da1ea6022de8b70b49ee577.zip |
* applied patch provided by Christophe Fergeau <teuf at gnome.org>
for artwork database support (read-only).
git-svn-id: https://gtkpod.svn.sf.net/svnroot/gtkpod/libgpod/trunk@1093 f01d2545-417e-4e96-918e-98f8d0dbbcb6
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 12 | ||||
-rw-r--r-- | src/db-artwork-debug.c | 182 | ||||
-rw-r--r-- | src/db-artwork-debug.h | 51 | ||||
-rw-r--r-- | src/db-artwork-parser.c | 398 | ||||
-rw-r--r-- | src/db-artwork-parser.h | 39 | ||||
-rw-r--r-- | src/db-image-parser.c | 220 | ||||
-rw-r--r-- | src/db-image-parser.h | 34 | ||||
-rw-r--r-- | src/db-itunes-parser.h | 540 | ||||
-rw-r--r-- | src/db-parse-context.c | 196 | ||||
-rw-r--r-- | src/db-parse-context.h | 53 | ||||
-rw-r--r-- | src/itdb.h | 33 | ||||
-rw-r--r-- | src/itdb_itunesdb.c | 15 |
12 files changed, 1770 insertions, 3 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index e0998fd..1a46d92 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,7 +5,17 @@ libgpod_la_SOURCES = \ itdb_itunesdb.c \ itdb_playlist.c \ itdb_private.h \ - itdb_track.c + itdb_track.c \ + db-artwork-parser.c \ + db-artwork-parser.h \ + db-parse-context.c \ + db-parse-context.h \ + db-artwork-debug.c \ + db-artwork-debug.h \ + db-image-parser.c \ + db-image-parser.h + + libgpod_la_headers = itdb.h libgpod_la_noinst_headers = itdb_private.h libgpod_la_LDFLAGS = -version-info $(LIBGPOD_CURRENT):$(LIBGPOD_REVISION):$(LIBGPOD_AGE) \ diff --git a/src/db-artwork-debug.c b/src/db-artwork-debug.c new file mode 100644 index 0000000..d8ccb96 --- /dev/null +++ b/src/db-artwork-debug.c @@ -0,0 +1,182 @@ +/* + * 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 "db-artwork-debug.h" + +#ifdef DEBUG_ARTWORKDB +G_GNUC_INTERNAL void +dump_mhif (MhifHeader *mhif) +{ + + g_print ("MHIF (%d):\n", sizeof (MhifHeader)); + g_print ("\tHeader length: %d\n", GINT_FROM_LE (mhif->header_len)); + g_print ("\tTotal length: %d\n", GINT_FROM_LE (mhif->total_len)); + g_print ("\tUnknown1: %08x\n", GINT_FROM_LE (mhif->unknown1)); + g_print ("\tCorrelation ID: %d (=> F%d_1.ithmb)\n", + GINT_FROM_LE (mhif->correlation_id), + GINT_FROM_LE (mhif->correlation_id)); + g_print ("\tImage size: %d bytes\n", GINT_FROM_LE (mhif->image_size)); +} + + +static char * +get_utf16_string (void* buffer, gint length) +{ + char *result; + gunichar2 *tmp; + int i; + /* Byte-swap the utf16 characters if necessary (I'm relying + * on gcc to optimize most of this code away on LE platforms) + */ + tmp = g_memdup (buffer, length); + for (i = 0; i < length/2; i++) { + tmp[i] = GINT16_FROM_LE (tmp[i]); + } + result = g_utf16_to_utf8 (tmp, length/2, NULL, NULL, NULL); + g_free (tmp); + + return result; + +} + +G_GNUC_INTERNAL void +dump_mhod_type_3 (MhodHeaderArtworkType3 *mhod3) +{ + gchar *str; + + g_print ("MHOD (%d):\n", sizeof (MhodHeaderArtworkType3)); + g_print ("\tHeader length: %d\n", GINT_FROM_LE (mhod3->header_len)); + g_print ("\tTotal length: %d\n", GINT_FROM_LE (mhod3->total_len)); + g_print ("\tType: %d\n", GINT_FROM_LE (mhod3->type)); + g_print ("\tUnknown1: %08x\n", GINT_FROM_LE (mhod3->unknown1)); + g_print ("\tUnknown2: %08x\n", GINT_FROM_LE (mhod3->unknown2)); + g_print ("\tString length: %u\n", GINT_FROM_LE (mhod3->string_len)); + g_print ("\tMHOD version: %u\n", GINT_FROM_LE (mhod3->mhod_version)); + g_print ("\tUnknown4: %08x\n", GINT_FROM_LE (mhod3->unknown4)); + str = get_utf16_string (mhod3->string, mhod3->string_len); + g_print ("\tString: %s\n", str); + g_free (str); +} + +G_GNUC_INTERNAL void +dump_mhni (MhniHeader *mhni) +{ + g_print ("MHNI (%d):\n", sizeof (MhniHeader)); + g_print ("\tHeader length: %d\n", GINT_FROM_LE (mhni->header_len)); + g_print ("\tTotal length: %d\n", GINT_FROM_LE (mhni->total_len)); + g_print ("\tNumber of children: %08x\n", GINT_FROM_LE (mhni->num_children)); + g_print ("\tCorrelation ID: %d (=> F%d_1.ithmb)\n", + GINT_FROM_LE (mhni->correlation_id), + GINT_FROM_LE (mhni->correlation_id)); + g_print ("\tithmb offset: %u bytes\n", GINT_FROM_LE (mhni->ithmb_offset)); + g_print ("\tImage size: %u bytes\n", GINT_FROM_LE (mhni->image_size)); + g_print ("\tUnknown3: %08x\n", GINT_FROM_LE (mhni->unknown3)); + g_print ("\tImage dimensions: %08x\n", GINT_FROM_LE (mhni->image_dimensions)); +} + +G_GNUC_INTERNAL void +dump_mhod (MhodHeader *mhod) +{ + g_print ("MHOD (%d):\n", sizeof (MhodHeader)); + g_print ("\tHeader length: %d\n", GINT_FROM_LE (mhod->header_len)); + g_print ("\tTotal length: %d\n", GINT_FROM_LE (mhod->total_len)); + g_print ("\tType: %d\n", GINT_FROM_LE (mhod->type)); + g_print ("\tUnknown1: %08x\n", GINT_FROM_LE (mhod->unknown1)); + g_print ("\tUnknown2: %08x\n", GINT_FROM_LE (mhod->unknown2)); +} + +G_GNUC_INTERNAL void +dump_mhii (MhiiHeader *mhii) +{ + g_print ("MHII (%d):\n", sizeof (MhiiHeader)); + g_print ("\tHeader length: %d\n", GINT_FROM_LE (mhii->header_len)); + g_print ("\tTotal length: %d\n", GINT_FROM_LE (mhii->total_len)); + g_print ("\tNumber of children: %d\n", GINT_FROM_LE (mhii->num_children)); + g_print ("\tImage ID: %08x\n", GINT_FROM_LE (mhii->image_id)); + g_print ("\tSong ID: %016llx\n", GINT64_FROM_LE (mhii->song_id)); + g_print ("\tUnknown4: %08x\n", GINT_FROM_LE (mhii->unknown4)); + g_print ("\tUnknown5: %08x\n", GINT_FROM_LE (mhii->unknown5)); + g_print ("\tUnknown6: %08x\n", GINT_FROM_LE (mhii->unknown6)); + g_print ("\tUnknown7: %08x\n", GINT_FROM_LE (mhii->unknown7)); + g_print ("\tUnknown8: %08x\n", GINT_FROM_LE (mhii->unknown8)); + g_print ("\tImage size: %d bytes\n", GINT_FROM_LE (mhii->orig_img_size)); +} + +G_GNUC_INTERNAL void +dump_mhl (MhlHeader *mhl, const char *id) +{ + GString *str; + + str = g_string_new (id); + g_string_ascii_up (str); + g_print ("%s (%d):\n", str->str, sizeof (MhlHeader)); + g_print ("\tHeader size: %d\n", GINT_FROM_LE (mhl->header_len)); + g_print ("\tNumber of items: %d\n", GINT_FROM_LE (mhl->num_children)); + g_string_free (str, TRUE); +} + +G_GNUC_INTERNAL void +dump_mhsd (MhsdHeader *mhsd) +{ + g_print ("MHSD (%d):\n", sizeof (MhsdHeader)); + g_print ("\tHeader length: %d\n", GINT_FROM_LE (mhsd->header_len)); + g_print ("\tTotal length: %d\n", GINT_FROM_LE (mhsd->total_len)); + g_print ("\tIndex: %d ", GINT_FROM_LE (mhsd->index)); + switch (GINT_FROM_LE (mhsd->index)) { + case MHSD_IMAGE_LIST: + g_print ("(Image list)\n"); + break; + case MHSD_ALBUM_LIST: + g_print ("(Album list)\n"); + break; + case MHSD_FILE_LIST: + g_print ("(File list)\n"); + break; + + default: + g_print ("(Unknown index\n"); + break; + } +} + +G_GNUC_INTERNAL void +dump_mhfd (MhfdHeader *mhfd) +{ + g_print ("MHFD (%d): \n", sizeof (MhfdHeader)); + g_print ("\tHeader length: %d\n", GINT_FROM_LE (mhfd->header_len)); + g_print ("\tTotal length: %d\n", GINT_FROM_LE (mhfd->total_len)); + g_print ("\tUnknown1: %08x\n", GINT_FROM_LE (mhfd->unknown1)); + g_print ("\tUnknown2: %08x\n", GINT_FROM_LE (mhfd->unknown2)); + g_print ("\tNumber of children: %d\n", GINT_FROM_LE (mhfd->num_children)); + g_print ("\tUnknown3: %08x\n", GINT_FROM_LE (mhfd->unknown3)); + g_print ("\tUnknown4: %08x\n", GINT_FROM_LE (mhfd->unknown4)); + g_print ("\tUnknown5: %016llx\n", GINT64_FROM_LE (mhfd->unknown5)); + g_print ("\tUnknown6: %016llx\n", GINT64_FROM_LE (mhfd->unknown6)); + g_print ("\tUnknown7: %08x\n", GINT_FROM_LE (mhfd->unknown7)); + g_print ("\tUnknown8: %08x\n", GINT_FROM_LE (mhfd->unknown8)); + g_print ("\tUnknown9: %08x\n", GINT_FROM_LE (mhfd->unknown9)); + g_print ("\tUnknown10: %08x\n", GINT_FROM_LE (mhfd->unknown10)); + g_print ("\tUnknown11: %08x\n", GINT_FROM_LE (mhfd->unknown11)); +} +#endif diff --git a/src/db-artwork-debug.h b/src/db-artwork-debug.h new file mode 100644 index 0000000..fe9dd70 --- /dev/null +++ b/src/db-artwork-debug.h @@ -0,0 +1,51 @@ +/* + * 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! + * + */ +#ifndef DB_ARTWORK_DEBUG_H +#define DB_ARTWORK_DEBUG_H + +#include "db-itunes-parser.h" + +/*#define DEBUG_ARTWORKDB*/ + +#ifdef DEBUG_ARTWORKDB +extern G_GNUC_INTERNAL void dump_mhif (MhifHeader *mhif); +extern G_GNUC_INTERNAL void dump_mhod_type_3 (MhodHeaderArtworkType3 *mhod); +extern G_GNUC_INTERNAL void dump_mhni (MhniHeader *mhni); +extern G_GNUC_INTERNAL void dump_mhod (MhodHeader *mhod); +extern G_GNUC_INTERNAL void dump_mhii (MhiiHeader *mhii); +extern G_GNUC_INTERNAL void dump_mhl (MhlHeader *mhl, const char *id); +extern G_GNUC_INTERNAL void dump_mhsd (MhsdHeader *mhsd); +extern G_GNUC_INTERNAL void dump_mhfd (MhfdHeader *mhfd); +#else +#define dump_mhif(x) +#define dump_mhod_type_3(x) +#define dump_mhni(x) +#define dump_mhod(x) +#define dump_mhii(x) +#define dump_mhl(x,y) +#define dump_mhsd(x) +#define dump_mhfd(x) +#endif + +#endif diff --git a/src/db-artwork-parser.c b/src/db-artwork-parser.c new file mode 100644 index 0000000..69f3b83 --- /dev/null +++ b/src/db-artwork-parser.c @@ -0,0 +1,398 @@ +/* + * 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 "db-artwork-debug.h" +#include "db-artwork-parser.h" +#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); + +static Itdb_Track * +get_song_by_dbid (Itdb_iTunesDB *db, guint64 id) +{ + GList *it; + + for (it = db->tracks; it != NULL; it = it->next) { + Itdb_Track *song; + + song = (Itdb_Track*)it->data; + if (song->dbid == id) { + return song; + } + } + return NULL; +} + + +static int +parse_mhif (DBParseContext *ctx, Itdb_iTunesDB *db, GError *error) +{ + MhifHeader *mhif; + + mhif = db_parse_context_get_m_header (ctx, MhifHeader, "mhif"); + if (mhif == NULL) { + return -1; + } + dump_mhif (mhif); + db_parse_context_set_total_len (ctx, GINT_FROM_LE (mhif->total_len)); + return 0; +} + +#ifdef DEBUG_ARTWORKDB +static int +parse_mhod_3 (DBParseContext *ctx, GError *error) +{ + MhodHeader *mhod; + MhodHeaderArtworkType3 *mhod3; + + mhod = db_parse_context_get_m_header (ctx, MhodHeader, "mhod"); + if (mhod == NULL) { + return -1; + } + db_parse_context_set_total_len (ctx, GINT_FROM_LE (mhod->total_len)); + + if (GINT_FROM_LE (mhod->total_len) < sizeof (MhodHeaderArtworkType3)){ + return -1; + } + mhod3 = (MhodHeaderArtworkType3*)mhod; + dump_mhod_type_3 (mhod3); + if ((GINT_FROM_LE (mhod3->type) & 0xff) != MHOD_TYPE_ALBUM) { + return -1; + } + + return 0; +} +#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; + + mhni = db_parse_context_get_m_header (ctx, MhniHeader, "mhni"); + if (mhni == NULL) { + return -1; + } + db_parse_context_set_total_len (ctx, GINT_FROM_LE (mhni->total_len)); + + dump_mhni (mhni); +#ifdef DEBUG_ARTWORKDB + { + DBParseContext *mhod_ctx; + + /* No information useful to us in mhod type 3, do not parse + * it in non-debug mode + */ + mhod_ctx = db_parse_context_get_sub_context (ctx, ctx->header_len); + if (mhod_ctx == NULL) { + return -1; + } + parse_mhod_3 (mhod_ctx, NULL); + 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)); + } +#endif + return 0; +} + +static int +parse_mhod (DBParseContext *ctx, iPodSong *song, GError *error) +{ + MhodHeader *mhod; + DBParseContext *mhni_ctx; + + mhod = db_parse_context_get_m_header (ctx, MhodHeader, "mhod"); + if (mhod == NULL) { + return -1; + } + db_parse_context_set_total_len (ctx, GINT_FROM_LE (mhod->total_len)); + dump_mhod (mhod); + + if (GINT_FROM_LE (mhod->type) != MHOD_TYPE_LOCATION) { + return -1; + } + + mhni_ctx = db_parse_context_get_sub_context (ctx, ctx->header_len); + if (mhni_ctx == NULL) { + return -1; + } + parse_mhni (mhni_ctx, song, NULL); + g_free (mhni_ctx); + + return 0; +} + + +static int +parse_mhii (DBParseContext *ctx, Itdb_iTunesDB *db, GError *error) +{ + MhiiHeader *mhii; + DBParseContext *mhod_ctx; + int num_children; + off_t cur_offset; + iPodSong *song; + + mhii = db_parse_context_get_m_header (ctx, MhiiHeader, "mhii"); + if (mhii == NULL) { + return -1; + } + db_parse_context_set_total_len (ctx, GINT_FROM_LE (mhii->total_len)); + + dump_mhii (mhii); + +#ifdef DEBUG_ARTWORKDB + song = NULL; +#else + song = get_song_by_dbid (db, GINT64_FROM_LE (mhii->song_id)); + if (song == NULL) { + return -1; + } + + song->orig_image = ipod_image_new_from_mhii (mhii); +#endif + + cur_offset = ctx->header_len; + mhod_ctx = db_parse_context_get_sub_context (ctx, cur_offset); + num_children = GINT_FROM_LE (mhii->num_children); + while ((num_children > 0) && (mhod_ctx != NULL)) { + parse_mhod (mhod_ctx, song, NULL); + num_children--; + cur_offset += mhod_ctx->total_len; + g_free (mhod_ctx); + mhod_ctx = db_parse_context_get_sub_context (ctx, cur_offset); + } + + return 0; +} + + +static int +parse_mhl (DBParseContext *ctx, Itdb_iTunesDB *db, GError *error, + const char *id, ParseListItem parse_child) +{ + MhlHeader *mhl; + int num_children; + DBParseContext *mhi_ctx; + off_t cur_offset; + + mhl = db_parse_context_get_m_header (ctx, MhlHeader, id); + if (mhl == NULL) { + return -1; + } + + dump_mhl (mhl, id); + + num_children = GINT_FROM_LE (mhl->num_children); + if (num_children < 0) { + return -1; + } + + cur_offset = ctx->header_len; + mhi_ctx = db_parse_context_get_sub_context (ctx, cur_offset); + while ((num_children > 0) && (mhi_ctx != NULL)) { + if (parse_child != NULL) { + parse_child (mhi_ctx, db, NULL); + } + num_children--; + cur_offset += mhi_ctx->total_len; + g_free (mhi_ctx); + mhi_ctx = db_parse_context_get_sub_context (ctx, cur_offset); + } + + return 0; + +} + + +static int +parse_mhsd (DBParseContext *ctx, Itdb_iTunesDB *db, GError **error) +{ + MhsdHeader *mhsd; + + mhsd = db_parse_context_get_m_header (ctx, MhsdHeader, "mhsd"); + if (mhsd == NULL) { + return -1; + } + + db_parse_context_set_total_len (ctx, GINT_FROM_LE (mhsd->total_len)); + dump_mhsd (mhsd); + switch (GINT_FROM_LE (mhsd->index)) { + case MHSD_IMAGE_LIST: { + DBParseContext *mhli_context; + mhli_context = db_parse_context_get_next_child (ctx); + parse_mhl (mhli_context, db, NULL, "mhli", parse_mhii); + g_free (mhli_context); + break; + } + case MHSD_ALBUM_LIST: { + DBParseContext *mhla_context; + mhla_context = db_parse_context_get_next_child (ctx); + parse_mhl (mhla_context, db, NULL, "mhla", NULL); + g_free (mhla_context); + break; + } + case MHSD_FILE_LIST: { + DBParseContext *mhlf_context; + mhlf_context = db_parse_context_get_next_child (ctx); + parse_mhl (mhlf_context, db, NULL, "mhlf", parse_mhif); + g_free (mhlf_context); + break; + } + default: + g_warning ("Unexpected mhsd index: %d\n", + GINT_FROM_LE (mhsd->index)); + return -1; + break; + } + + return 0; +} + +/* Database Object */ +static int +parse_mhfd (DBParseContext *ctx, Itdb_iTunesDB *db, GError **error) +{ + MhfdHeader *mhfd; + DBParseContext *mhsd_context; + unsigned int cur_pos; + + mhfd = db_parse_context_get_m_header (ctx, MhfdHeader, "mhfd"); + if (mhfd == NULL) { + return -1; + } + + /* Sanity check */ + g_assert (GINT_FROM_LE (mhfd->total_len) == ctx->total_len); + dump_mhfd (mhfd); + cur_pos = ctx->header_len; + + mhsd_context = db_parse_context_get_sub_context (ctx, cur_pos); + if (mhsd_context == NULL) { + return -1; + } + parse_mhsd (mhsd_context, db, NULL); + cur_pos += mhsd_context->total_len; + g_free (mhsd_context); + + mhsd_context = db_parse_context_get_sub_context (ctx, cur_pos); + if (mhsd_context == NULL) { + return -1; + } + parse_mhsd (mhsd_context, db, NULL); + cur_pos += mhsd_context->total_len; + g_free (mhsd_context); + + mhsd_context = db_parse_context_get_sub_context (ctx, cur_pos); + if (mhsd_context == NULL) { + return -1; + } + parse_mhsd (mhsd_context, db, NULL); + cur_pos += mhsd_context->total_len; + g_free (mhsd_context); + + return 0; +} + + +G_GNUC_INTERNAL char * +ipod_db_get_artwork_db_path (Itdb_iTunesDB *db) +{ + return g_build_filename (G_DIR_SEPARATOR_S, db->mountpoint, + "iPod_Control", "Artwork", "ArtworkDB", + NULL); +} + + +static char * +ipod_db_get_photo_db_path (const char *mount_point) +{ + g_return_val_if_fail (mount_point != NULL, NULL); + return g_build_filename (G_DIR_SEPARATOR_S, mount_point, + "Photos", "Photo Database", + NULL); +} + + +int +ipod_parse_artwork_db (Itdb_iTunesDB *db) +{ + DBParseContext *ctx; + char *filename; + + filename = ipod_db_get_artwork_db_path (db); + ctx = db_parse_context_new_from_file (filename); + g_free (filename); + if (ctx == NULL) { + goto error; + } + + parse_mhfd (ctx, db, NULL); + g_free (ctx); + return 0; + + error: + /* FIXME: needs to destroy ctx and to release the mmap'ed memory*/ + return -1; +} + +int +ipod_parse_photo_db (const char *mount_point) +{ + DBParseContext *ctx; + char *filename; + + filename = ipod_db_get_photo_db_path (mount_point); + if (filename == NULL) { + return -1; + } + ctx = db_parse_context_new_from_file (filename); + g_free (filename); + if (ctx == NULL) { + return -1; + } + + parse_mhfd (ctx, NULL, NULL); + g_free (ctx); + return 0; +} diff --git a/src/db-artwork-parser.h b/src/db-artwork-parser.h new file mode 100644 index 0000000..06b0e09 --- /dev/null +++ b/src/db-artwork-parser.h @@ -0,0 +1,39 @@ +/* + * 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! + * + */ + + +#ifndef DB_PHOTO_PARSER_H +#define DB_PHOTO_PARSER_H + +#include "itdb.h" + +#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); + +G_GNUC_INTERNAL char *ipod_db_get_artwork_db_path (Itdb_iTunesDB *db); + +#endif diff --git a/src/db-image-parser.c b/src/db-image-parser.c new file mode 100644 index 0000000..a87485f --- /dev/null +++ b/src/db-image-parser.c @@ -0,0 +1,220 @@ +/* + * 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 <errno.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> + +#include <glib.h> +#include <glib-object.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) +{ + unsigned char *result; + unsigned int i; + + result = g_malloc ((bytes_len/2) * 3); + if (result == NULL) { + return NULL; + } + for (i = 0; i < bytes_len/2; i++) { + gushort cur_pixel; + + cur_pixel = GINT16_FROM_LE (pixels[i]); + /* Unpack pixels */ + result[3*i] = (pixels[i] & RED_MASK) >> RED_SHIFT; + result[3*i+1] = (pixels[i] & GREEN_MASK) >> GREEN_SHIFT; + result[3*i+2] = (pixels[i] & BLUE_MASK) >> BLUE_SHIFT; + + /* Normalize color values so that they use a [0..255] range */ + result[3*i] <<= (8 - RED_BITS); + result[3*i+1] <<= (8 - GREEN_BITS); + result[3*i+2] <<= (8 - BLUE_BITS); + } + + 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) +{ + unsigned char *result; + FILE *f; + int res; + + f = NULL; + result = g_malloc (image->size); + if (result == NULL) { + return NULL; + } + + f = fopen (image->filename, "r"); + if (f == NULL) { + g_print ("Failed to open %s: %s\n", + image->filename, strerror (errno)); + goto end; + } + + res = fseek (f, image->offset, SEEK_SET); + if (res != 0) { + g_print ("Seek to %lu failed on %s: %s\n", + image->offset, image->filename, strerror (errno)); + goto end; + } + + res = fread (result, image->size, 1, f); + if (res != 1) { + g_print ("Failed to read %u bytes from %s: %s\n", + image->size, image->filename, strerror (errno)); + goto end; + } + fclose (f); + + return result; + + end: + if (f != NULL) { + fclose (f); + } + g_free (result); + + return NULL; +} + +unsigned char * +itdb_image_get_rgb_data (Itdb_Image *image) +{ + void *pixels565; + void *pixels; + + pixels565 = get_pixel_data (image); + if (pixels565 == NULL) { + return NULL; + } + + pixels = unpack_RGB_565 (pixels565, image->size); + g_free (pixels565); + + return pixels; + + /* return gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, FALSE, + 8, image->width, image->height, + image->width * 3, + (GdkPixbufDestroyNotify)g_free, + NULL); + */ +} + +G_GNUC_INTERNAL Itdb_Image * +ipod_image_new_from_mhni (MhniHeader *mhni, const char *mount_point) +{ + Itdb_Image *img; + + img = g_new0 (Itdb_Image, 1); + 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->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) { + return NULL; + } + img->size = GINT_FROM_LE (mhii->orig_img_size); + img->id = GINT_FROM_LE (mhii->image_id); + + return img; +} diff --git a/src/db-image-parser.h b/src/db-image-parser.h new file mode 100644 index 0000000..0c36cc4 --- /dev/null +++ b/src/db-image-parser.h @@ -0,0 +1,34 @@ +/* + * 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! + * + */ + +#ifndef IMAGE_PARSER_H +#define IMAGE_PARSER_H + +#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); + +#endif diff --git a/src/db-itunes-parser.h b/src/db-itunes-parser.h new file mode 100644 index 0000000..bf5f7f2 --- /dev/null +++ b/src/db-itunes-parser.h @@ -0,0 +1,540 @@ +/* + * 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! + * + */ + + +#ifndef DB_PARSER_H +#define DB_PARSER_H + +#include <glib.h> +/*#include "ipod-db-parser.h"*/ + +#define ITUNESDB_MAX_SIZE 10 * 1024 * 1024 + +struct _MHeader { + unsigned char header_id[4]; + gint32 header_len; +}; + +typedef struct _MHeader MHeader; + +struct _MhlHeader { + unsigned char header_id[4]; + gint32 header_len; + gint32 num_children; + unsigned char padding[]; +}; + +typedef struct _MhlHeader MhlHeader; + + +typedef struct _MhbdHeader MhbdHeader; +typedef struct _MhsdHeader MhsdHeader; +typedef struct _MhltHeader MhltHeader; +typedef struct _MhlpHeader MhlpHeader; +typedef struct _MhypHeader MhypHeader; +typedef struct _MhipHeader MhipHeader; +typedef struct _MhitHeader MhitHeader; +typedef struct _MhodHeader MhodHeader; +typedef struct _MhfdHeader MhfdHeader; +typedef struct _MhliHeader MhliHeader; +typedef struct _MhiiHeader MhiiHeader; +typedef struct _MhniHeader MhniHeader; +typedef struct _MhlaHeader MhlaHeader; +typedef struct _MhlfHeader MhlfHeader; +typedef struct _MhifHeader MhifHeader; + +typedef struct _MhitHeader471 MhitHeader471; +/* MHOD typedef mess */ +typedef struct _MhodHeaderString MhodHeaderString; +typedef struct _MhodHeaderArtworkType3 MhodHeaderArtworkType3; +typedef struct _MhodHeaderSmartPlaylistData MhodHeaderSmartPlaylistData; +typedef struct _MhodHeaderSmartPlaylistRuleString MhodHeaderSmartPlaylistRuleString; +typedef struct _MhodHeaderSmartPlaylistRuleNonString MhodHeaderSmartPlaylistRuleNonString; +typedef struct _MhodHeaderSmartPlaylistRule MhodHeaderSmartPlaylistRule; + +struct _MhbdHeader { + unsigned char header_id[4]; + gint32 header_len; + gint32 total_len; + gint32 unknown1; + gint32 version; + gint32 num_children; + gint64 db_id; + gint32 unknown2; + unsigned char padding[]; +}; + +enum MhsdIndexType { + MHSD_TRACK_LIST = 1, + MHSD_PLAYLIST_LIST = 2 +}; + +enum MhsdPhotoIndexType { + MHSD_IMAGE_LIST = 1, + MHSD_ALBUM_LIST = 2, + MHSD_FILE_LIST = 3 +}; + +struct _MhsdHeader { + unsigned char header_id[4]; + gint32 header_len; + gint32 total_len; + gint32 index; + unsigned char padding[]; +}; + +struct _MhltHeader { + unsigned char header_id[4]; + gint32 header_len; + gint32 num_songs; + unsigned char padding[]; +}; + +struct _MhlpHeader { + unsigned char header_id[4]; + gint32 header_len; + gint32 num_playlists; + unsigned char padding[]; +}; + +struct _MhypHeader { + unsigned char header_id[4]; + gint32 header_len; + gint32 total_len; + gint32 num_mhod; + gint32 num_items; + gint32 hidden; + gint32 timestamp; + gint32 playlist_id; + gint32 unknown3; + gint32 unknown4; + gint32 unknown5; + unsigned char padding[]; +}; + +struct _MhipHeader { + unsigned char header_id[4]; + gint32 header_len; + gint32 total_len; + gint32 unknown1; + gint32 correlation_id; + gint32 unknown2; + gint32 track_id; + gint32 timestamp; + unsigned char padding[]; +}; + +/* MHIT header as written by iTunes 4.7.1, the added fields (from unknown17 to + * the end of the struct) are pretty useless, so we don't use this struct + */ +struct _MhitHeader471 { + unsigned char header_id[4]; + gint32 header_len; + gint32 total_len; + gint32 num_mhod; + gint32 track_id; + gint32 visible; + gint32 filetype; + /* FIXME: endianness issue with the order of the 3 fields above ? */ + gint16 type; + gchar compilation; + gchar rating; + gint32 date_added; + gint32 size; + gint32 length; + gint32 track_number; + gint32 track_total; + gint32 year; + gint32 bitrate; + /* FIXME: endianness issue with the order of the 2 fields above ? */ + gint16 unknown; + gint16 sample_rate; + gint32 volume; + gint32 start_time; + gint32 stop_time; + gint32 sound_check; + gint32 play_count; + gint32 play_count2; + gint32 last_played; + gint32 disc_number; + gint32 disc_total; + gint32 user_id; + gint32 last_modified; + gint32 bookmark_time; + gint64 song_id; + /* FIXME: endianness issue with the order of the 5 fields above ? */ + gchar checked; + gchar app_rating; + gint16 bpm; + gint16 artwork_count; + gint16 unknown9; + gint32 artwork_size; + gint32 unknown11; + gint32 sample_rate2; + gint32 unknown13; + gint32 unknown14; + gint32 unknown15; + gint32 unknown16; + gint32 unknown17; + gint32 unknown18; + gint32 unknown19; + gint64 song_id2; + gint32 unknown20; + gint32 unknown21; + gint32 unknown22; + gint32 unknown23; + gint32 unknown24; + gint32 unknown25; + gint32 unknown26; + gint32 unknown27; + gint32 unknown28; + gint32 unknown29; + gint32 unknown30; + gint32 unknown31; + gint32 unknown32; + gint32 unknown33; + gint32 unknown34; + gint32 unknown35; + gint32 unknown36; + unsigned char padding[]; +}; + +struct _MhitHeader { + unsigned char header_id[4]; + gint32 header_len; + gint32 total_len; + gint32 num_mhod; + gint32 track_id; + gint32 visible; + gint32 filetype; + /* FIXME: endianness issue with the order of the 3 fields above ? */ + gint16 type; + gchar compilation; + gchar rating; + gint32 date_added; + gint32 size; + gint32 length; + gint32 track_number; + gint32 track_total; + gint32 year; + gint32 bitrate; + /* FIXME: endianness issue with the order of the 2 fields above ? */ + gint16 unknown; + gint16 sample_rate; + gint32 volume; + gint32 start_time; + gint32 stop_time; + gint32 sound_check; + gint32 play_count; + gint32 play_count2; + gint32 last_played; + gint32 disc_number; + gint32 disc_total; + gint32 user_id; + gint32 last_modified; + gint32 bookmark_time; + gint64 song_id; + /* FIXME: endianness issue with the order of the 5 fields above ? */ + gchar checked; + gchar app_rating; + gint16 bpm; + gint16 artwork_count; + gint16 unknown9; + gint32 artwork_size; + gint32 unknown11; + gint32 sample_rate2; + gint32 unknown13; + gint32 unknown14; + gint32 unknown15; + gint32 unknown16; + unsigned char padding[]; +}; + +enum MhodEncoding { + MHOD_ENCODING_UTF16 = 0, + MHOD_ENCODING_UTF8 = 1 +}; + +enum MhodType { + MHOD_TYPE_TITLE = 1, + MHOD_TYPE_LOCATION = 2, + MHOD_TYPE_ALBUM = 3, + MHOD_TYPE_ARTIST = 4, + MHOD_TYPE_GENRE = 5, + MHOD_TYPE_FILETYPE = 6, + MHOD_TYPE_EQ_SETTING = 7, + MHOD_TYPE_COMMENT = 8, + MHOD_TYPE_COMPOSER = 12, + MHOD_TYPE_GROUPING = 13, + MHOD_TYPE_SMART_PLAYLIST_DATA = 50, + MHOD_TYPE_SMART_PLAYLIST_RULES = 51, + MHOD_TYPE_LIBRARY_PLAYLIST_INDEX = 52, + MHOD_TYPE_100 = 100 +}; + +struct _MhodHeader { + unsigned char header_id[4]; + gint32 header_len; + gint32 total_len; + gint32 type; + gint32 unknown1; + gint32 unknown2; +}; + +struct _MhodHeaderString { + unsigned char header_id[4]; + gint32 header_len; + gint32 total_len; + gint32 type; /* < 50 */ + gint32 unknown1; + gint32 unknown2; + gint32 position; + gint32 string_len; + gint32 encoding; + gint32 unknown4; + unsigned char string[]; +}; + +struct _MhodHeaderArtworkType3 { + unsigned char header_id[4]; + gint32 header_len; + gint32 total_len; + gint32 type; /* 3 */ + gint32 unknown1; + gint32 unknown2; + gint32 string_len; + gint32 mhod_version; + gint32 unknown4; + gunichar2 string[]; +}; + +enum MhodLimitType { + MHOD_LIMIT_MINUTES = 1, + MHOD_LIMIT_MEGABYTES = 2, + MHOD_LIMIT_SONGS = 3, + MHOD_LIMIT_HOURS = 4, + MHOD_LIMIT_GIGABYTES = 5 +}; + +enum MhodLimitSortType { + MHOD_LIMIT_SORT_RANDOM = 0x02, + MHOD_LIMIT_SORT_SONG_NAME = 0x03, + MHOD_LIMIT_SORT_ALBUM = 0x04, + MHOD_LIMIT_SORT_ARTIST = 0x05, + MHOD_LIMIT_SORT_GENRE = 0x07, + MHOD_LIMIT_SORT_MOST_RECENTLY_ADDED = 0x10, + MHOD_LIMIT_SORT_MOST_OFTEN_PLAYED = 0x14, + MHOD_LIMIT_SORT_MOST_RECENTLY_PLAYED = 0x15, + MHOD_LIMIT_SORT_HIGHEST_RATING = 0x17 +}; + +struct _MhodHeaderSmartPlaylistData { + unsigned char header_id[4]; + gint32 header_len; + gint32 total_len; + gint32 type; /* 50 */ + gint32 unknown1; + gint32 unknown2; + gchar live_update; + gchar rules_enable; + gchar limit_enable; + gchar limit_type; + gchar limit_sort; + gchar unknow3[3]; + gint32 limit; + gchar match_checked_only; + gchar reverse_limit_sort; + unsigned char padding[]; +}; + + +enum MhodSmartPlaylistRuleFieldType { + MHOD_FIELD_SONG_NAME = 0x02, + MHOD_FIELD_ALBUM = 0x03, + MHOD_FIELD_ARTIST = 0x04, + MHOD_FIELD_BITRATE = 0x05, + MHOD_FIELD_SAMPLE_RATE = 0x06, + MHOD_FIELD_YEAR = 0x07, + MHOD_FIELD_GENRE = 0x08, + MHOD_FIELD_KIND = 0x09, + MHOD_FIELD_DATE_MODIFIED = 0x0a, + MHOD_FIELD_TRACK_NUMBER = 0x0b, + MHOD_FIELD_SIZE = 0x0c, + MHOD_FIELD_TIME = 0x0d, + MHOD_FIELD_COMMENT = 0x0e, + MHOD_FIELD_DATE_ADDED = 0x10, + MHOD_FIELD_COMPOSER = 0x12, + MHOD_FIELD_PLAY_COUNT = 0x16, + MHOD_FIELD_LAST_PLAYED = 0x17, + MHOD_FIELD_DISC_NUMBER = 0x18, + MHOD_FIELD_RATING = 0x19, + MHOD_FIELD_COMPILATION = 0x1f, + MHOD_FIELD_BPM = 0x23, + MHOD_FIELD_GROUPING = 0x27, + MHOD_FIELD_PLAYLIST = 0x28 +}; + +enum MhodSmartPlaylistRuleAction { + MHOD_RULE_IS = 1 << 0, + MHOD_RULE_CONTAINS = 1 << 1, + MHOD_RULE_BEGINS_WITH = 1 << 2, + MHOD_RULE_ENDS_WITH = 1 << 3, + MHOD_RULE_GREATER_THAN = 1 << 4, + MHOD_RULE_GREATER_THAN_OR_EQUAL_TO = 1 << 5, + MHOD_RULE_LESS_THAN = 1 << 6, + MHOD_RULE_LESS_THAN_OR_EQUAL_TO = 1 << 7, + MHOD_RULE_IN_THE_RANGE = 1 << 8, + MHOD_RULE_IS_THE_LAST = 1 << 9, + MHOD_RULE_NOT = 1 << 24, + MHOD_RULE_STRING = 1 << 25 +}; + + +struct _MhodHeaderSmartPlaylistRuleString { + /* Big endian fields */ + gint32 field; + gint32 action; + gchar padding[44]; + gint32 string_len; + gchar string[]; +}; + +struct _MhodHeaderSmartPlaylistRuleNonString { + /* Big endian fields */ + gint32 field; + gint32 action; + gchar padding[44]; + gint32 length; + guint64 from_value; + gint64 from_date; + guint64 from_unit; + guint64 to_value; + gint64 to_date; + guint64 to_unit; + gchar unknown[20]; +}; + +struct _MhodHeaderSmartPlaylistRule { + unsigned char header_id[4]; + gint32 header_len; + gint32 total_len; + gint32 type; /* 51 */ + gint32 unknown1; + gint32 unknown2; + gchar rules_id[4]; + /* Fields stored in big-endian from there */ + gint32 unknown5; + gint32 number_of_rules; + gint32 rules_operator; + gchar padding[120]; + union { + MhodHeaderSmartPlaylistRuleString rule_string; + MhodHeaderSmartPlaylistRuleNonString rule_non_string; + } rule; +}; + + + +struct _MhfdHeader { + unsigned char header_id[4]; + gint32 header_len; + gint32 total_len; + gint32 unknown1; + gint32 unknown2; + gint32 num_children; + gint32 unknown3; + gint32 unknown4; + gint64 unknown5; + gint64 unknown6; + gint32 unknown7; + gint32 unknown8; + gint32 unknown9; + gint32 unknown10; + gint32 unknown11; + unsigned char padding[]; +}; + +struct _MhliHeader { + unsigned char header_id[4]; + gint32 header_len; + gint32 num_children; + unsigned char padding[]; +}; + +struct _MhiiHeader { + unsigned char header_id[4]; + gint32 header_len; + gint32 total_len; + gint32 num_children; + gint32 image_id; + gint64 song_id; + gint32 unknown4; + gint32 unknown5; + gint32 unknown6; + gint32 unknown7; + gint32 unknown8; + gint32 orig_img_size; + unsigned char padding[]; +}; + +struct _MhniHeader { + unsigned char header_id[4]; + gint32 header_len; + gint32 total_len; + gint32 num_children; + gint32 correlation_id; + gint32 ithmb_offset; + gint32 image_size; + gint32 unknown3; + gint32 image_dimensions; + unsigned char padding[]; +}; + +struct _MhlaHeader { + unsigned char header_id[4]; + gint32 header_len; + gint32 num_children; + unsigned char padding[]; +}; + +struct _MhlfHeader { + unsigned char header_id[4]; + gint32 header_len; + gint32 num_files; + unsigned char padding[]; +}; + +struct _MhifHeader { + unsigned char header_id[4]; + gint32 header_len; + gint32 total_len; + gint32 unknown1; + gint32 correlation_id; + gint32 image_size; + unsigned char padding[]; +}; + + + +#endif /* PARSE_DB_H */ diff --git a/src/db-parse-context.c b/src/db-parse-context.c new file mode 100644 index 0000000..4f4264e --- /dev/null +++ b/src/db-parse-context.c @@ -0,0 +1,196 @@ +/* + * 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 <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> + +#include <glib.h> + +#include "db-parse-context.h" +#include "db-itunes-parser.h" + +DBParseContext * +db_parse_context_new (const unsigned char *buffer, off_t len) +{ + DBParseContext *result; + + result = g_new0 (DBParseContext, 1); + if (result == NULL) { + return NULL; + } + + result->buffer = buffer; + result->cur_pos = buffer; + result->total_len = len; + + return result; +} + + +static void +db_parse_context_set_header_len (DBParseContext *ctx, off_t len) +{ + /* FIXME: this can probably happen in malformed itunesdb files, + * don't g_assert on this, only output a warning + */ + g_assert ((ctx->cur_pos - ctx->buffer) <= len); + g_assert (len <= ctx->total_len); + ctx->header_len = len; +} + +void +db_parse_context_set_total_len (DBParseContext *ctx, off_t len) +{ + /* FIXME: this can probably happen in malformed itunesdb files, + * don't g_assert on this, only output a warning + */ + g_assert ((ctx->cur_pos - ctx->buffer) <= len); + if (ctx->header_len != 0) { + g_assert (len >= ctx->header_len); + } + ctx->total_len = len; +} + + +off_t +db_parse_context_get_remaining_length (DBParseContext *ctx) +{ + if (ctx->header_len != 0) { + return ctx->header_len - (ctx->cur_pos - ctx->buffer); + } else { + return ctx->total_len - (ctx->cur_pos - ctx->buffer); + } +} + +DBParseContext * +db_parse_context_get_sub_context (DBParseContext *ctx, off_t offset) +{ + if (offset >= ctx->total_len) { + return NULL; + } + return db_parse_context_new (&ctx->buffer[offset], + ctx->total_len - offset); +} + + +DBParseContext * +db_parse_context_get_next_child (DBParseContext *ctx) +{ + if (ctx->header_len == 0) { + return NULL; + } + if (ctx->header_len >= ctx->total_len) { + return NULL; + } + + return db_parse_context_get_sub_context (ctx, ctx->header_len); +} + +void * +db_parse_context_get_m_header_internal (DBParseContext *ctx, const char *id, off_t size) +{ + MHeader *h; + + if (db_parse_context_get_remaining_length (ctx) < 8) { + return NULL; + } + + h = (MHeader *)ctx->cur_pos; + if (strncmp (id, (char *)h->header_id, 4) != 0) { + return NULL; + } + + /* FIXME: this test sucks for compat: if a field is smaller than + * expected, we probably should create a buffer of the appropriate + * size inited to 0, copy the data that is available in it and use + * that buffer in the rest of the code (maybe it's harmful to have + * some fields at 0 in some headers though...) + */ + if (GINT_FROM_LE (h->header_len) < size) { + return NULL; + } + + db_parse_context_set_header_len (ctx, GINT_FROM_LE (h->header_len)); + + return h; +} + +DBParseContext * +db_parse_context_new_from_file (const char *filename) +{ + int fd; + struct stat stat_buf; + int result; + unsigned char *buffer; + DBParseContext *ctx; + + buffer = NULL; + ctx = NULL; + + fd = open (filename, O_RDONLY); + if (fd == -1) { + g_print ("Failed to open %s: %s\n", + filename, strerror (errno)); + return NULL; + } + + result = fstat (fd, &stat_buf); + if (result == -1) { + g_print ("Failed to read %s size: %s\n", + filename, strerror (errno)); + goto error; + } + + if (!S_ISREG (stat_buf.st_mode)) { + g_print ("%s is not a regular file\n", filename); + goto error; + } + + if (stat_buf.st_size > ITUNESDB_MAX_SIZE) { + g_print ("%s is too big to be an buffer file\n", filename); + goto error; + } + + buffer = mmap (NULL, stat_buf.st_size, PROT_READ, MAP_SHARED, fd, 0); + + if (buffer == MAP_FAILED) { + g_print ("Error while mmap'ing %s: %s\n", + filename, strerror (errno)); + goto error; + } + + ctx = db_parse_context_new (buffer, stat_buf.st_size); + if (ctx == NULL) { + munmap (buffer, stat_buf.st_size); + } + error: + close (fd); + return ctx; +} diff --git a/src/db-parse-context.h b/src/db-parse-context.h new file mode 100644 index 0000000..b442c36 --- /dev/null +++ b/src/db-parse-context.h @@ -0,0 +1,53 @@ +/* + * 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! + * + */ + + +#ifndef DB_PARSE_CONTEXT +#define DB_PARSE_CONTEXT + +#include <sys/types.h> + +struct _DBParseContext { + const unsigned char *buffer; + const unsigned char *cur_pos; + off_t header_len; + off_t total_len; +}; + +typedef struct _DBParseContext DBParseContext; + + +#define db_parse_context_get_m_header(ctx, type, id) (type *)db_parse_context_get_m_header_internal (ctx, id, sizeof (type)) + +G_GNUC_INTERNAL DBParseContext *db_parse_context_new (const unsigned char *buffer, off_t len) G_GNUC_INTERNAL; +G_GNUC_INTERNAL void db_parse_context_set_total_len (DBParseContext *ctx, off_t len) G_GNUC_INTERNAL; +G_GNUC_INTERNAL off_t db_parse_context_get_remaining_length (DBParseContext *ctx) G_GNUC_INTERNAL; +G_GNUC_INTERNAL DBParseContext *db_parse_context_get_sub_context (DBParseContext *ctx, + off_t offset) G_GNUC_INTERNAL; +G_GNUC_INTERNAL DBParseContext *db_parse_context_get_next_child (DBParseContext *ctx) G_GNUC_INTERNAL; +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; + +#endif @@ -1,4 +1,4 @@ -/* Time-stamp: <2005-09-19 17:50:40 jcs> +/* Time-stamp: <2005-09-19 19:14:40 jcs> | | Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net> | Part of the gtkpod project. @@ -40,10 +40,10 @@ # include <config.h> #endif +#include <sys/types.h> #include <time.h> #include <glib.h> - /* one star is how much (track->rating) */ #define ITDB_RATING_STEP 20 @@ -307,6 +307,27 @@ typedef struct SPLRules GList *rules; } SPLRules; +/* 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), + * for such images, filename points to a 'real' image file, offset is + * not significant, size, width and height may or may not be set + * and id corresponds to the image id to write in mhii records of the + * photo database + * + * - a thumbnail (big or small) stored on a database in the iPod. + * For such images, id isn't significant, filename point to a .ithmb file + * on the iPod + */ +struct _Itdb_Image { + 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); typedef gpointer (* ItdbUserDataDuplicateFunc) (gpointer userdata); @@ -484,6 +505,11 @@ typedef struct guint32 unk208, unk212, unk216, unk220, unk224; 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; + /* below is for use by application */ guint64 usertype; gpointer userdata; @@ -586,6 +612,9 @@ gboolean itdb_splr_eval (Itdb_iTunesDB *itdb, SPLRule *splr, Itdb_Track *track); void itdb_spl_update (Itdb_iTunesDB *itdb, Itdb_Playlist *spl); void itdb_spl_update_all (Itdb_iTunesDB *itdb); +/* thumbnails functions */ +unsigned char *itdb_image_get_rgb_data (Itdb_Image *image); + /* time functions */ guint64 itdb_time_get_mac_time (void); time_t itdb_time_mac_to_host (guint64 mactime); diff --git a/src/itdb_itunesdb.c b/src/itdb_itunesdb.c index ad6d747..9ed1b52 100644 --- a/src/itdb_itunesdb.c +++ b/src/itdb_itunesdb.c @@ -120,6 +120,7 @@ #include <errno.h> #include <stdio.h> #include "itdb_private.h" +#include "db-artwork-parser.h" #include <glib/gi18n-lib.h> #define ITUNESDB_DEBUG 0 @@ -1981,6 +1982,19 @@ Itdb_iTunesDB *itdb_parse (const gchar *mp, GError **error) itdb->mountpoint = g_strdup (mp); } g_free (filename); + + /* We don't test the return value of ipod_parse_artwork_db since the + * database content will be consistent even if we fail to get the + * various thumbnails, we ignore the error since older ipods don't have + * thumbnails. + * FIXME: this probably should go into itdb_parse_file, but I don't + * understand its purpose, and ipod_parse_artwork_db needs the + * mountpoint field from the itdb, which may not be available in the + * other function + */ + ipod_parse_artwork_db (itdb); + + } else { @@ -2034,6 +2048,7 @@ Itdb_iTunesDB *itdb_parse_file (const gchar *filename, GError **error) g_propagate_error (error, fimp->error); } itdb_free_fimp (fimp); + return itdb; } |