summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/itdb.h45
-rw-r--r--src/itdb_itunesdb.c1212
-rw-r--r--src/itdb_private.h3
3 files changed, 774 insertions, 486 deletions
diff --git a/src/itdb.h b/src/itdb.h
index 1b19c6d..305fa25 100644
--- a/src/itdb.h
+++ b/src/itdb.h
@@ -1,4 +1,4 @@
-/* Time-stamp: <2005-09-19 22:52:40 jcs>
+/* Time-stamp: <2005-09-24 01:48:50 jcs>
|
| Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>
| Part of the gtkpod project.
@@ -53,6 +53,12 @@ enum ItdbPlType { /* types for playlist->type */
not visible in iPod */
};
+enum ItdbPlFlag { /* types for playlist->podcastflag */
+ ITDB_PL_FLAG_NORM = 0, /* normal playlist, visible under
+ 'Playlists */
+ ITDB_PL_FLAG_PODCAST = 1 /* special podcast playlist visible
+ under 'Music' */
+};
/* Most of the knowledge about smart playlists has been provided by
@@ -348,6 +354,7 @@ typedef struct
gint musicdirs; /* number of /iPod_Control/Music/F.. dirs */
guint32 version;
guint64 id;
+ guint32 next_id; /* strictly internal during itdb write */
/* below is for use by application */
guint64 usertype;
gpointer userdata;
@@ -362,14 +369,30 @@ typedef struct
{
Itdb_iTunesDB *itdb; /* pointer to iTunesDB (for convenience) */
gchar *name; /* name of playlist in UTF8 */
- guint32 type; /* PL_TYPE_MPL: master play list */
+ guint32 type; /* ITDB_PL_TYPE_NORM/_MPL */
gint num; /* number of tracks in playlist */
GList *members; /* tracks in playlist (Track *) */
gboolean is_spl; /* smart playlist? */
guint32 timestamp; /* some timestamp */
guint64 id; /* playlist ID */
- guint32 unk036, unk040;
+ guint32 mhodcount; /* This appears to be the number of string
+ MHODs (type < 50) associated with this
+ playlist (typically 0x01). Doesn't seem
+ to be signficant unless you include Type
+ 52 MHODs. libgpod sets this to 1 when
+ syncing */
+ guint16 libmhodcount; /* The number of Type 52 MHODs associated
+ with this playlist. If you don't create
+ Type 52 MHODs, this can be
+ zero. Otherwise, if you have Type 52
+ MHODs associated with this playlist and
+ set this to zero, no songs appear on the
+ iPod. jcsjcs: with iTunes 4.9 this seems
+ to be set to 1 even without any Type 52
+ MHODs present. libgpod sets this to 1
+ when syncing */
guint32 sortorder; /* How to sort playlist -- see below */
+ guint32 podcastflag; /* ITDB_PL_FLAG_NORM/_PODCAST */
SPLPref splpref; /* smart playlist prefs */
SPLRules splrules; /* rules for smart playlists */
/* below is for use by application */
@@ -538,9 +561,11 @@ typedef struct
(like WAVE format), 0x1 for Audible. itdb
will try to set this when adding a new track */
guint32 unk132; /* unknown */
- guint32 unk140; /* date/time added to music store? definitely a
- timestamp, always appears to be a time of
- 0700 GMT */
+ guint32 time_released;/* date/time added to music store? definitely a
+ timestamp, always appears to be a time of
+ 0700 GMT. For podcasts: release date as
+ displayed next to the title in the Podcast
+ playlist */
guint32 unk144; /* unknown, but MP3 songs appear to be always
0x0000000c or 0x0100000c (if played one or
more times in iTunes), AAC songs are always
@@ -579,6 +604,14 @@ typedef struct
guint32 unk208, unk212, unk216, unk220, unk224;
guint32 unk228, unk232, unk236, unk240;
+ /* Chapter data: defines where the chapter stops are in the track,
+ as well as what info should be displayed for each section of
+ the track. Until it can be parsed and interpreted, the
+ chapterdata will just be read as a block and written back on
+ sync. This will be changed at a later time */
+ void *chapterdata_raw;
+ guint32 chapterdata_raw_length;
+
/* This is for Cover Art support */
GList *thumbnails;
unsigned int image_id;
diff --git a/src/itdb_itunesdb.c b/src/itdb_itunesdb.c
index e8eacc3..bbaaba4 100644
--- a/src/itdb_itunesdb.c
+++ b/src/itdb_itunesdb.c
@@ -1,4 +1,4 @@
-/* Time-stamp: <2005-09-22 00:36:40 jcs>
+/* Time-stamp: <2005-09-24 01:56:22 jcs>
|
| Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>
| Part of the gtkpod project.
@@ -161,13 +161,13 @@ enum MHOD_ID {
MHOD_ID_PODCASTRSS = 16,
/* Chapter data. This is a m4a-style entry that is used to display
subsongs within a mhit. Introduced in db version 0x0d. */
-/* MHOD_ID_CHAPTERDATA = 17,*/
+ MHOD_ID_CHAPTERDATA = 17,
/* Subtitle (usually the same as Description). Introduced in db
version 0x0d. */
MHOD_ID_SUBTITLE = 18,
MHOD_ID_SPLPREF = 50, /* settings for smart playlist */
MHOD_ID_SPLRULES = 51, /* rules for smart playlist */
- MHOD_ID_MHYP = 52, /* unknown */
+ MHOD_ID_LIBPLAYLISTINDEX = 52, /* Library Playlist Index */
MHOD_ID_PLAYLIST = 100
};
@@ -941,7 +941,7 @@ static void *get_mhod (FContents *cts, gulong mhod_seek,
guint8 limitsort_opposite;
void *result = NULL;
gint32 xl;
- guint32 len;
+ guint32 mhod_len;
gint32 header_length;
gulong seek;
@@ -959,7 +959,7 @@ static void *get_mhod (FContents *cts, gulong mhod_seek,
g_return_val_if_fail (!cts->error, NULL);
- *mty = get_mhod_type (cts, mhod_seek, &len);
+ *mty = get_mhod_type (cts, mhod_seek, &mhod_len);
if (*mty == -1)
{
if (!cts->error)
@@ -983,7 +983,7 @@ static void *get_mhod (FContents *cts, gulong mhod_seek,
switch ((enum MHOD_ID)*mty)
{
- case MHOD_ID_MHYP:
+ case MHOD_ID_LIBPLAYLISTINDEX:
/* this is not yet supported */
case MHOD_ID_PLAYLIST:
/* return the position indicator */
@@ -1019,7 +1019,7 @@ static void *get_mhod (FContents *cts, gulong mhod_seek,
case MHOD_ID_PODCASTURL:
case MHOD_ID_PODCASTRSS:
/* length of string */
- xl = get32lint (cts, mhod_seek+8) - header_length;
+ xl = mhod_len - header_length;
if (cts->error) return NULL;
result = g_new0 (gchar, xl+1);
if (!seek_get_n_bytes (cts, (gchar *)result, seek, xl))
@@ -1028,6 +1028,16 @@ static void *get_mhod (FContents *cts, gulong mhod_seek,
result = NULL;
}
break;
+ case MHOD_ID_CHAPTERDATA:
+ /* we'll just copy the entire data section */
+ xl = mhod_len - header_length;
+ result = g_new0 (gchar, xl);
+ if (!seek_get_n_bytes (cts, result, seek, xl))
+ {
+ g_free (result);
+ result = NULL;
+ }
+ break;
case MHOD_ID_SPLPREF: /* Settings for smart playlist */
splp = g_new0 (SPLPref, 1);
splp->liveupdate = get8int (cts, seek);
@@ -1155,7 +1165,7 @@ static void *get_mhod (FContents *cts, gulong mhod_seek,
g_warning (_("Encountered unknown MHOD type (%d) while parsing the iTunesDB. Ignoring.\n\n"), *mty);
break;
}
- *ml = len;
+ *ml = mhod_len;
return result;
}
@@ -1191,8 +1201,9 @@ static gchar *get_mhod_string (FContents *cts, glong seek, guint32 *ml, gint32 *
break;
case MHOD_ID_SPLPREF:
case MHOD_ID_SPLRULES:
- case MHOD_ID_MHYP:
+ case MHOD_ID_LIBPLAYLISTINDEX:
case MHOD_ID_PLAYLIST:
+ case MHOD_ID_CHAPTERDATA:
/* these do not have a string entry */
break;
}
@@ -1200,67 +1211,353 @@ static gchar *get_mhod_string (FContents *cts, glong seek, guint32 *ml, gint32 *
}
-/* Get a playlist. Returns the position where the next playlist should
- be. On error -1 is returned and fimp->error is set appropriately. */
-static glong get_playlist (FImport *fimp, glong seek)
+/* convenience function: set error for zero length hunk */
+static void set_error_zero_length_hunk (GError **error, glong seek,
+ const gchar *filename)
{
- auto gint pos_comp (gpointer a, gpointer b);
- gint pos_comp (gpointer a, gpointer b)
- {
- return ((gint)a - (gint)b);
- }
+ g_set_error (error,
+ ITDB_FILE_ERROR,
+ ITDB_FILE_ERROR_CORRUPT,
+ _("iTunesDB corrupt: hunk length 0 for hunk at %ld in file '%s'."),
+ seek, filename);
+}
+
+/* convenience function: set error for missing hunk */
+static void set_error_a_not_found_in_b (GError **error,
+ const gchar *a,
+ const gchar *b,
+ glong b_seek)
+{
+ g_set_error (error,
+ ITDB_FILE_ERROR,
+ ITDB_FILE_ERROR_CORRUPT,
+ _("iTunesDB corrupt: no section '%s' found in section '%s' starting at %ld."),
+ a, b, b_seek);
+}
+
+/* convenience function: set error if header is smaller than expected */
+static void set_error_a_header_smaller_than_b (GError **error,
+ const gchar *a,
+ guint32 b, guint32 len,
+ glong a_seek,
+ const gchar *filename)
+{
+ g_set_error (error,
+ ITDB_FILE_ERROR,
+ ITDB_FILE_ERROR_CORRUPT,
+ _("header length of '%s' smaller than expected (%d < %d) at offset %ld in file '%s'."),
+ a, b, len, a_seek, filename);
+}
+
+
+/* finds next occurence of section @a in section b (@b_seek) starting
+ at @start_seek
+*/
+/* Return value:
+ -1 and cts->error not set: section @a could not be found
+ -1 and cts->error set: some error occured
+ >=0: start of next occurence of section @a
+*/
+static glong find_next_a_in_b (FContents *cts,
+ const gchar *a,
+ glong b_seek, glong start_seek)
+{
+ glong b_len;
+ glong offset, len;
+
+ g_return_val_if_fail (a, -1);
+ g_return_val_if_fail (cts, -1);
+ g_return_val_if_fail (strlen (a) == 4, -1);
+ g_return_val_if_fail (b_seek>=0, -1);
+ g_return_val_if_fail (start_seek >= b_seek, -1);
+
+/* printf ("%s: b_seek: %lx, start_seek: %lx\n", a, b_seek, start_seek); */
+
+ b_len = get32lint (cts, b_seek+8);
+ if (cts->error) return -1;
+
+ offset = start_seek - b_seek;
+ len = 0;
+ do
+ { /* skip headers inside the b hunk (b_len) until we find header
+ @a */
+ len = get32lint (cts, b_seek+offset+4);
+ if (cts->error) return -1;
+ if (len == 0)
+ { /* This needs to be checked, otherwise we might hang */
+ set_error_zero_length_hunk (&cts->error, b_seek+offset,
+ cts->filename);
+ return -1;
+ }
+ offset += len;
+/* printf ("offset: %lx, b_len: %lx, bseek+offset: %lx\n", */
+/* offset, b_len, b_seek+offset); */
+ } while ((offset < b_len-4) &&
+ !cmp_n_bytes_seek (cts, a, b_seek+offset, 4));
+ if (cts->error) return -1;
+
+ if (offset >= b_len) return -1;
+
+/* printf ("%s found at %lx\n", a, b_seek+offset); */
+
+ return b_seek+offset;
+}
+
+
+
+
+/* return the position of mhsd with type @type */
+/* Return value:
+ -1 if mhsd cannot be found. cts->error will not be set
+
+ 0 and cts->error is set if some other error occurs.
+ Since the mhsd can never be at position 0 (a mhbd must be there),
+ a return value of 0 always indicates an error.
+*/
+static glong find_mhsd (FContents *cts, guint32 type)
+{
+ guint32 i, len, mhsd_num;
+ glong seek;
+
+ if (!cmp_n_bytes_seek (cts, "mhbd", 0, 4))
+ {
+ if (!cts->error)
+ { /* set error */
+ g_set_error (&cts->error,
+ ITDB_FILE_ERROR,
+ ITDB_FILE_ERROR_CORRUPT,
+ _("Not a iTunesDB: '%s' (missing mhdb header)."),
+ cts->filename);
+ }
+ return 0;
+ }
+ len = get32lint (cts, 4);
+ if (cts->error) return 0;
+ /* all the headers I know are 0x68 long -- if this one is longer
+ we can could simply ignore the additional information */
+ /* Since 'we' (parse_fimp()) only need data from the first 32
+ bytes, don't complain unless it's smaller than that */
+ if (len < 32)
+ {
+ g_set_error (&cts->error,
+ ITDB_FILE_ERROR,
+ ITDB_FILE_ERROR_CORRUPT,
+ _("iTunesDB ('%s'): header length of mhsd hunk smaller than expected (%d<32). Aborting."),
+ cts->filename, len);
+ return FALSE;
+ }
+
+ mhsd_num = get32lint (cts, 20);
+ if (cts->error) return 0;
+
+ seek = 0;
+ for (i=0; i<mhsd_num; ++i)
+ {
+ guint32 mhsd_type;
+
+ seek += len;
+ if (!cmp_n_bytes_seek (cts, "mhsd", seek, 4))
+ {
+ if (!cts->error)
+ { /* set error */
+ g_set_error (&cts->error,
+ ITDB_FILE_ERROR,
+ ITDB_FILE_ERROR_CORRUPT,
+ _("iTunesDB '%s' corrupt: mhsd expected at %ld."),
+ cts->filename, seek);
+ }
+ return 0;
+ }
+ len = get32lint (cts, seek+8);
+ if (cts->error) return 0;
+
+ mhsd_type = get32lint (cts, seek+12);
+ if (cts->error) return 0;
+
+ if (mhsd_type == type) return seek;
+ }
+ return -1;
+}
+
+
+
+/* Read and process the mhip at @seek. Return a pointer to the next
+ possible mhip. */
+/* Return value: -1 if no mhip is present at @seek */
+static glong get_mhip (FImport *fimp, Itdb_Playlist *plitem,
+ glong mhip_seek)
+{
+ auto gint pos_comp (gpointer a, gpointer b);
+ gint pos_comp (gpointer a, gpointer b)
+ {
+ return ((gint)a - (gint)b);
+ }
+ FContents *cts;
+ guint32 mhip_hlen, mhip_len, mhod_num, mhod_seek;
+ Itdb_Track *tr;
+ gint32 i, pos=-1;
+ guint32 posid;
+ gint32 mhod_type;
+ guint32 trackid;
- gchar *plname_utf8 = NULL;
- guint32 i, tracknum, mhod_num;
- glong nextseek;
- guint32 hlen;
+ g_return_val_if_fail (fimp, -1);
+ g_return_val_if_fail (plitem, -1);
+
+ cts = fimp->itunesdb;
+
+ if (!cmp_n_bytes_seek (cts, "mhip", mhip_seek, 4))
+ {
+ CHECK_ERROR (fimp, -1);
+ return -1;
+ }
+
+ mhip_hlen = get32lint (cts, mhip_seek+4);
+ CHECK_ERROR (fimp, -1);
+
+ if (mhip_hlen < 36)
+ {
+ set_error_a_header_smaller_than_b (&fimp->error,
+ "mhip",
+ mhip_hlen, 36,
+ mhip_seek, cts->filename);
+ return -1;
+ }
+
+ /* Check if entire mhip header can be read -- that way we won't
+ have to check for read errors every time we access a single
+ byte */
+
+ check_seek (cts, mhip_seek, mhip_hlen);
+ CHECK_ERROR (fimp, -1);
+
+ mhip_len = get32lint (cts, mhip_seek+8);
+ mhod_num = get32lint (cts, mhip_seek+12);
+ trackid = get32lint(cts, mhip_seek+24);
+
+ mhod_seek = mhip_seek + mhip_hlen;
+
+ /* the mhod that follows gives us the position in the
+ playlist (type 100). Just for flexibility, we scan all
+ following mhods and pick the type 100 */
+ for (i=0; i<mhod_num; ++i)
+ {
+ guint32 mhod_len;
+
+ mhod_type = get_mhod_type (cts, mhod_seek, &mhod_len);
+ CHECK_ERROR (fimp, -1);
+ if (mhod_type == MHOD_ID_PLAYLIST)
+ {
+ posid = (guint32)get_mhod (cts, mhod_seek,
+ &mhod_len, &mhod_type);
+ CHECK_ERROR (fimp, -1);
+ /* The posids don't have to be in numeric order, but our
+ database depends on the playlist members being sorted
+ according to the order they appear in the
+ playlist. Therefore we need to find out at which
+ position to insert the track */
+ fimp->pos_glist = g_list_insert_sorted (
+ fimp->pos_glist, (gpointer)posid,
+ (GCompareFunc)pos_comp);
+ pos = g_list_index (fimp->pos_glist, (gpointer)posid);
+ /* for speedup: pos==-1 is appending at the end */
+ if (pos == fimp->pos_len) pos = -1;
+ break;
+ }
+ else
+ {
+ if (mhod_len == -1)
+ {
+ g_warning (_("Number of MHODs in mhip at %ld inconsistent in file '%s'."),
+ mhip_seek);
+ break;
+ }
+ mhod_seek += mhod_len;
+ }
+ }
+
+ tr = itdb_track_id_tree_by_id (fimp->idtree, trackid);
+ if (tr)
+ {
+ itdb_playlist_add_track (plitem, tr, pos);
+ ++fimp->pos_len;
+ }
+ else
+ {
+ if (plitem->podcastflag == ITDB_PL_FLAG_NORM)
+ {
+ g_warning (_("Itdb_Track ID '%d' not found.\n"), trackid);
+ }
+ }
+
+ return mhip_seek+mhip_len;
+}
+
+
+
+
+/* Get a playlist. Returns the position where the next playlist should
+ be. On error -1 is returned and fimp->error is set
+ appropriately. */
+/* get_mhyp */
+static glong get_playlist (FImport *fimp, glong mhyp_seek)
+{
+ guint32 i, mhipnum, mhod_num;
+ glong nextseek, mhod_seek, mhip_seek;
+ guint32 header_len;
Itdb_Playlist *plitem = NULL;
FContents *cts;
#if ITUNESDB_DEBUG
- fprintf(stderr, "mhyp seek: %x\n", (int)seek);
+ fprintf(stderr, "mhyp seek: %x\n", (int)mhyp_seek);
#endif
g_return_val_if_fail (fimp, -1);
g_return_val_if_fail (fimp->idtree, -1);
+ g_return_val_if_fail (fimp->pos_glist == NULL, -1);
+ g_return_val_if_fail (fimp->pos_len == 0, -1);
cts = fimp->itunesdb;
- if (!cmp_n_bytes_seek (cts, "mhyp", seek, 4))
+ if (!cmp_n_bytes_seek (cts, "mhyp", mhyp_seek, 4))
{
if (cts->error)
g_propagate_error (&fimp->error, cts->error);
return -1;
}
- hlen = get32lint (cts, seek+4); /* length of header */
+ header_len = get32lint (cts, mhyp_seek+4); /* length of header */
CHECK_ERROR (fimp, -1);
- if (hlen == 0)
+
+ if (header_len < 48)
{
- g_set_error (&fimp->error,
- ITDB_FILE_ERROR,
- ITDB_FILE_ERROR_CORRUPT,
- _("iTunesDB corrupt: hunk length 0 for hunk at %ld in file '%s'."),
- seek, cts->filename);
+ set_error_a_header_smaller_than_b (&fimp->error,
+ "mhyp",
+ header_len, 48,
+ mhyp_seek, cts->filename);
return -1;
}
- nextseek = seek + get32lint (cts, seek+8);/* possible begin of next PL */
- CHECK_ERROR (fimp, -1);
- mhod_num = get32lint (cts, seek+12); /* number of MHODs we expect */
- CHECK_ERROR (fimp, -1);
- tracknum = get32lint (cts, seek+16); /* number of tracks in playlist */
+
+ /* Check if entire mhyp can be read -- that way we won't have to
+ * check for read errors every time we access a single byte */
+
+ check_seek (cts, mhyp_seek, header_len);
CHECK_ERROR (fimp, -1);
+
+ nextseek = mhyp_seek + get32lint (cts, mhyp_seek+8);/* possible begin of next PL */ mhod_num = get32lint (cts, mhyp_seek+12); /* number of MHODs we expect */
+ mhipnum = get32lint (cts, mhyp_seek+16); /* number of tracks
+ (mhips) in playlist */
+
plitem = itdb_playlist_new (NULL, FALSE);
+
/* Some Playlists have added 256 to their type -- I don't know what
it's for, so we just ignore it for now -> & 0xff */
- plitem->type = get32lint (cts, seek+20) & 0xff;
- CHECK_ERROR (fimp, -1);
- plitem->id = get64lint (cts, seek+28);
- CHECK_ERROR (fimp, -1);
- plitem->unk036 = get32lint (cts, seek+36);
- CHECK_ERROR (fimp, -1);
- plitem->unk040 = get32lint (cts, seek+40);
- CHECK_ERROR (fimp, -1);
- plitem->sortorder = get32lint (cts, seek+44);
- CHECK_ERROR (fimp, -1);
+ plitem->type = get32lint (cts, mhyp_seek+20) & 0xff;
+ plitem->id = get64lint (cts, mhyp_seek+28);
+ plitem->mhodcount = get32lint (cts, mhyp_seek+36);
+ plitem->libmhodcount = get16lint (cts, mhyp_seek+40);
+ plitem->podcastflag = get16lint (cts, mhyp_seek+42);
+ plitem->sortorder = get32lint (cts, mhyp_seek+44);
+
+ mhod_seek = mhyp_seek + header_len;
+
for (i=0; i < mhod_num; ++i)
{
gchar *plname_utf8_maybe;
@@ -1268,78 +1565,89 @@ static glong get_playlist (FImport *fimp, glong seek)
SPLRules *splrules = NULL;
gint32 type;
- seek += hlen;
- type = get_mhod_type (cts, seek, &hlen);
+ type = get_mhod_type (cts, mhod_seek, &header_len);
CHECK_ERROR (fimp, -1);
- if (hlen != -1) switch ((enum MHOD_ID)type)
+ if (header_len != -1)
{
- case MHOD_ID_PLAYLIST:
- /* here we could do something about the playlist settings */
- break;
- case MHOD_ID_TITLE:
- plname_utf8_maybe = get_mhod (cts, seek, &hlen, &type);
- CHECK_ERROR (fimp, -1);
- if (plname_utf8_maybe)
- {
- /* sometimes there seem to be two mhod TITLE headers */
- g_free (plname_utf8);
- plname_utf8 = plname_utf8_maybe;
- }
- break;
- case MHOD_ID_SPLPREF:
- splpref = get_mhod (cts, seek, &hlen, &type);
- CHECK_ERROR (fimp, -1);
- if (splpref)
- {
- plitem->is_spl = TRUE;
- memcpy (&plitem->splpref, splpref, sizeof (SPLPref));
- g_free (splpref);
- splpref = NULL;
- }
- break;
- case MHOD_ID_SPLRULES:
- splrules = get_mhod (cts, seek, &hlen, &type);
- CHECK_ERROR (fimp, -1);
- if (splrules)
+ switch ((enum MHOD_ID)type)
{
- plitem->is_spl = TRUE;
- memcpy (&plitem->splrules, splrules, sizeof (SPLRules));
- g_free (splrules);
- splrules = NULL;
+ case MHOD_ID_PLAYLIST:
+ /* here we could do something about the playlist settings */
+ break;
+ case MHOD_ID_TITLE:
+ plname_utf8_maybe = get_mhod (cts, mhod_seek, &header_len, &type);
+ CHECK_ERROR (fimp, -1);
+ if (plname_utf8_maybe)
+ {
+ /* sometimes there seem to be two mhod TITLE headers */
+ g_free (plitem->name);
+ plitem->name = plname_utf8_maybe;
+ }
+ break;
+ case MHOD_ID_SPLPREF:
+ splpref = get_mhod (cts, mhod_seek, &header_len, &type);
+ CHECK_ERROR (fimp, -1);
+ if (splpref)
+ {
+ plitem->is_spl = TRUE;
+ memcpy (&plitem->splpref, splpref, sizeof (SPLPref));
+ g_free (splpref);
+ splpref = NULL;
+ }
+ break;
+ case MHOD_ID_SPLRULES:
+ splrules = get_mhod (cts, mhod_seek, &header_len, &type);
+ CHECK_ERROR (fimp, -1);
+ if (splrules)
+ {
+ plitem->is_spl = TRUE;
+ memcpy (&plitem->splrules, splrules, sizeof (SPLRules));
+ g_free (splrules);
+ splrules = NULL;
+ }
+ break;
+ case MHOD_ID_PATH:
+ case MHOD_ID_ALBUM:
+ case MHOD_ID_ARTIST:
+ case MHOD_ID_GENRE:
+ case MHOD_ID_FILETYPE:
+ case MHOD_ID_COMMENT:
+ case MHOD_ID_CATEGORY:
+ case MHOD_ID_COMPOSER:
+ case MHOD_ID_GROUPING:
+ case MHOD_ID_DESCRIPTION:
+ case MHOD_ID_PODCASTURL:
+ case MHOD_ID_PODCASTRSS:
+ case MHOD_ID_SUBTITLE:
+ case MHOD_ID_CHAPTERDATA:
+ /* these are not expected here */
+ break;
+ case MHOD_ID_LIBPLAYLISTINDEX:
+ /* this I don't know how to handle */
+ break;
}
- break;
- case MHOD_ID_PATH:
- case MHOD_ID_ALBUM:
- case MHOD_ID_ARTIST:
- case MHOD_ID_GENRE:
- case MHOD_ID_FILETYPE:
- case MHOD_ID_COMMENT:
- case MHOD_ID_CATEGORY:
- case MHOD_ID_COMPOSER:
- case MHOD_ID_GROUPING:
- case MHOD_ID_DESCRIPTION:
- case MHOD_ID_PODCASTURL:
- case MHOD_ID_PODCASTRSS:
- case MHOD_ID_SUBTITLE:
- /* these are not expected here */
- break;
- case MHOD_ID_MHYP:
- /* this I don't know how to handle */
+ mhod_seek += header_len;
+ }
+ else
+ {
+ g_warning (_("Number of MHODs in mhyp at %ld inconsistent in file '%s'."),
+ mhyp_seek, cts->filename);
break;
}
}
- if (plname_utf8)
- {
- plitem->name = plname_utf8;
- }
- else
+ if (!plitem->name)
{ /* we did not read a valid mhod TITLE header -> */
/* we simply make up our own name */
if (plitem->type == ITDB_PL_TYPE_MPL)
plitem->name = _("Master-PL");
else
- plitem->name = _("Playlist");
+ {
+ if (plitem->podcastflag == ITDB_PL_FLAG_PODCAST)
+ plitem->name = _("Podcasts");
+ else
+ plitem->name = _("Playlist");
+ }
}
#if ITUNESDB_DEBUG
@@ -1349,77 +1657,27 @@ static glong get_playlist (FImport *fimp, glong seek)
/* add new playlist */
itdb_playlist_add (fimp->itdb, plitem, -1);
+ mhip_seek = mhod_seek;
+
i=0; /* tracks read */
- while (i<tracknum)
+ for (i=0; i < mhipnum; ++i)
{
- guint32 len = get32lint (cts, seek+8);
-#if ITUNESDB_DEBUG
- fprintf(stderr, " %lx: seeking track %d of %d\n", seek, i+1, (int)tracknum);
-#endif
- CHECK_ERROR (fimp, -1);
- /* We read the mhip headers and skip everything else (the mhips
- * seem to come in pairs: mhip/mhod mhip/mhod ...). */
- if (cmp_n_bytes_seek (cts, "mhyp", seek, 4))
- { /* This cannot be, let's abort... */
+ mhip_seek = get_mhip (fimp, plitem, mhip_seek);
+ if (mhip_seek == -1)
+ {
g_set_error (&fimp->error,
ITDB_FILE_ERROR,
ITDB_FILE_ERROR_CORRUPT,
- _("iTunesDB corrupt: found mhyp at %ld in file '%s'."),
- seek, cts->filename);
+ _("iTunesDB corrupt: number of mhip sections inconsistent in mhyp starting at %ld in file '%s'."),
+ mhyp_seek, cts->filename);
return -1;
}
- if (cmp_n_bytes_seek (cts, "mhip", seek, 4))
- {
-#if ITUNESDB_DEBUG
- fprintf(stderr, " %lx: mhit\n", seek);
-#endif
- Itdb_Track *tr;
- gint32 pos = -1;
- guint32 posid;
- gint32 mhod_type;
- guint32 mhod_len;
- guint32 mhit_len;
- guint32 ref;
- mhit_len = get32lint(cts, seek+4);
- CHECK_ERROR (fimp, -1);
- ref = get32lint(cts, seek+24);
- CHECK_ERROR (fimp, -1);
- /* the mhod that follows gives us the position in the
- playlist (type 100) */
- mhod_type = get_mhod_type (cts, seek+mhit_len, NULL);
- CHECK_ERROR (fimp, -1);
- if (mhod_type == MHOD_ID_PLAYLIST)
- {
- posid = (guint32)get_mhod (cts, seek+mhit_len,
- &mhod_len, &mhod_type);
- CHECK_ERROR (fimp, -1);
- /* The posids don't have to be in numeric order, but our
- database depends on the playlist members being sorted
- according to the order they appear in the
- playlist. Therefore we need to find out at which
- position to insert the track */
- fimp->pos_glist = g_list_insert_sorted (
- fimp->pos_glist, (gpointer)posid,
- (GCompareFunc)pos_comp);
- pos = g_list_index (fimp->pos_glist, (gpointer)posid);
- /* For performance reasons set pos to -1 if position is
- end of list */
- if (pos == i) pos = -1;
- }
- tr = itdb_track_id_tree_by_id (fimp->idtree, ref);
- if (tr)
- {
- itdb_playlist_add_track (plitem, tr, pos);
- }
- else
- g_warning (_("Itdb_Track ID '%d' not found.\n"), ref);
- ++i;
- }
- CHECK_ERROR (fimp, -1);
- seek += len;
- }
+ }
+
+
g_list_free (fimp->pos_glist);
fimp->pos_glist = NULL;
+ fimp->pos_len = 0;
return nextseek;
}
@@ -1427,7 +1685,7 @@ static glong get_playlist (FImport *fimp, glong seek)
/* returns a pointer to the next header or -1 on error. fimp->error is
set appropriately. If no "mhit" header is found at the location
specified, -1 is returned but no error is set. */
-static glong get_mhit (FImport *fimp, glong seek)
+static glong get_mhit (FImport *fimp, glong mhit_seek)
{
Itdb_Track *track;
gchar *entry_utf8;
@@ -1437,6 +1695,7 @@ static glong get_mhit (FImport *fimp, glong seek)
struct playcount *playcount;
guint32 i, mhod_nums;
FContents *cts;
+ glong seek = mhit_seek;
#if ITUNESDB_DEBUG
fprintf(stderr, "get_mhit seek: %x\n", (int)seek);
@@ -1456,6 +1715,18 @@ static glong get_mhit (FImport *fimp, glong seek)
header_len = get32lint (cts, seek+4);
CHECK_ERROR (fimp, -1);
+ /* size of the mhit header: For dbversion <= 0x0b (iTunes 4.7 and
+ earlier), the length is 0x9c. As of dbversion 0x0c and 0x0d
+ (iTunes 4.7.1 - iTunes 4.9), the size is 0xf4. */
+ if (header_len < 0x9c)
+ {
+ set_error_a_header_smaller_than_b (&fimp->error,
+ "mhit",
+ header_len, 0x9c,
+ seek, cts->filename);
+ return -1;
+ }
+
/* Check if entire mhit can be read -- that way we won't have to
* check for read errors every time we access a single byte */
@@ -1464,21 +1735,8 @@ static glong get_mhit (FImport *fimp, glong seek)
mhod_nums = get32lint (cts, seek+12);
CHECK_ERROR (fimp, -1);
- track = itdb_track_new ();
- /* size of the mhit header: For dbversion <= 0x0b (iTunes 4.7 and
- earlier), the length is 0x9c. As of dbversion 0x0c and 0x0d
- (iTunes 4.7.1 - iTunes 4.9), the size is 0xf4. */
- if (header_len < 0x9c)
- {
- g_return_val_if_fail (cts->filename, FALSE);
- g_set_error (&fimp->error,
- ITDB_FILE_ERROR,
- ITDB_FILE_ERROR_CORRUPT,
- _("mhit header length smaller than expected (%x < 0x9c) at offset %ld in file '%s'."),
- header_len, seek, cts->filename);
- return -1;
- }
+ track = itdb_track_new ();
if (header_len >= 0x9c)
{
@@ -1522,7 +1780,7 @@ static glong get_mhit (FImport *fimp, glong seek)
track->artwork_size = get32lint (cts, seek+128);
track->unk132 = get32lint (cts, seek+132);
track->samplerate2 = get32lfloat (cts, seek+136);
- track->unk140 = get32lint (cts, seek+140);
+ track->time_released = get32lint (cts, seek+140);
track->unk144 = get32lint (cts, seek+144);
track->unk148 = get32lint (cts, seek+148);
track->unk152 = get32lint (cts, seek+152);
@@ -1554,62 +1812,6 @@ static glong get_mhit (FImport *fimp, glong seek)
track->transferred = TRUE; /* track is on iPod! */
-#if ITUNESDB_MHIT_DEBUG
-time_t time_mac_to_host (guint32 mactime);
-gchar *time_time_to_string (time_t time);
-#define printf_mhit(sk, str) printf ("%3d: %d (%s)\n", sk, get32lint (file, seek+sk), str);
-#define printf_mhit_time(sk, str) { gchar *buf = time_time_to_string (itunesdb_time_mac_to_host (get32lint (file, seek+sk))); printf ("%3d: %s (%s)\n", sk, buf, str); g_free (buf); }
- {
- printf ("\nmhit: seek=%lu\n", seek);
- printf_mhit ( 4, "header size");
- printf_mhit ( 8, "mhit size");
- printf_mhit ( 12, "nr of mhods");
- printf_mhit ( 16, "iPod ID");
- printf_mhit ( 20, "?");
- printf_mhit ( 24, "?");
- printf (" 28: %u (type)\n", get32lint (file, seek+28) & 0xffffff);
- printf (" 28: %u (rating)\n", get32lint (file, seek+28) >> 24);
- printf_mhit ( 32, "timestamp file");
- printf_mhit_time ( 32, "timestamp file");
- printf_mhit ( 36, "size");
- printf_mhit ( 40, "tracklen (ms)");
- printf_mhit ( 44, "track_nr");
- printf_mhit ( 48, "total tracks");
- printf_mhit ( 52, "year");
- printf_mhit ( 56, "bitrate");
- printf_mhit ( 60, "sample rate");
- printf (" 60: %u (sample rate LSB)\n", get32lint (file, seek+60) & 0xffff);
- printf (" 60: %u (sample rate HSB)\n", (get32lint (file, seek+60) >> 16));
- printf_mhit ( 64, "?");
- printf_mhit ( 68, "?");
- printf_mhit ( 72, "?");
- printf_mhit ( 76, "?");
- printf_mhit ( 80, "playcount");
- printf_mhit ( 84, "?");
- printf_mhit ( 88, "last played");
- printf_mhit_time ( 88, "last played");
- printf_mhit ( 92, "CD");
- printf_mhit ( 96, "total CDs");
- printf_mhit (100, "?");
- printf_mhit (104, "?");
- printf_mhit_time (104, "?");
- printf_mhit (108, "?");
- printf_mhit (112, "?");
- printf_mhit (116, "?");
- printf_mhit (120, "?");
- printf_mhit (124, "?");
- printf_mhit (128, "?");
- printf_mhit (132, "?");
- printf_mhit (136, "?");
- printf_mhit (140, "?");
- printf_mhit (144, "?");
- printf_mhit (148, "?");
- printf_mhit (152, "?");
- }
-#undef printf_mhit_time
-#undef printf_mhit
-#endif
-
seek += get32lint (cts, seek+4); /* 1st mhod starts here! */
CHECK_ERROR (fimp, -1);
@@ -1664,10 +1866,33 @@ gchar *time_time_to_string (time_t time);
track->subtitle = entry_utf8;
break;
default: /* unknown entry -- discard */
+ g_warning ("Unknown string mhod type %d at %lx inside mhit starting at %lx\n",
+ type, seek, mhit_seek);
g_free (entry_utf8);
break;
}
}
+ else
+ {
+ switch (type)
+ {
+ case MHOD_ID_CHAPTERDATA:
+ /* we just read the entire chapterdata info until we
+ have a better way to parse and represent it */
+ track->chapterdata_raw = get_mhod (cts, seek, &zip, &type);
+ if (track->chapterdata_raw)
+ {
+ track->chapterdata_raw_length =
+ zip - get32lint (cts, seek+4);
+ }
+ break;
+ default:
+/*
+ printf ("found mhod type %d at %lx inside mhit starting at %lx\n",
+ type, seek, mhit_seek);*/
+ break;
+ }
+ }
seek += zip;
}
@@ -1842,161 +2067,6 @@ static gboolean read_OTG_playlists (FImport *fimp)
}
-/* convenience function: set error for zero length hunk */
-static void set_error_zero_length_hunk (GError **error, glong seek,
- gchar *filename)
-{
- g_set_error (error,
- ITDB_FILE_ERROR,
- ITDB_FILE_ERROR_CORRUPT,
- _("iTunesDB corrupt: hunk length 0 for hunk at %ld in file '%s'."),
- seek, filename);
-}
-
-/* convenience function: set error for missing hunk */
-static void set_error_a_not_found_in_b (GError **error,
- const gchar *a,
- const gchar *b,
- glong mhsd_seek)
-{
- g_set_error (error,
- ITDB_FILE_ERROR,
- ITDB_FILE_ERROR_CORRUPT,
- _("iTunesDB corrupt: no section '%s' found in section '%s' starting at %ld."),
- a, b, mhsd_seek);
-}
-
-
-/* finds next occurence of section @a in section b (@b_seek) starting
- at @start_seek
-*/
-/* Return value:
- -1 and cts->error not set: section @a could not be found
- -1 and cts->error set: some error occured
- >=0: start of next occurence of section @a
-*/
-static glong find_next_a_in_b (FContents *cts,
- const gchar *a,
- glong b_seek, glong start_seek)
-{
- glong b_len;
- glong offset, len;
-
- g_return_val_if_fail (a, -1);
- g_return_val_if_fail (cts, -1);
- g_return_val_if_fail (strlen (a) == 4, -1);
- g_return_val_if_fail (b_seek>=0, -1);
- g_return_val_if_fail (start_seek >= b_seek, -1);
-
-/* printf ("%s: b_seek: %lx, start_seek: %lx\n", a, b_seek, start_seek); */
-
- b_len = get32lint (cts, b_seek+8);
- if (cts->error) return -1;
-
- offset = start_seek - b_seek;
- len = 0;
- do
- { /* skip headers inside the b hunk (b_len) until we find header
- @a */
- len = get32lint (cts, b_seek+offset+4);
- if (cts->error) return -1;
- if (len == 0)
- { /* This needs to be checked, otherwise we might hang */
- set_error_zero_length_hunk (&cts->error, b_seek+offset,
- cts->filename);
- return -1;
- }
- offset += len;
-/* printf ("offset: %lx, b_len: %lx, bseek+offset: %lx\n", */
-/* offset, b_len, b_seek+offset); */
- } while ((offset < b_len-4) &&
- !cmp_n_bytes_seek (cts, a, b_seek+offset, 4));
- if (cts->error) return -1;
-
- if (offset >= b_len) return -1;
-
-/* printf ("%s found at %lx\n", a, b_seek+offset); */
-
- return b_seek+offset;
-}
-
-
-
-
-/* return the position of mhsd with type @type */
-/* Return value:
- -1 if mhsd cannot be found. cts->error will not be set
-
- 0 and cts->error is set if some other error occurs.
- Since the mhsd can never be at position 0 (a mhbd must be there),
- a return value of 0 always indicates an error.
-*/
-static glong find_mhsd (FContents *cts, guint32 type)
-{
- guint32 i, len, mhsd_num;
- glong seek;
-
- if (!cmp_n_bytes_seek (cts, "mhbd", 0, 4))
- {
- if (!cts->error)
- { /* set error */
- g_set_error (&cts->error,
- ITDB_FILE_ERROR,
- ITDB_FILE_ERROR_CORRUPT,
- _("Not a iTunesDB: '%s' (missing mhdb header)."),
- cts->filename);
- }
- return 0;
- }
- len = get32lint (cts, 4);
- if (cts->error) return 0;
- /* all the headers I know are 0x68 long -- if this one is longer
- we can could simply ignore the additional information */
- /* Since 'we' (parse_fimp()) only need data from the first 32
- bytes, don't complain unless it's smaller than that */
- if (len < 32)
- {
- g_set_error (&cts->error,
- ITDB_FILE_ERROR,
- ITDB_FILE_ERROR_CORRUPT,
- _("iTunesDB ('%s'): header length of mhsd hunk smaller than expected (%d<32). Aborting."),
- cts->filename, len);
- return FALSE;
- }
-
- mhsd_num = get32lint (cts, 20);
- if (cts->error) return 0;
-
- seek = 0;
- for (i=0; i<mhsd_num; ++i)
- {
- guint32 mhsd_type;
-
- seek += len;
- if (!cmp_n_bytes_seek (cts, "mhsd", seek, 4))
- {
- if (!cts->error)
- { /* set error */
- g_set_error (&cts->error,
- ITDB_FILE_ERROR,
- ITDB_FILE_ERROR_CORRUPT,
- _("iTunesDB '%s' corrupt: mhsd expected at %ld."),
- cts->filename, seek);
- }
- return 0;
- }
- len = get32lint (cts, seek+8);
- if (cts->error) return 0;
-
- mhsd_type = get32lint (cts, seek+12);
- if (cts->error) return 0;
-
- if (mhsd_type == type) return seek;
- }
- return -1;
-}
-
-
/* Read the tracklist (mhlt). mhsd_seek must point to type 1 mhsd
(this is treated as a programming error) */
/* Return value:
@@ -2326,6 +2396,17 @@ static void put_data (WContents *cts, gchar *data, gulong len)
}
+/* Write @string without trailing Null to end of @cts. Will always be
+ * successful because glib terminates when out of memory */
+static void put_string (WContents *cts, gchar *string)
+{
+ g_return_if_fail (cts);
+ g_return_if_fail (string);
+
+ put_data (cts, string, strlen(string));
+}
+
+
/* Write 1-byte integer @n to @cts */
static void put8int (WContents *cts, guint8 n)
{
@@ -2476,7 +2557,7 @@ static void put32_n0 (WContents *cts, gulong n)
/* Write out the mhbd header. Size will be written later */
-static void mk_mhbd (FExport *fexp)
+static void mk_mhbd (FExport *fexp, guint32 children)
{
WContents *cts;
@@ -2486,7 +2567,7 @@ static void mk_mhbd (FExport *fexp)
cts = fexp->itunesdb;
- put_data (cts, "mhbd", 4);
+ put_string (cts, "mhbd");
put32lint (cts, 104); /* header size */
put32lint (cts, -1); /* size of whole mhdb -- fill in later */
put32lint (cts, 1); /* ? */
@@ -2499,7 +2580,7 @@ static void mk_mhbd (FExport *fexp)
0x0d: iTunes 4.9 */
fexp->itdb->version = 0x0d;
put32lint (cts, fexp->itdb->version);
- put32lint (cts, 2); /* 2 children (track list and playlist list) */
+ put32lint (cts, children);
put64lint (cts, fexp->itdb->id);
put32lint (cts, 2); /* ? */
put32_n0 (cts, 17); /* dummy space */
@@ -2523,7 +2604,7 @@ static void mk_mhsd (FExport *fexp, guint32 type)
cts = fexp->itunesdb;
- put_data (cts, "mhsd", 4);
+ put_string (cts, "mhsd");
put32lint (cts, 96); /* Headersize */
put32lint (cts, -1); /* size of whole mhsd -- fill in later */
put32lint (cts, type); /* type: 1 = track, 2 = playlist */
@@ -2542,7 +2623,7 @@ static void mk_mhlt (FExport *fexp, guint32 num)
cts = fexp->itunesdb;
- put_data (cts, "mhlt", 4);
+ put_string (cts, "mhlt");
put32lint (cts, 92); /* Headersize */
put32lint (cts, num); /* tracks in this itunesdb */
put32_n0 (cts, 20); /* dummy space */
@@ -2555,7 +2636,7 @@ static void mk_mhit (WContents *cts, Itdb_Track *track)
g_return_if_fail (cts);
g_return_if_fail (track);
- put_data (cts, "mhit", 4);
+ put_string (cts, "mhit");
put32lint (cts, 0xf4); /* header size */
put32lint (cts, -1); /* size of whole mhit -- fill in later */
put32lint (cts, -1); /* nr of mhods in this mhit -- later */
@@ -2598,7 +2679,7 @@ static void mk_mhit (WContents *cts, Itdb_Track *track)
put32lint (cts, track->artwork_size);
put32lint (cts, track->unk132);
put32lfloat (cts, track->samplerate2);
- put32lint (cts, track->unk140);
+ put32lint (cts, track->time_released);
put32lint (cts, track->unk144);
put32lint (cts, track->unk148);
put32lint (cts, track->unk152);
@@ -2671,7 +2752,7 @@ static void mk_mhod (WContents *cts, enum MHOD_ID type, void *data)
NULL, NULL, NULL);
guint32 len = utf16_strlen (entry_utf16);
fixup_little_utf16 (entry_utf16);
- put_data (cts, "mhod", 4); /* header */
+ put_string (cts, "mhod"); /* header */
put32lint (cts, 24); /* size of header */
put32lint (cts, 2*len+40); /* size of header + body */
put32lint (cts, type); /* type of the mhod */
@@ -2689,16 +2770,16 @@ static void mk_mhod (WContents *cts, enum MHOD_ID type, void *data)
g_return_if_fail (data);
{
guint32 len = strlen ((gchar *)data);
- put_data (cts, "mhod", 4); /* header */
+ put_string (cts, "mhod"); /* header */
put32lint (cts, 24); /* size of header */
put32lint (cts, 24+len); /* size of header + data */
put32lint (cts, type); /* type of the mhod */
put32_n0 (cts, 2); /* unknown */
- put_data (cts, (gchar *)data, len); /* the string */
+ put_string (cts, (gchar *)data); /* the string */
}
break;
case MHOD_ID_PLAYLIST:
- put_data (cts, "mhod", 4); /* header */
+ put_string (cts, "mhod"); /* header */
put32lint (cts, 24); /* size of header */
put32lint (cts, 44); /* size of header + body */
put32lint (cts, type); /* type of the entry */
@@ -2707,11 +2788,24 @@ static void mk_mhod (WContents *cts, enum MHOD_ID type, void *data)
put32lint (cts, (guint32)data);/* position of track in playlist */
put32_n0 (cts, 4); /* unknown */
break;
+ case MHOD_ID_CHAPTERDATA:
+ g_return_if_fail (data);
+ {
+ Itdb_Track *track = data;
+ put_string (cts, "mhod"); /* header */
+ put32lint (cts, 24); /* size of header */
+ put32lint (cts, 24+track->chapterdata_raw_length); /*size */
+ put32lint (cts, type); /* type of the entry */
+ put32_n0 (cts, 2); /* unknown */
+ put_data (cts, track->chapterdata_raw,
+ track->chapterdata_raw_length);
+ }
+ break;
case MHOD_ID_SPLPREF:
g_return_if_fail (data);
{
SPLPref *splp = data;
- put_data (cts, "mhod", 4); /* header */
+ put_string (cts, "mhod"); /* header */
put32lint (cts, 24); /* size of header */
put32lint (cts, 96); /* size of header + body */
put32lint (cts, type); /* type of the entry */
@@ -2743,7 +2837,7 @@ static void mk_mhod (WContents *cts, enum MHOD_ID type, void *data)
GList *gl;
gint numrules = g_list_length (splrs->rules);
- put_data (cts, "mhod", 4); /* header */
+ put_string (cts, "mhod"); /* header */
put32lint (cts, 24); /* size of header */
put32lint (cts, -1); /* total length, fix later */
put32lint (cts, type); /* type of the entry */
@@ -2751,7 +2845,7 @@ static void mk_mhod (WContents *cts, enum MHOD_ID type, void *data)
/* end of header, start of data */
/* For some reason this is the only part of the iTunesDB
that uses big endian */
- put_data (cts, "SLst", 4); /* header */
+ put_string (cts, "SLst"); /* header */
put32bint (cts, splrs->unk004); /* unknown */
put32bint (cts, numrules);
put32bint (cts, splrs->match_operator);
@@ -2799,7 +2893,7 @@ static void mk_mhod (WContents *cts, enum MHOD_ID type, void *data)
fix_header (cts, header_seek);
}
break;
- case MHOD_ID_MHYP:
+ case MHOD_ID_LIBPLAYLISTINDEX:
g_warning (_("Cannot write mhod of type %d\n"), type);
break;
}
@@ -2816,7 +2910,7 @@ static void mk_mhlp (FExport *fexp)
cts = fexp->itunesdb;
- put_data (cts, "mhlp", 4); /* header */
+ put_string (cts, "mhlp"); /* header */
put32lint (cts, 92); /* size of header */
/* playlists on iPod (including main!) */
put32lint (cts, g_list_length (fexp->itdb->playlists));
@@ -2842,7 +2936,7 @@ static void mk_long_mhod_id_playlist (FExport *fexp, Itdb_Playlist *pl)
cts = fexp->itunesdb;
- put_data (cts, "mhod", 4); /* header */
+ put_string (cts, "mhod"); /* header */
put32lint (cts, 0x18); /* size of header ? */
put32lint (cts, 0x0288); /* size of header + body */
put32lint (cts, MHOD_ID_PLAYLIST); /* type of the entry */
@@ -2878,7 +2972,13 @@ static void mk_long_mhod_id_playlist (FExport *fexp, Itdb_Playlist *pl)
/* Header for new PL item */
/* @pos: position in playlist */
-static void mk_mhip (FExport *fexp, guint32 pos, guint32 id)
+static void mk_mhip (FExport *fexp,
+ guint32 childcount,
+ guint32 podcastgroupflag,
+ guint32 podcastgroupid,
+ guint32 trackid,
+ guint32 timestamp,
+ guint32 podcastgroupref)
{
WContents *cts;
@@ -2887,20 +2987,22 @@ static void mk_mhip (FExport *fexp, guint32 pos, guint32 id)
cts = fexp->itunesdb;
- put_data (cts, "mhip", 4);
+ put_string (cts, "mhip");
put32lint (cts, 76); /* 4 */
put32lint (cts, -1); /* fill in later */ /* 8 */
- put32lint (cts, 1); /* number of children */ /* 12 */
- put32lint (cts, 0); /* unknown */ /* 16 */
- put32lint (cts, 0); /* unknown */ /* 20 */
- put32lint (cts, id); /* id */ /* 24 */
- put32_n0 (cts, 12); /* 28 */
+ put32lint (cts, childcount); /* 12 */
+ put32lint (cts, podcastgroupflag); /* 16 */
+ put32lint (cts, podcastgroupid); /* 20 */
+ put32lint (cts, trackid); /* 24 */
+ put32lint (cts, timestamp); /* 28 */
+ put32lint (cts, podcastgroupref); /* 32 */
+ put32_n0 (cts, 10); /* 36 */
}
/* Write first mhsd hunk. Return FALSE in case of error and set
* fexp->error */
-static gboolean write_mhsd_one(FExport *fexp)
+static gboolean write_mhsd_tracks (FExport *fexp)
{
GList *gl;
gulong mhsd_seek;
@@ -2920,15 +3022,11 @@ static gboolean write_mhsd_one(FExport *fexp)
{
Itdb_Track *track = gl->data;
guint32 mhod_num = 0;
+
+ g_return_val_if_fail (track, FALSE);
+
gulong mhit_seek = cts->pos;
- if (!track)
- {
- g_set_error (&fexp->error,
- ITDB_FILE_ERROR,
- ITDB_FILE_ERROR_ITDB_CORRUPT,
- _("Database in memory corrupt (track pointer == NULL). Aborting export."));
- return FALSE;
- }
+
mk_mhit (cts, track);
if (track->title && *track->title)
{
@@ -3000,6 +3098,11 @@ static gboolean write_mhsd_one(FExport *fexp)
mk_mhod (cts, MHOD_ID_PODCASTRSS, track->podcastrss);
++mhod_num;
}
+ if (track->chapterdata_raw && track->chapterdata_raw_length)
+ {
+ mk_mhod (cts, MHOD_ID_CHAPTERDATA, track);
+ ++mhod_num;
+ }
/* Fill in the missing items of the mhit header */
fix_mhit (cts, mhit_seek, mhod_num);
}
@@ -3007,14 +3110,163 @@ static gboolean write_mhsd_one(FExport *fexp)
return TRUE;
}
+
+/* write out the mhip/mhod pairs for playlist @pl. @mhyp_seek points
+ to the start of the corresponding mhyp, but is not used yet. */
+static gboolean write_playlist_mhips (FExport *fexp,
+ Itdb_Playlist *pl,
+ glong mhyp_seek)
+{
+ GList *gl;
+ WContents *cts;
+ guint32 i=0;
+ guint32 mhip_num;
+
+ g_return_val_if_fail (fexp, FALSE);
+ g_return_val_if_fail (fexp->itdb, FALSE);
+ g_return_val_if_fail (fexp->itunesdb, FALSE);
+ g_return_val_if_fail (pl, FALSE);
+
+ cts = fexp->itunesdb;
+
+ for (gl=pl->members; gl; gl=gl->next)
+ {
+ Itdb_Track *track = gl->data;
+ g_return_val_if_fail (track, FALSE);
+
+ glong mhip_seek = cts->pos;
+ mk_mhip (fexp, 1, 0, 0, track->id, 0, 0);
+ mk_mhod (cts, MHOD_ID_PLAYLIST, (void *)i);
+ /* note: with iTunes 4.9 the mhod is counted as a child to
+ mhip, so we have put the total length of the mhip and mhod
+ into the mhip header */
+ fix_header (cts, mhip_seek);
+ ++i;
+ }
+
+ /* set number of mhips */
+ mhip_num = g_list_length (pl->members);
+ put32lint_seek (cts, mhip_num, mhyp_seek+16);
+
+ return TRUE;
+}
+
+
+/* write out the mhip/mhod pairs for the podcast playlist
+ @pl. @mhyp_seek points to the start of the corresponding mhyp, but
+ is not used yet. */
+static gboolean write_podcast_mhips (FExport *fexp,
+ Itdb_Playlist *pl,
+ glong mhyp_seek)
+{
+ auto void free_memberlist (gpointer data);
+ auto void write_one_podcast_group (gpointer key, gpointer value,
+ gpointer userdata);
+ void free_memberlist (gpointer data)
+ {
+ GList **memberlist = data;
+ if (memberlist)
+ {
+ g_list_free (*memberlist);
+ g_free (memberlist);
+ }
+ }
+ auto void write_one_podcast_group (gpointer key, gpointer value,
+ gpointer userdata)
+ {
+ gchar *album = key;
+ GList **memberlist = value;
+ FExport *fexp = userdata;
+ GList *gl;
+ WContents *cts;
+ glong mhip_seek;
+ guint32 groupid;
+ Itdb_iTunesDB *itdb;
+
+ g_return_if_fail (album);
+ g_return_if_fail (memberlist);
+ g_return_if_fail (fexp);
+ g_return_if_fail (fexp->itdb);
+ g_return_if_fail (fexp->itunesdb);
+
+ cts = fexp->itunesdb;
+ itdb = fexp->itdb;
+ mhip_seek = cts->pos;
+
+ groupid = itdb->next_id++;
+ mk_mhip (fexp, 1, 256, groupid, 0, 0, 0);
+ mk_mhod (cts, MHOD_ID_TITLE, album);
+ fix_header (cts, mhip_seek);
+
+ /* write members */
+ for (gl=*memberlist; gl; gl=gl->next)
+ {
+ Itdb_Track *track = gl->data;
+ g_return_if_fail (track);
+ guint32 mhip_id;
+
+ mhip_seek = cts->pos;
+ mhip_id = itdb->next_id++;
+ mk_mhip (fexp, 1, 0, mhip_id, track->id, 0, groupid);
+ mk_mhod (cts, MHOD_ID_PLAYLIST, (void *)mhip_id);
+ fix_header (cts, mhip_seek);
+ }
+ }
+ GList *gl;
+ WContents *cts;
+ guint32 mhip_num;
+ GHashTable *album_hash;
+
+ g_return_val_if_fail (fexp, FALSE);
+ g_return_val_if_fail (fexp->itdb, FALSE);
+ g_return_val_if_fail (fexp->itunesdb, FALSE);
+ g_return_val_if_fail (pl, FALSE);
+
+ cts = fexp->itunesdb;
+
+ /* Create a list wit all available album names because we have to
+ group the podcasts according to albums */
+
+ album_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, free_memberlist);
+ for (gl=pl->members; gl; gl=gl->next)
+ {
+ GList **memberlist;
+ Itdb_Track *track = gl->data;
+ g_return_val_if_fail (track, FALSE);
+
+ memberlist = g_hash_table_lookup (album_hash, track->album);
+ if (!memberlist)
+ {
+ memberlist = g_new0 (GList *, 1);
+ g_hash_table_insert (album_hash, track->album, memberlist);
+ }
+ *memberlist = g_list_append (*memberlist, track);
+ }
+
+ g_hash_table_foreach (album_hash, write_one_podcast_group, fexp);
+
+ /* set number of mhips */
+ mhip_num = g_list_length (pl->members)+g_hash_table_size (album_hash);
+ put32lint_seek (cts, mhip_num, mhyp_seek+16);
+
+ g_hash_table_destroy (album_hash);
+
+ return TRUE;
+}
+
+
+
/* corresponds to mk_mhyp */
+/* mhsd_type: 2: write normal playlists
+ 3: write podcast playlist in special format */
/* Return FALSE in case of error and set fexp->error */
-static gboolean write_playlist(FExport *fexp, Itdb_Playlist *pl)
+static gboolean write_playlist (FExport *fexp,
+ Itdb_Playlist *pl, guint32 mhsd_type)
{
- GList *gl;
- gulong mhyp_seek, mhip_seek;
- guint32 i;
+ gulong mhyp_seek;
WContents *cts;
+ gboolean result = TRUE;
g_return_val_if_fail (fexp, FALSE);
g_return_val_if_fail (fexp->itdb, FALSE);
@@ -3028,7 +3280,7 @@ static gboolean write_playlist(FExport *fexp, Itdb_Playlist *pl)
fprintf(stderr, "Playlist: %s (%d tracks)\n", pl->name, g_list_length (pl->members));
#endif
- put_data (cts, "mhyp", 4); /* header */
+ put_string (cts, "mhyp"); /* header */
put32lint (cts, 108); /* length */
put32lint (cts, -1); /* size -> later */
if (pl->is_spl)
@@ -3036,12 +3288,16 @@ static gboolean write_playlist(FExport *fexp, Itdb_Playlist *pl)
else
put32lint (cts, 2); /* nr of mhods */
/* number of tracks in plist */
- put32lint (cts, g_list_length (pl->members));
+ put32lint (cts, -1); /* number of mhips -> later */
put32lint (cts, pl->type); /* 1 = main, 0 = visible */
put32lint (cts, 0); /* some timestamp */
put64lint (cts, pl->id); /* 64 bit ID */
- put32lint (cts, pl->unk036);
- put32lint (cts, pl->unk040);
+ pl->mhodcount = 1; /* we only write one mhod type < 50 */
+ put32lint (cts, pl->mhodcount);
+ pl->libmhodcount = 1; /* we don't write mhod type 52, and
+ "1" seems to be the default */
+ put16lint (cts, pl->libmhodcount);
+ put16lint (cts, pl->podcastflag);
put32lint (cts, pl->sortorder);
put32_n0 (cts, 15); /* ? */
@@ -3054,37 +3310,24 @@ static gboolean write_playlist(FExport *fexp, Itdb_Playlist *pl)
mk_mhod (cts, MHOD_ID_SPLRULES, &pl->splrules);
}
- /* write hard-coded tracks */
- i=0;
- for (gl=pl->members; gl; gl=gl->next)
+ if ((pl->podcastflag == ITDB_PL_FLAG_PODCAST) &&
+ (mhsd_type == 3))
{
- Itdb_Track *track = gl->data;
- if (!track)
- {
- g_set_error (&fexp->error,
- ITDB_FILE_ERROR,
- ITDB_FILE_ERROR_ITDB_CORRUPT,
- _("Database in memory corrupt (track pointer == NULL). Aborting export."));
- return FALSE;
- }
- mhip_seek = cts->pos;
- mk_mhip (fexp, i, track->id);
- mk_mhod (cts, MHOD_ID_PLAYLIST, (void *)i);
- /* note: with iTunes 4.9 the mhod is counted as a child to
- mhip, so we fill have put the total length of the mhip and
- mhod into the mhip header */
- fix_header (cts, mhip_seek);
- ++i;
+ /* write special podcast playlist */
+ result = write_podcast_mhips (fexp, pl, mhyp_seek);
+ }
+ else
+ { /* write standard playlist hard-coded tracks */
+ result = write_playlist_mhips (fexp, pl, mhyp_seek);
}
fix_header (cts, mhyp_seek);
- return TRUE;
+ return result;
}
-
/* Expects the master playlist to be the first in the list */
/* Return FALSE in case of error and set fexp->error */
-static gboolean write_mhsd_two(FExport *fexp)
+static gboolean write_mhsd_playlists (FExport *fexp, guint32 mhsd_type)
{
GList *gl;
glong mhsd_seek;
@@ -3093,24 +3336,19 @@ static gboolean write_mhsd_two(FExport *fexp)
g_return_val_if_fail (fexp, FALSE);
g_return_val_if_fail (fexp->itdb, FALSE);
g_return_val_if_fail (fexp->itunesdb, FALSE);
+ g_return_val_if_fail ((mhsd_type == 2) || (mhsd_type == 3), FALSE);
cts = fexp->itunesdb;
mhsd_seek = cts->pos; /* get position of mhsd header */
- mk_mhsd (fexp, 2); /* write header: type 2: playlists */
+ mk_mhsd (fexp, mhsd_type); /* write header */
/* write header with nr. of playlists */
mk_mhlp (fexp);
for(gl=fexp->itdb->playlists; gl; gl=gl->next)
{
Itdb_Playlist *pl = gl->data;
- if (!pl)
- {
- g_set_error (&fexp->error,
- ITDB_FILE_ERROR,
- ITDB_FILE_ERROR_ITDB_CORRUPT,
- _("Database in memory corrupt (playlist pointer == NULL). Aborting export."));
- return FALSE;
- }
- write_playlist (fexp, pl);
+ g_return_val_if_fail (pl, FALSE);
+
+ write_playlist (fexp, pl, mhsd_type);
if (fexp->error) return FALSE;
}
fix_header (cts, mhsd_seek);
@@ -3201,14 +3439,24 @@ static void reassign_ids (Itdb_iTunesDB *itdb)
g_return_if_fail (itdb);
- /* copy mpl->members to itdb->tracks to make sure they are in the
- same order (otherwise On-The-Go Playlists will not show the
- correct content) */
+ /* Arrange itdb->tracks in the same order as mpl->members
+ (otherwise On-The-Go Playlists will not show the correct
+ content. Unfortunately we cannot just copy mpl->members to
+ itdb->tracks because podcasts do not show up in the MPL. */
+
mpl = itdb_playlist_mpl (itdb);
g_return_if_fail (mpl);
- g_return_if_fail (g_list_length (mpl->members) == g_list_length (itdb->tracks));
- g_list_free (itdb->tracks);
- itdb->tracks = g_list_copy (mpl->members);
+
+ for (gl=g_list_last(mpl->members); gl; gl=gl->prev)
+ {
+ Itdb_Track *track = gl->data;
+ g_return_if_fail (track);
+ g_return_if_fail (g_list_find (itdb->tracks, track));
+
+ /* move this track to the beginning of itdb_tracks */
+ itdb->tracks = g_list_remove (itdb->tracks, track);
+ itdb->tracks = g_list_prepend (itdb->tracks, track);
+ }
/* assign unique IDs */
for (gl=itdb->tracks; gl; gl=gl->next)
@@ -3217,6 +3465,8 @@ static void reassign_ids (Itdb_iTunesDB *itdb)
g_return_if_fail (track);
track->id = id++;
}
+
+ itdb->next_id = id;
}
@@ -3243,12 +3493,16 @@ gboolean itdb_write_file (Itdb_iTunesDB *itdb, const gchar *filename,
fexp->itunesdb = wcontents_new (filename);
cts = fexp->itunesdb;
- mk_mhbd (fexp);
- if (write_mhsd_one(fexp))
- { /* write playlists mhsd */
- if (write_mhsd_two(fexp))
- {
- fix_header (cts, mhbd_seek);
+ mk_mhbd (fexp, 3); /* three mhsds */
+ /* write tracklist */
+ if (write_mhsd_tracks (fexp))
+ { /* write special podcast version mhsd */
+ if (write_mhsd_playlists (fexp, 3))
+ { /* write standard playlist mhsd */
+ if (write_mhsd_playlists (fexp, 2))
+ {
+ fix_header (cts, mhbd_seek);
+ }
}
}
if (!fexp->error)
diff --git a/src/itdb_private.h b/src/itdb_private.h
index abbd1d0..903f4cd 100644
--- a/src/itdb_private.h
+++ b/src/itdb_private.h
@@ -1,4 +1,4 @@
-/* Time-stamp: <2005-09-19 21:30:34 jcs>
+/* Time-stamp: <2005-09-23 23:53:25 jcs>
|
| Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>
| Part of the gtkpod project.
@@ -53,6 +53,7 @@ typedef struct
Itdb_iTunesDB *itdb;
FContents *itunesdb;
GList *pos_glist; /* temporary list to store position indicators */
+ gint32 pos_len; /* current length of above list */
GList *playcounts; /* contents of Play Counts file */
GTree *idtree; /* temporary tree with track id tree */
GError *error; /* where to report errors to */