1 /*
2 | Copyright (C) 2002-2007 Jorg Schuler <jcsjcs at users sourceforge net>
3 | Part of the gtkpod project.
4 |
5 | URL: http://www.gtkpod.org/
6 | URL: http://gtkpod.sourceforge.net/
7 |
8 | Much of the code in this file has originally been ported from the
9 | perl script "mktunes.pl" (part of the gnupod-tools collection)
10 | written by Adrian Ulrich <pab at blinkenlights.ch>:
11 |
12 | gnupod-tools: http://www.blinkenlights.ch/cgi-bin/fm.pl?get=ipod
13 |
14 | The code contained in this file is free software; you can redistribute
15 | it and/or modify it under the terms of the GNU Lesser General Public
16 | License as published by the Free Software Foundation; either version
17 | 2.1 of the License, or (at your option) any later version.
18 |
19 | This file is distributed in the hope that it will be useful,
20 | but WITHOUT ANY WARRANTY; without even the implied warranty of
21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 | Lesser General Public License for more details.
23 |
24 | You should have received a copy of the GNU Lesser General Public
25 | License along with this code; if not, write to the Free Software
26 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 | USA
28 |
29 | iTunes and iPod are trademarks of Apple
30 |
31 | This product is not supported/written/published by Apple!
32 |
33 | $Id$
34 */
35
36 /* Some notes on how to use the functions in this file:
37
38
39 *** Reading the iTunesDB ***
40
41 Itdb_iTunesDB *itunesdb_parse (gchar *path); /+ path to mountpoint +/
42 will read an Itdb_iTunesDB and pass the data over to your program.
43
44 The information given in the "Play Counts" file is also read if
45 available and the playcounts, star rating and the time last played
46 is updated.
47
48 Itdb_iTunesDB is a structure containing a GList for the tracks and a
49 GList for the playlists.
50
51 For each track these fields are set as follows:
52
53 "transferred" will be set to TRUE because all tracks read from a
54 Itdb_iTunesDB are obviously (or hopefully) already transferred to the
55 iPod.
56
57 "recent_playcount" is for information only (it will allow to
58 generate playlists with tracks played since the last time) and will
59 not be stored to the iPod.
60
61 The master playlist is guaranteed to be the first playlist, and
62 this must not be changed by your code.
63
64
65 *** Writing the Itdb_iTunesDB ***
66
67 gboolean itunesdb_write (gchar *path, Itdb_iTunesDB *itb)
68 /+ @path to mountpoint, itb to @write +/
69 will write an updated version of the Itdb_iTunesDB.
70
71 The "Play Counts" file is renamed to "Play Counts.bak" if it exists
72 to avoid reading it multiple times.
73
74 Please note that non-transferred tracks are not automatically
75 transferred to the iPod. A function
76
77 gboolean itunesdb_copy_track_to_ipod (gchar *path, Itdb_Track *track, gchar *pcfile)
78
79 is provided to help you do that, however.
80
81 The following functions most likely will be useful:
82
83 Itdb_Track *itunesdb_new_track (void);
84 Use itunesdb_new_track() to get an "initialized" track structure
85 (the "unknowns" are initialized with reasonable values).
86
87 gboolean itunesdb_cp (gchar *from_file, gchar *to_file);
88 void itunesdb_convert_filename_fs2ipod(gchar *ipod_file);
89 void itunesdb_convert_filename_ipod2fs(gchar *ipod_file);
90
91 void itunesdb_rename_files (const gchar *dirname);
92
93 (Renames/removes some files on the iPod (Playcounts, OTG
94 semaphore). Needs to be called if you write the Itdb_iTunesDB not
95 directly to the iPod but to some other location and then manually
96 copy the file from there to the iPod. That's much faster in the
97 case of using an iPod mounted in sync'ed mode.)
98
99 Jorg Schuler, 29.12.2004 */
100
101
102 /* call itdb_parse () to read the Itdb_iTunesDB */
103 /* call itdb_write () to write the Itdb_iTunesDB */
104
105
106 #ifdef HAVE_CONFIG_H
107 # include <config.h>
108 #endif
109
110 #include "db-artwork-parser.h"
111 #include "itdb_device.h"
112 #include "itdb_private.h"
113 #include "itdb_zlib.h"
114 #include "itdb_plist.h"
115
116 #include <errno.h>
117 #include <fcntl.h>
118 #include <glib-object.h>
119 #include <glib/gi18n-lib.h>
120 #include <glib/gstdio.h>
121 #include <glib.h>
122 #include <stdio.h>
123 #include <string.h>
124 #include <sys/types.h>
125 #include <time.h>
126 #ifdef HAVE_UNISTD_H
127 #include <unistd.h>
128 #endif
129
130 #define ITUNESDB_DEBUG 0
131 #define ITUNESDB_MHIT_DEBUG 0
132
133 #define ITUNESDB_COPYBLK (1024*1024*4) /* blocksize for cp () */
134
135
136 /* NOTE for developers:
137
138 Sometimes new MHOD string fields are added by Apple in the
139 iTunesDB. In that case you need to modify the code in the following
140 places:
141
142 itdb_itunesdb.c:
143 - enum MHOD_ID
144 - get_mhod(): inside the switch() statement. Currently no compiler
145 warning is given.
146 - get_mhod_string(): inside the switch() statement. A compiler warning
147 will help you find it.
148 - get_playlist(): inside the switch() statement. A compiler warning
149 will help you find it.
150 - get_mhit(): inside the switch() statement. A compiler warning
151 will help you find it.
152 - mk_mhod(): inside the switch() statement. A compiler warning
153 will help you find it.
154 - write_mhsd_tracks(): inside the for() loop.
155
156 itdb_track.c:
157 - itdb_track_free()
158 - itdb_track_duplicate()
159
160 itdb_playlists.c:
161 Analogous to itdb_track.c in case the string is part of the playlist
162 description.
163 */
164
165 /* Note: some of the comments for the MHOD_IDs are copied verbatim
166 * from http://ipodlinux.org/ITunesDB */
167 enum MHOD_ID {
168 MHOD_ID_TITLE = 1,
169 MHOD_ID_PATH = 2, /* file path on iPod (special format) */
170 MHOD_ID_ALBUM = 3,
171 MHOD_ID_ARTIST = 4,
172 MHOD_ID_GENRE = 5,
173 MHOD_ID_FILETYPE = 6,
174 /* MHOD_ID_EQSETTING = 7, */
175 MHOD_ID_COMMENT = 8,
176 /* Category - This is the category ("Technology", "Music", etc.) where
177 the podcast was located. Introduced in db version 0x0d. */
178 MHOD_ID_CATEGORY = 9,
179 MHOD_ID_COMPOSER = 12,
180 MHOD_ID_GROUPING = 13,
181 /* Description text (such as podcast show notes). Accessible by
182 wselecting the center button on the iPod, where this string is
183 displayed along with the song title, date, and
184 timestamp. Introduced in db version 0x0d. */
185 MHOD_ID_DESCRIPTION = 14,
186 /* Podcast Enclosure URL. Note: this is either a UTF-8 or ASCII
187 encoded string (NOT UTF-16). Also, there is no mhod::length value
188 for this type. Introduced in db version 0x0d. */
189 MHOD_ID_PODCASTURL = 15,
190 /* Podcast RSS URL. Note: this is either a UTF-8 or ASCII encoded
191 string (NOT UTF-16). Also, there is no mhod::length value for this
192 type. Introduced in db version 0x0d. */
193 MHOD_ID_PODCASTRSS = 16,
194 /* Chapter data. This is a m4a-style entry that is used to display
195 subsongs within a mhit. Introduced in db version 0x0d. */
196 MHOD_ID_CHAPTERDATA = 17,
197 /* Subtitle (usually the same as Description). Introduced in db
198 version 0x0d. */
199 MHOD_ID_SUBTITLE = 18,
200 /* Show (for TV Shows only). Introduced in db version 0x0d? */
201 MHOD_ID_TVSHOW = 19,
202 /* Episode # (for TV Shows only). Introduced in db version 0x0d? */
203 MHOD_ID_TVEPISODE = 20,
204 /* TV Network (for TV Shows only). Introduced in db version 0x0d? */
205 MHOD_ID_TVNETWORK = 21,
206 /* Album Artist. Introduced in db version 0x13? */
207 MHOD_ID_ALBUMARTIST = 22,
208 /* Sort key for artist. */
209 MHOD_ID_SORT_ARTIST = 23,
210 /* Appears to be a list of keywords pertaining to a track. Introduced
211 in db version 0x13? */
212 MHOD_ID_KEYWORDS = 24,
213 /* more sort keys, taking precedence over the standard entries if
214 present */
215 MHOD_ID_SORT_TITLE = 27,
216 MHOD_ID_SORT_ALBUM = 28,
217 MHOD_ID_SORT_ALBUMARTIST = 29,
218 MHOD_ID_SORT_COMPOSER = 30,
219 MHOD_ID_SORT_TVSHOW = 31,
220 MHOD_ID_SPLPREF = 50, /* settings for smart playlist */
221 MHOD_ID_SPLRULES = 51, /* rules for smart playlist */
222 MHOD_ID_LIBPLAYLISTINDEX = 52, /* Library Playlist Index */
223 MHOD_ID_LIBPLAYLISTJUMPTABLE=53,
224 MHOD_ID_PLAYLIST = 100,
225 MHOD_ID_ALBUM_ALBUM = 200, /* MHODs for the MHIAs */
226 MHOD_ID_ALBUM_ARTIST = 201,
227 MHOD_ID_ALBUM_SORT_ARTIST = 202,
228 MHOD_ID_ALBUM_ARTIST_MHII = 300,
229 };
230
231
232 /* Used as first iPod ID when renumbering (re-assigning) the iPod
233 (track) IDs before writing out the iTunesDB. */
234 static const gint FIRST_IPOD_ID=52;
235
236
237 enum MHOD52_SORTTYPE {
238 MHOD52_SORTTYPE_TITLE = 0x03,
239 MHOD52_SORTTYPE_ALBUM = 0x04,
240 MHOD52_SORTTYPE_ARTIST = 0x05,
241 MHOD52_SORTTYPE_GENRE = 0x07,
242 MHOD52_SORTTYPE_COMPOSER = 0x12/*,
243 MHOD52_SORTTYPE_TVSHOW = 0x1d,
244 MHOD52_SORTTYPE_TVSEASON = 0x1e,
245 MHOD52_SORTTYPE_TVEPISODE= 0x1f*/
246 };
247
248 struct mhod52track
249 {
250 gchar *album;
251 gchar *title;
252 gchar *artist;
253 gchar *genre;
254 gchar *composer;
255 gint track_nr;
256 gint cd_nr;
257 gint index;
258 gint numtracks;
259 gunichar2 letter_album;
260 gunichar2 letter_title;
261 gunichar2 letter_artist;
262 gunichar2 letter_genre;
263 gunichar2 letter_composer;
264 };
265
266 struct mhod53_entry
267 {
268 gunichar2 letter;
269 gint32 start;
270 gint32 count;
271 };
272
273 struct _MHODData
274 {
275 gboolean valid;
276 gint32 type;
277 union
278 {
279 gint32 track_pos;
280 gchar *string;
281 Itdb_Chapterdata *chapterdata;
282 Itdb_SPLPref *splpref;
283 Itdb_SPLRules *splrules;
284 GList *mhod52coltracks;
285 } data;
286 enum MHOD52_SORTTYPE mhod52sorttype;
287 GList *mhod53_list;
288 };
289
290 typedef struct _MHODData MHODData;
291
292 struct _PosEntry {
293 guint32 trackid;
294 gint32 track_pos;
295 };
296
297 typedef struct _PosEntry PosEntry;
298
299 /* Declarations */
300 static gboolean itdb_create_directories (Itdb_Device *device, GError **error);
301
302 /* ID for error domain */
itdb_file_error_quark(void)303 GQuark itdb_file_error_quark (void)
304 {
305 static GQuark q = 0;
306 if (q == 0)
307 q = g_quark_from_static_string ("itdb-file-error-quark");
308 return q;
309 }
310
311
312 static guint16 raw_get16lint (FContents *cts, glong seek);
313 static guint32 raw_get24lint (FContents *cts, glong seek);
314 static guint32 raw_get32lint (FContents *cts, glong seek);
315 static guint64 raw_get64lint (FContents *cts, glong seek);
316 static float raw_get32lfloat (FContents *cts, glong seek);
317 static guint16 raw_get16bint (FContents *cts, glong seek);
318 static guint32 raw_get24bint (FContents *cts, glong seek);
319 static guint32 raw_get32bint (FContents *cts, glong seek);
320 static guint64 raw_get64bint (FContents *cts, glong seek);
321 static float raw_get32bfloat (FContents *cts, glong seek);
322
323 static gboolean is_shuffle_2g (Itdb_Device *device);
324
325 static const ByteReader LITTLE_ENDIAN_READER =
326 {
327 raw_get16lint, raw_get24lint, raw_get32lint, raw_get64lint, raw_get32lfloat
328 };
329 static const ByteReader BIG_ENDIAN_READER =
330 {
331 raw_get16bint, raw_get24bint, raw_get32bint, raw_get64bint, raw_get32bfloat
332 };
333
fcontents_set_reversed(FContents * cts,gboolean reversed)334 static void fcontents_set_reversed(FContents *cts, gboolean reversed)
335 {
336 cts->reversed = reversed;
337 if (!reversed)
338 {
339 memcpy(&cts->le_reader, &LITTLE_ENDIAN_READER, sizeof (ByteReader));
340 memcpy(&cts->be_reader, &BIG_ENDIAN_READER, sizeof (ByteReader));
341 }
342 else
343 {
344 memcpy(&cts->le_reader, &BIG_ENDIAN_READER, sizeof (ByteReader));
345 memcpy(&cts->be_reader, &LITTLE_ENDIAN_READER, sizeof (ByteReader));
346 }
347 }
348
349 /* Read the contents of @filename and return a FContents
350 struct. Returns NULL in case of error and @error is set
351 accordingly */
fcontents_read(const gchar * fname,GError ** error)352 static FContents *fcontents_read (const gchar *fname, GError **error)
353 {
354 FContents *cts;
355
356 g_return_val_if_fail (fname, NULL);
357
358 cts = g_new0 (FContents, 1);
359 fcontents_set_reversed (cts, FALSE);
360
361 if (g_file_get_contents (fname, &cts->contents, &cts->length, error))
362 {
363 cts->filename = g_strdup (fname);
364 }
365 else
366 {
367 g_free (cts);
368 cts = NULL;
369 }
370 return cts;
371 }
372
373
374 /* Frees the memory taken by a FContents structure. NULL pointer will
375 * be ignored */
fcontents_free(FContents * cts)376 static void fcontents_free (FContents *cts)
377 {
378 if (cts)
379 {
380 g_free (cts->filename);
381 g_free (cts->contents);
382 /* must not g_error_free (cts->error) because the error was
383 propagated -> might free the error twice */
384 g_free (cts);
385 }
386 }
387
itdb_fsync(void)388 static void itdb_fsync (void)
389 {
390 #ifndef WIN32
391 sync();
392 #endif
393 }
394
395 /* There seems to be a problem with some distributions (kernel
396 versions or whatever -- even identical version numbers don't don't
397 show identical behaviour...): even though vfat is supposed to be
398 case insensitive, a difference is made between upper and lower case
399 under some special circumstances. As in "/iPod_Control/Music/F00"
400 and "/iPod_Control/Music/f00 "... If the former filename does not
401 exist, we try to find an existing case insensitive match for each
402 component of the filename. If we can find such a match, we return
403 it. Otherwise, we return NULL.*/
404
405 /**
406 * itdb_resolve_path:
407 * @root: in local encoding
408 * @components: in utf8
409 *
410 * Resolve the path to a track on the iPod
411 *
412 * We start by assuming that the iPod mount point exists. Then, for
413 * each component c of @track->ipod_path, we try to find an entry d in
414 * good_path that is case-insensitively equal to c. If we find d, we
415 * append d to good_path and make the result the new good_path.
416 * Otherwise, we quit and return NULL.
417 *
418 * Returns: path to track on the iPod or NULL.
419 */
itdb_resolve_path(const gchar * root,const gchar * const * components)420 gchar * itdb_resolve_path (const gchar *root,
421 const gchar * const * components)
422 {
423 gchar *good_path;
424 guint32 i;
425
426 if (!root) return NULL;
427 good_path = g_strdup (root);
428
429 for(i = 0 ; components[i] ; i++) {
430 GDir *cur_dir;
431 gchar *component_as_filename;
432 gchar *test_path;
433 gchar *component_stdcase;
434 const gchar *dir_file=NULL;
435
436 /* skip empty components */
437 if (strlen (components[i]) == 0) continue;
438 component_as_filename =
439 g_filename_from_utf8(components[i],-1,NULL,NULL,NULL);
440 test_path = g_build_filename(good_path,component_as_filename,NULL);
441 g_free(component_as_filename);
442 if(g_file_test(test_path,G_FILE_TEST_EXISTS)) {
443 /* This component does not require fixup */
444 g_free(good_path);
445 good_path = test_path;
446 continue;
447 }
448 g_free(test_path);
449 component_stdcase = g_utf8_casefold(components[i],-1);
450 /* Case insensitively compare the current component with each entry
451 * in the current directory. */
452
453 cur_dir = g_dir_open(good_path,0,NULL);
454 if (cur_dir) while ((dir_file = g_dir_read_name(cur_dir)))
455 {
456 gchar *file_utf8;
457 gchar *file_stdcase;
458 gboolean found;
459 gchar *new_good_path;
460
461 file_utf8 = g_filename_to_utf8(dir_file,-1,NULL,NULL,NULL);
462 if (file_utf8 == NULL) {
463 continue;
464 }
465 file_stdcase = g_utf8_casefold(file_utf8,-1);
466 g_free(file_utf8);
467 found = !g_utf8_collate(file_stdcase,component_stdcase);
468 g_free(file_stdcase);
469 if(!found)
470 {
471 /* This is not the matching entry */
472 continue;
473 }
474
475 new_good_path = g_build_filename(good_path,dir_file,NULL);
476 g_free(good_path);
477 good_path= new_good_path;
478 /* This is the matching entry, so we can stop searching */
479 break;
480 }
481
482 if(!dir_file) {
483 /* We never found a matching entry */
484 g_free(good_path);
485 good_path = NULL;
486 }
487
488 g_free(component_stdcase);
489 if (cur_dir) g_dir_close(cur_dir);
490 if(!good_path || !g_file_test(good_path,G_FILE_TEST_EXISTS))
491 break; /* We couldn't fix this component, so don't try later ones */
492 }
493
494 if(good_path && g_file_test(good_path,G_FILE_TEST_EXISTS))
495 return good_path;
496
497 g_free (good_path);
498 return NULL;
499 }
500
501
502 /* Check if the @seek with length @len is legal or out of
503 * range. Returns TRUE if legal and FALSE when it is out of range, in
504 * which case cts->error is set as well. */
check_seek(FContents * cts,glong seek,glong len)505 static gboolean check_seek (FContents *cts, glong seek, glong len)
506 {
507 g_return_val_if_fail (cts, FALSE);
508 g_return_val_if_fail (cts->contents, FALSE);
509
510 if ((seek+len <= cts->length) && (seek >=0))
511 {
512 return TRUE;
513 }
514 else
515 {
516 g_return_val_if_fail (cts->filename, FALSE);
517 g_set_error (&cts->error,
518 ITDB_FILE_ERROR,
519 ITDB_FILE_ERROR_SEEK,
520 _("Illegal seek to offset %ld (length %ld) in file '%s'."),
521 seek, len, cts->filename);
522 return FALSE;
523 }
524 }
525
526
527 /* Copies @len bytes from position @seek in @cts->contents to
528 @data. Returns FALSE on error and sets cts->error accordingly. */
seek_get_n_bytes(FContents * cts,gchar * data,glong seek,glong len)529 static gboolean seek_get_n_bytes (FContents *cts, gchar *data,
530 glong seek, glong len)
531 {
532 if (check_seek (cts, seek, len))
533 {
534 memcpy (data, &cts->contents[seek], len);
535 return TRUE;
536 }
537 return FALSE;
538 }
539
540 /* Compare @n bytes of @cts->contents starting at @seek and
541 * @data. Returns TRUE if equal, FALSE if not. Also returns FALSE on
542 * error, so you must check cts->error */
cmp_n_bytes_seek(FContents * cts,const gchar * data,glong seek,glong len)543 static gboolean cmp_n_bytes_seek (FContents *cts, const gchar *data,
544 glong seek, glong len)
545 {
546 if (check_seek (cts, seek, len))
547 {
548 gint i;
549 for (i=0; i<len; ++i)
550 {
551 if (cts->contents[seek+i] != data[i]) return FALSE;
552 }
553 return TRUE;
554 }
555 return FALSE;
556 }
557
558
559 /* Compare 4 bytes of @header with 4 bytes at @seek taking into
560 * consideration the status of cts->reversed */
check_header_seek(FContents * cts,const gchar * data,glong seek)561 static gboolean check_header_seek (FContents *cts, const gchar *data,
562 glong seek)
563 {
564 gchar rdata[4];
565 gint i, offset, sign;
566
567 g_return_val_if_fail (cts, FALSE);
568 g_return_val_if_fail (data, FALSE);
569 /* reverse data for compare if necessary */
570 if (cts->reversed)
571 {
572 offset = 3;
573 sign = -1;
574 }
575 else
576 {
577 offset = 0;
578 sign = 1;
579 }
580 for (i=0; i<4; ++i)
581 {
582 rdata[i] = data[offset + sign*i];
583 }
584
585 return cmp_n_bytes_seek (cts, rdata, seek, 4);
586 }
587
588
589 /* ------------------------------------------------------------
590 Not Endian Dependent
591 ------------------------------------------------------------ */
592
593 /* Returns the 1-byte number stored at position @seek. On error the
594 * GError in @cts is set. */
get8int(FContents * cts,glong seek)595 static inline guint8 get8int (FContents *cts, glong seek)
596 {
597 guint8 n=0;
598
599 if (check_seek (cts, seek, 1))
600 {
601 n = cts->contents[seek];
602 }
603 return n;
604 }
605
606
607 /* ------------------------------------------------------------
608 Little Endian
609 ------------------------------------------------------------ */
610
611 /* Get the 2-byte-number stored at position "seek" in little endian
612 encoding. On error the GError in @cts is set. */
raw_get16lint(FContents * cts,glong seek)613 static guint16 raw_get16lint (FContents *cts, glong seek)
614 {
615 guint16 n=0;
616
617 if (check_seek (cts, seek, 2))
618 {
619 memcpy (&n, &cts->contents[seek], 2);
620 n = GUINT16_FROM_LE (n);
621 }
622 return n;
623 }
624
625 /* Get the 3-byte-number stored at position "seek" in little endian
626 encoding. On error the GError in @cts is set. */
raw_get24lint(FContents * cts,glong seek)627 static guint32 raw_get24lint (FContents *cts, glong seek)
628 {
629 guint32 n=0;
630
631 if (check_seek (cts, seek, 3))
632 {
633 n = ((guint32)get8int (cts, seek+0)) +
634 (((guint32)get8int (cts, seek+1)) >> 8) +
635 (((guint32)get8int (cts, seek+2)) >> 16);
636 }
637 return n;
638 }
639
640 /* Get the 4-byte-number stored at position "seek" in little endian
641 encoding. On error the GError in @cts is set. */
raw_get32lint(FContents * cts,glong seek)642 static guint32 raw_get32lint (FContents *cts, glong seek)
643 {
644 guint32 n=0;
645
646 if (check_seek (cts, seek, 4))
647 {
648 memcpy (&n, &cts->contents[seek], 4);
649 n = GUINT32_FROM_LE (n);
650 }
651 return n;
652 }
653
654 /* Get 4 byte floating number */
raw_get32lfloat(FContents * cts,glong seek)655 static float raw_get32lfloat (FContents *cts, glong seek)
656 {
657 union
658 {
659 guint32 i;
660 float f;
661 } flt;
662
663 g_return_val_if_fail (sizeof (float) == 4, 0);
664
665 flt.i = raw_get32lint (cts, seek);
666
667 return flt.f;
668 }
669
670
671 /* Get the 8-byte-number stored at position "seek" in little endian
672 encoding. On error the GError in @cts is set. */
raw_get64lint(FContents * cts,glong seek)673 static guint64 raw_get64lint (FContents *cts, glong seek)
674 {
675 guint64 n=0;
676
677 if (check_seek (cts, seek, 8))
678 {
679 memcpy (&n, &cts->contents[seek], 8);
680 n = GUINT64_FROM_LE (n);
681 }
682 return n;
683 }
684
685
686 /* ------------------------------------------------------------
687 Big Endian
688 ------------------------------------------------------------ */
689
690 /* Get the 2-byte-number stored at position "seek" in little endian
691 encoding. On error the GError in @cts is set. */
raw_get16bint(FContents * cts,glong seek)692 static guint16 raw_get16bint (FContents *cts, glong seek)
693 {
694 guint16 n=0;
695
696 if (check_seek (cts, seek, 2))
697 {
698 memcpy (&n, &cts->contents[seek], 2);
699 n = GUINT16_FROM_BE (n);
700 }
701 return n;
702 }
703
704 /* Get the 3-byte-number stored at position "seek" in big endian
705 encoding. On error the GError in @cts is set. */
raw_get24bint(FContents * cts,glong seek)706 static guint32 raw_get24bint (FContents *cts, glong seek)
707 {
708 guint32 n=0;
709
710 if (check_seek (cts, seek, 3))
711 {
712 n = ((guint32)get8int (cts, seek+2)) +
713 (((guint32)get8int (cts, seek+1)) >> 8) +
714 (((guint32)get8int (cts, seek+0)) >> 16);
715 }
716 return n;
717 }
718
719 /* Get the 4-byte-number stored at position "seek" in big endian
720 encoding. On error the GError in @cts is set. */
raw_get32bint(FContents * cts,glong seek)721 static guint32 raw_get32bint (FContents *cts, glong seek)
722 {
723 guint32 n=0;
724
725 if (check_seek (cts, seek, 4))
726 {
727 memcpy (&n, &cts->contents[seek], 4);
728 n = GUINT32_FROM_BE (n);
729 }
730 return n;
731 }
732
733 /* Get 4 byte floating number */
raw_get32bfloat(FContents * cts,glong seek)734 static float raw_get32bfloat (FContents *cts, glong seek)
735 {
736 union
737 {
738 guint32 i;
739 float f;
740 } flt;
741
742 g_return_val_if_fail (sizeof (float) == 4, 0);
743
744 flt.i = raw_get32bint (cts, seek);
745
746 return flt.f;
747 }
748
749 /* Get the 8-byte-number stored at position "seek" in big endian
750 encoding. On error the GError in @cts is set. */
raw_get64bint(FContents * cts,glong seek)751 static guint64 raw_get64bint (FContents *cts, glong seek)
752 {
753 guint64 n=0;
754
755 if (check_seek (cts, seek, 8))
756 {
757 memcpy (&n, &cts->contents[seek], 8);
758 n = GUINT64_FROM_BE (n);
759 }
760 return n;
761 }
762
763
764 /* ------------------------------------------------------------
765 Little Endian
766 ------------------------------------------------------------ */
767
get16lint(FContents * cts,glong seek)768 static inline guint16 get16lint (FContents *cts, glong seek)
769 {
770 return cts->le_reader.get16int (cts, seek);
771 }
772
get24lint(FContents * cts,glong seek)773 static inline guint32 get24lint (FContents *cts, glong seek)
774 {
775 return cts->le_reader.get24int (cts, seek);
776 }
777 #if 0
778 static inline guint32 get24bint (FContents *cts, glong seek)
779 {
780 return cts->be_reader.get24int (cts, seek);
781 }
782 #endif
get32lint(FContents * cts,glong seek)783 static inline guint32 get32lint (FContents *cts, glong seek)
784 {
785 return cts->le_reader.get32int (cts, seek);
786 }
787
get32lfloat(FContents * cts,glong seek)788 static inline float get32lfloat (FContents *cts, glong seek)
789 {
790 return cts->le_reader.get32float (cts, seek);
791 }
792
get64lint(FContents * cts,glong seek)793 static inline guint64 get64lint (FContents *cts, glong seek)
794 {
795 return cts->le_reader.get64int (cts, seek);
796 }
797
798
799
800 /* ------------------------------------------------------------
801 Big Endian
802 ------------------------------------------------------------ */
803
get16bint(FContents * cts,glong seek)804 static inline guint16 get16bint (FContents *cts, glong seek)
805 {
806 return cts->be_reader.get16int (cts, seek);
807 }
808
get32bint(FContents * cts,glong seek)809 static inline guint32 get32bint (FContents *cts, glong seek)
810 {
811 return cts->be_reader.get32int (cts, seek);
812 }
813
814 #if 0
815 static inline float get32bfloat (FContents *cts, glong seek)
816 {
817 return cts->be_reader.get32float (cts, seek);
818 }
819 #endif
820
get64bint(FContents * cts,glong seek)821 static inline guint64 get64bint (FContents *cts, glong seek)
822 {
823 return cts->be_reader.get64int (cts, seek);
824 }
825
826 /* Try to convert from UTF-16 to UTF-8 and handle partial characters
827 * at the end of the string */
utf16_to_utf8_with_partial(gunichar2 * entry_utf16)828 static char *utf16_to_utf8_with_partial (gunichar2 *entry_utf16)
829 {
830 GError *error = NULL;
831 char *entry_utf8;
832 glong items_read;
833
834 entry_utf8 = g_utf16_to_utf8 (entry_utf16, -1, &items_read, NULL, &error);
835 if (entry_utf8 == NULL) {
836 if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT)) {
837 entry_utf8 = g_utf16_to_utf8 (entry_utf16, items_read, NULL, NULL, NULL);
838 }
839 g_error_free (error);
840 }
841
842 return entry_utf8;
843 }
844
845 /* Fix little endian UTF16 String to correct byteorder if necessary
846 * (all strings in the Itdb_iTunesDB are little endian except for the ones
847 * in smart playlists). */
fixup_little_utf16(gunichar2 * utf16_string)848 static gunichar2 *fixup_little_utf16 (gunichar2 *utf16_string)
849 {
850 # if (G_BYTE_ORDER == G_BIG_ENDIAN)
851 gint32 i;
852 if (utf16_string)
853 {
854 for(i=0; utf16_string[i]; i++)
855 {
856 utf16_string[i] = GUINT16_SWAP_LE_BE (utf16_string[i]);
857 }
858 }
859 # endif
860 return utf16_string;
861 }
862
863 /* Fix big endian UTF16 String to correct byteorder if necessary (only
864 * strings in smart playlists and chapter data are big endian) */
fixup_big_utf16(gunichar2 * utf16_string)865 static gunichar2 *fixup_big_utf16 (gunichar2 *utf16_string)
866 {
867 # if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
868 gint32 i;
869 if (utf16_string)
870 {
871 for(i=0; utf16_string[i]; i++)
872 {
873 utf16_string[i] = GUINT16_SWAP_LE_BE (utf16_string[i]);
874 }
875 }
876 # endif
877 return utf16_string;
878 }
879
880
881 #define CHECK_ERROR(imp, val) if (cts->error) { g_propagate_error (&imp->error, cts->error); return (val); }
882
883
884 /* get next playcount, that is the first entry of GList
885 * playcounts. This entry is removed from the list. You must free the
886 * return value after use */
playcount_take_next(FImport * fimp)887 static struct playcount *playcount_take_next (FImport *fimp)
888 {
889 struct playcount *playcount;
890 g_return_val_if_fail (fimp, NULL);
891
892 playcount = g_list_nth_data (fimp->playcounts, 0);
893
894 if (playcount)
895 fimp->playcounts = g_list_remove (fimp->playcounts, playcount);
896 return playcount;
897 }
898
899 /* delete all entries of GList *playcounts */
playcounts_free(FImport * fimp)900 static void playcounts_free (FImport *fimp)
901 {
902 struct playcount *playcount;
903
904 g_return_if_fail (fimp);
905
906 while ((playcount=playcount_take_next (fimp))) g_free (playcount);
907 }
908
909
910 /* called by playcounts_init */
playcounts_read(FImport * fimp,FContents * cts)911 static gboolean playcounts_read (FImport *fimp, FContents *cts)
912 {
913 GList* playcounts = NULL;
914 guint32 header_length, entry_length, entry_num, i=0;
915
916 g_return_val_if_fail (fimp, FALSE);
917 g_return_val_if_fail (cts, FALSE);
918
919 if (!check_header_seek (cts, "mhdp", 0))
920 {
921 if (cts->error)
922 {
923 g_propagate_error (&fimp->error, cts->error);
924 return FALSE;
925 }
926 fcontents_set_reversed (cts, TRUE);
927 if (!check_header_seek (cts, "mhdp", 0))
928 {
929 if (cts->error)
930 {
931 g_propagate_error (&fimp->error, cts->error);
932 return FALSE;
933 }
934 else
935 { /* set error */
936 g_return_val_if_fail (cts->filename, FALSE);
937 g_set_error (&fimp->error,
938 ITDB_FILE_ERROR,
939 ITDB_FILE_ERROR_CORRUPT,
940 _("Not a Play Counts file: '%s' (missing mhdp header)."),
941 cts->filename);
942 return FALSE;
943 }
944 }
945 }
946 header_length = get32lint (cts, 4);
947 CHECK_ERROR (fimp, FALSE);
948 /* all the headers I know are 0x60 long -- if this one is longer
949 we can simply ignore the additional information */
950 if (header_length < 0x60)
951 {
952 g_set_error (&fimp->error,
953 ITDB_FILE_ERROR,
954 ITDB_FILE_ERROR_CORRUPT,
955 _("Play Counts file ('%s'): header length smaller than expected (%d<96)."),
956 cts->filename, header_length);
957 return FALSE;
958 }
959 entry_length = get32lint (cts, 8);
960 CHECK_ERROR (fimp, FALSE);
961 /* all the entries I know are 0x0c (firmware 1.3) or 0x10
962 * (firmware 2.0), 0x14 (iTunesDB version 0x0d) or 0x1c (iTunesDB
963 * version 0x13) in length */
964 if (entry_length < 0x0c)
965 {
966 g_set_error (&fimp->error,
967 ITDB_FILE_ERROR,
968 ITDB_FILE_ERROR_CORRUPT,
969 _("Play Counts file ('%s'): entry length smaller than expected (%d<12)."),
970 cts->filename, entry_length);
971 return FALSE;
972 }
973 /* number of entries */
974 entry_num = get32lint (cts, 12);
975 CHECK_ERROR (fimp, FALSE);
976 for (i=0; i<entry_num; ++i)
977 {
978 guint32 mac_time;
979 struct playcount *playcount;
980 glong seek = header_length + i*entry_length;
981
982 check_seek (cts, seek, entry_length);
983 CHECK_ERROR (fimp, FALSE);
984
985 playcount = g_new0 (struct playcount, 1);
986 playcounts = g_list_prepend (playcounts, playcount);
987 playcount->playcount = get32lint (cts, seek);
988 mac_time = get32lint (cts, seek+4);
989 playcount->time_played = device_time_mac_to_time_t (fimp->itdb->device, mac_time);
990 playcount->bookmark_time = get32lint (cts, seek+8);
991
992 /* rating only exists if the entry length is at least 0x10 */
993 if (entry_length >= 0x10)
994 {
995 playcount->rating = get32lint (cts, seek+12);
996 }
997 else
998 {
999 playcount->rating = NO_PLAYCOUNT;
1000 }
1001 /* unk16 only exists if the entry length is at least 0x14 */
1002 if (entry_length >= 0x14)
1003 {
1004 playcount->pc_unk16 = get32lint (cts, seek+16);
1005 }
1006 /* skip_count and last_skipped only exists if the entry length
1007 is at least 0x1c */
1008 if (entry_length >= 0x1c)
1009 {
1010 playcount->skipcount = get32lint (cts, seek+20);
1011 mac_time = get32lint (cts, seek+24);
1012 playcount->last_skipped = device_time_mac_to_time_t (fimp->itdb->device,
1013 mac_time);
1014
1015 }
1016 }
1017 fimp->playcounts = g_list_reverse(playcounts);
1018 return TRUE;
1019 }
1020
1021
1022 /* called by playcounts_init */
itunesstats_read(FImport * fimp,FContents * cts)1023 static gboolean itunesstats_read (FImport *fimp, FContents *cts)
1024 {
1025 GList* playcounts = NULL;
1026 guint32 entry_num, entry_length, i=0;
1027 struct playcount *playcount;
1028 glong seek = 0;
1029
1030 g_return_val_if_fail (fimp, FALSE);
1031 g_return_val_if_fail (cts, FALSE);
1032 g_return_val_if_fail (fimp->itdb, FALSE);
1033 g_return_val_if_fail (fimp->itdb->device, FALSE);
1034
1035 if ( is_shuffle_2g (fimp->itdb->device))
1036 {
1037 /* Old iTunesStats format */
1038
1039 /* number of entries */
1040 entry_num = get24lint (cts, seek);
1041 CHECK_ERROR (fimp, FALSE);
1042 seek = 6;
1043 for (i=0; i<entry_num; ++i)
1044 {
1045 entry_length = get24lint (cts, seek+0);
1046 CHECK_ERROR (fimp, FALSE);
1047 if (entry_length < 18)
1048 {
1049 g_set_error (&fimp->error,
1050 ITDB_FILE_ERROR,
1051 ITDB_FILE_ERROR_CORRUPT,
1052 _("iTunesStats file ('%s'): entry length smaller than expected (%d<18)."),
1053 cts->filename, entry_length);
1054 return FALSE;
1055 }
1056 playcount = g_new0 (struct playcount, 1);
1057 playcounts = g_list_prepend (playcounts, playcount);
1058 /* NOTE:
1059 *
1060 * The iPod (firmware 1.3, 2.0, ...?) doesn't seem to use the
1061 * timezone information correctly -- no matter what you set
1062 * iPod's timezone to, it will always record as if it were set
1063 * to UTC -- we need to subtract the difference between
1064 * current timezone and UTC to get a correct
1065 * display. -- this should be done by the application were
1066 * necessary */
1067 playcount->bookmark_time = get24lint (cts, seek+3);
1068 CHECK_ERROR (fimp, FALSE);
1069 playcount->st_unk06 = get24lint (cts, seek+6);
1070 CHECK_ERROR (fimp, FALSE);
1071 playcount->st_unk09 = get24lint (cts, seek+9);
1072 CHECK_ERROR (fimp, FALSE);
1073 playcount->playcount = get24lint (cts, seek+12);
1074 CHECK_ERROR (fimp, FALSE);
1075 playcount->skipped = get24lint (cts, seek+15);
1076 CHECK_ERROR (fimp, FALSE);
1077
1078 playcount->rating = NO_PLAYCOUNT;
1079
1080 seek += entry_length;
1081 }
1082 } else {
1083 /* New iTunesStats format */
1084 entry_num = get32lint (cts, seek); /* Number of entries */
1085 CHECK_ERROR (fimp, FALSE);
1086
1087 seek = 8;
1088 for (i=0; i<entry_num; ++i)
1089 {
1090 entry_length = get32lint (cts, seek+0);
1091 CHECK_ERROR (fimp, FALSE);
1092 if (entry_length < 32)
1093 {
1094 g_set_error (&fimp->error,
1095 ITDB_FILE_ERROR,
1096 ITDB_FILE_ERROR_CORRUPT,
1097 _("iTunesStats file ('%s'): entry length smaller than expected (%d<32)."),
1098 cts->filename, entry_length);
1099 return FALSE;
1100 }
1101 playcount = g_new0 (struct playcount, 1);
1102 playcounts = g_list_prepend (playcounts, playcount);
1103
1104 playcount->bookmark_time = get32lint (cts, seek+4);
1105 CHECK_ERROR (fimp, FALSE);
1106 playcount->playcount = get32lint (cts, seek+8);
1107 CHECK_ERROR (fimp, FALSE);
1108 playcount->time_played = get32lint (cts, seek+12);
1109 CHECK_ERROR (fimp, FALSE);
1110 playcount->skipped = get32lint (cts, seek+16);
1111 CHECK_ERROR (fimp, FALSE);
1112 playcount->last_skipped = get32lint (cts, seek+20);
1113 CHECK_ERROR (fimp, FALSE);
1114 /* 8 unknown bytes follow but it is unlikely they are
1115 * the same as the unknown fields in the old format.
1116 * I have only seen zeros in those spots so far so I'm
1117 * ignoring them for now. */
1118 }
1119 }
1120 fimp->playcounts = g_list_reverse(playcounts);
1121 return TRUE;
1122 }
1123
playcounts_plist_get_gint64(GHashTable * track_dict,const char * key)1124 static gint64 playcounts_plist_get_gint64 (GHashTable *track_dict,
1125 const char *key)
1126 {
1127 GValue *value;
1128
1129 value = g_hash_table_lookup (track_dict, key);
1130 if (value && G_VALUE_HOLDS (value, G_TYPE_INT64)) {
1131 return g_value_get_int64 (value);
1132 }
1133
1134 return 0;
1135 }
1136
1137 #ifndef HAVE_G_INT64_EQUAL
g_int64_equal(gconstpointer v1,gconstpointer v2)1138 static gboolean g_int64_equal (gconstpointer v1, gconstpointer v2)
1139 {
1140 return *((const gint64*) v1) == *((const gint64*) v2);
1141 }
1142 #endif
1143
1144 #ifndef HAVE_G_INT64_HASH
g_int64_hash(gconstpointer v)1145 static guint g_int64_hash (gconstpointer v)
1146 {
1147 return (guint) *(const gint64*) v;
1148 }
1149 #endif
1150
1151 /* called by playcounts_init */
playcounts_plist_read(FImport * fimp,GValue * plist_data)1152 static gboolean playcounts_plist_read (FImport *fimp, GValue *plist_data)
1153 {
1154 GHashTable *playcounts;
1155 struct playcount *playcount;
1156 GHashTable *pc_dict, *track_dict;
1157 GValue *to_parse;
1158 GArray *array;
1159 gint i;
1160 guint32 mac_time;
1161 guint64 *dbid;
1162
1163 g_return_val_if_fail (G_VALUE_HOLDS (plist_data, G_TYPE_HASH_TABLE), FALSE);
1164 pc_dict = g_value_get_boxed (plist_data);
1165
1166 to_parse = g_hash_table_lookup (pc_dict, "tracks");
1167 if (to_parse == NULL) {
1168 return FALSE;
1169 }
1170 if (!G_VALUE_HOLDS (to_parse, G_TYPE_ARRAY)) {
1171 return FALSE;
1172 }
1173
1174 playcounts = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, g_free);
1175
1176 array = (GArray*)g_value_get_boxed (to_parse);
1177 for (i = 0; i < array->len; i++) {
1178 if (!G_VALUE_HOLDS (g_array_index (array, GValue *, i), G_TYPE_HASH_TABLE)) {
1179 continue;
1180 }
1181
1182 track_dict = g_value_get_boxed (g_array_index (array, GValue *, i));
1183 if (track_dict == NULL)
1184 continue;
1185
1186 to_parse = g_hash_table_lookup (track_dict, "persistentID");
1187 if (!to_parse)
1188 continue;
1189
1190 dbid = g_new0 (guint64, 1);
1191 if (!G_VALUE_HOLDS (to_parse, G_TYPE_INT64))
1192 continue;
1193 *dbid = g_value_get_int64 (to_parse);
1194 playcount = g_new0 (struct playcount, 1);
1195 g_hash_table_insert (playcounts, dbid, playcount);
1196
1197 playcount->bookmark_time = playcounts_plist_get_gint64 (track_dict, "bookmarkTimeInMS");
1198 playcount->playcount = playcounts_plist_get_gint64 (track_dict, "playCount");
1199 mac_time = playcounts_plist_get_gint64 (track_dict, "playMacOSDate");
1200 playcount->time_played = device_time_mac_to_time_t (fimp->itdb->device, mac_time);
1201 playcount->skipcount = playcounts_plist_get_gint64 (track_dict, "skipCount");
1202 mac_time = playcounts_plist_get_gint64 (track_dict, "skipMacOSDate");
1203 playcount->last_skipped = device_time_mac_to_time_t (fimp->itdb->device, mac_time);
1204 playcount->rating = playcounts_plist_get_gint64 (track_dict, "userRating");
1205 if (!playcount->rating)
1206 playcount->rating = NO_PLAYCOUNT;
1207
1208 to_parse = g_hash_table_lookup (track_dict, "playedState");
1209 if (to_parse && G_VALUE_HOLDS (to_parse, G_TYPE_BOOLEAN)) {
1210 ; /* What do we do with this? */
1211 }
1212 }
1213 fimp->pcounts2 = playcounts;
1214 return TRUE;
1215 }
1216
1217 /* Delete the file containing the playcounts, once we saved the database,
1218 * the recent playcounts will have been added to the database, so this
1219 * file is no longer useful. */
playcounts_reset(const char * mountpoint)1220 static void playcounts_reset (const char *mountpoint)
1221 {
1222 const gchar *dirs[] = { "Play Counts", "iTunesStats",
1223 "PlayCounts.plist", NULL };
1224 const gchar **it;
1225
1226 for (it = dirs; *it != NULL; it++) {
1227 char *playcounts_path;
1228 const gchar *components[] = { *it, NULL };
1229 playcounts_path = itdb_resolve_path (mountpoint, components);
1230 if (playcounts_path != NULL) {
1231 g_unlink (playcounts_path);
1232 g_free (playcounts_path);
1233 }
1234 }
1235 }
1236
1237 /* Read the Play Count file (formed by adding "Play Counts" to the
1238 * directory component of fimp->itdb->itdb_filename) and set up the
1239 * GList *playcounts. If no Play Count file is present, attempt to
1240 * read the iTunesStats file instead, which is used on the Shuffle for
1241 * the same purpose.
1242 *
1243 * Returns TRUE on success (also when no Play Count
1244 * file is found as this is not an error) and FALSE otherwise, in
1245 * which case fimp->error is set accordingly. */
playcounts_init(FImport * fimp)1246 static gboolean playcounts_init (FImport *fimp)
1247 {
1248 const gchar *plc[] = {"Play Counts", NULL};
1249 const gchar *ist[] = {"iTunesStats", NULL};
1250 const gchar *plcpl[] = {"PlayCounts.plist", NULL};
1251 gchar *plcname, *dirname, *istname, *plcplname;
1252 gboolean result=TRUE;
1253 struct stat filestat;
1254 FContents *cts;
1255 GValue *plist_data;
1256
1257 g_return_val_if_fail (fimp, FALSE);
1258 g_return_val_if_fail (!fimp->error, FALSE);
1259 g_return_val_if_fail (!fimp->playcounts, FALSE);
1260 g_return_val_if_fail (fimp->itdb, FALSE);
1261 g_return_val_if_fail (fimp->itdb->filename, FALSE);
1262
1263 dirname = g_path_get_dirname (fimp->itdb->filename);
1264
1265 plcname = itdb_resolve_path (dirname, plc);
1266 istname = itdb_resolve_path (dirname, ist);
1267 plcplname = itdb_resolve_path (dirname, plcpl);
1268
1269 g_free (dirname);
1270
1271 /* skip if no playcounts file is present */
1272 if (plcname)
1273 {
1274 /* skip if playcounts file has zero-length (often happens after
1275 * dosfsck) */
1276 stat (plcname, &filestat);
1277 if (filestat.st_size >= 0x60)
1278 {
1279 cts = fcontents_read (plcname, &fimp->error);
1280 if (cts)
1281 {
1282 result = playcounts_read (fimp, cts);
1283 fcontents_free (cts);
1284 }
1285 else
1286 {
1287 result = FALSE;
1288 }
1289 }
1290 }
1291 else if (istname)
1292 {
1293 /* skip if iTunesStats file has zero-length (often happens after
1294 * dosfsck) */
1295 stat (istname, &filestat);
1296 if (filestat.st_size >= 0x06)
1297 {
1298 cts = fcontents_read (istname, &fimp->error);
1299 if (cts)
1300 {
1301 result = itunesstats_read (fimp, cts);
1302 fcontents_free (cts);
1303 }
1304 else
1305 {
1306 result = FALSE;
1307 }
1308 }
1309 }
1310 else if (plcplname)
1311 {
1312 /* skip if PlayCounts.plist file has zero-length */
1313 stat (plcplname, &filestat);
1314 if (filestat.st_size > 0)
1315 {
1316 plist_data = itdb_plist_parse_from_file (plcplname, &fimp->error);
1317 if (plist_data)
1318 {
1319 result = playcounts_plist_read (fimp, plist_data);
1320 g_value_unset (plist_data);
1321 }
1322 else
1323 {
1324 result = FALSE;
1325 }
1326 }
1327 }
1328
1329 g_free (plcname);
1330 g_free (istname);
1331 g_free (plcplname);
1332
1333 return result;
1334 }
1335
1336
1337 /* Free the memory taken by @fimp. fimp->itdb must be freed separately
1338 * before calling this function */
itdb_free_fimp(FImport * fimp)1339 static void itdb_free_fimp (FImport *fimp)
1340 {
1341 if (fimp)
1342 {
1343 if (fimp->fcontents) fcontents_free (fimp->fcontents);
1344 g_list_free (fimp->pos_glist);
1345 g_list_free (fimp->tracks);
1346 playcounts_free (fimp);
1347 if (fimp->pcounts2 != NULL) {
1348 g_hash_table_destroy (fimp->pcounts2);
1349 }
1350 g_free (fimp);
1351 }
1352 }
1353
1354 /**
1355 * itdb_free:
1356 * @itdb: an #Itdb_iTunesDB
1357 *
1358 * Free the memory taken by @itdb.
1359 */
itdb_free(Itdb_iTunesDB * itdb)1360 void itdb_free (Itdb_iTunesDB *itdb)
1361 {
1362 if (itdb)
1363 {
1364 g_list_foreach (itdb->playlists,
1365 (GFunc)(itdb_playlist_free), NULL);
1366
1367 if (itdb->priv) {
1368 if (itdb->priv->mhsd5_playlists)
1369 {
1370 g_list_foreach (itdb->priv->mhsd5_playlists,
1371 (GFunc)(itdb_playlist_free), NULL);
1372 }
1373
1374 g_free(itdb->priv->genius_cuid);
1375 }
1376
1377 g_list_free (itdb->playlists);
1378 g_list_foreach (itdb->tracks,
1379 (GFunc)(itdb_track_free), NULL);
1380 g_list_free (itdb->tracks);
1381 g_free (itdb->filename);
1382 itdb_device_free (itdb->device);
1383 if (itdb->userdata && itdb->userdata_destroy)
1384 (*itdb->userdata_destroy) (itdb->userdata);
1385 g_free (itdb->priv);
1386 g_free (itdb);
1387 }
1388 }
1389
1390 /**
1391 * itdb_duplicate:
1392 * @itdb: an #Itdb_iTunesDB
1393 *
1394 * Duplicate @itdb
1395 * FIXME: not implemented yet
1396 *
1397 * Returns: always return NULL since it's unimplemented
1398 */
itdb_duplicate(Itdb_iTunesDB * itdb)1399 Itdb_iTunesDB *itdb_duplicate (Itdb_iTunesDB *itdb)
1400 {
1401 g_return_val_if_fail (itdb, NULL);
1402 /* FIXME: not yet implemented */
1403 g_return_val_if_reached (NULL);
1404 }
1405
1406 /**
1407 * itdb_playlists_number:
1408 * @itdb: an #Itdb_iTunesDB
1409 *
1410 * Counts the number of playlists stored in @itdb
1411 *
1412 * Returns: the number of playlists in @itdb (including the master
1413 * playlist)
1414 */
itdb_playlists_number(Itdb_iTunesDB * itdb)1415 guint32 itdb_playlists_number (Itdb_iTunesDB *itdb)
1416 {
1417 g_return_val_if_fail (itdb, 0);
1418
1419 return g_list_length (itdb->playlists);
1420 }
1421
1422 /**
1423 * itdb_tracks_number:
1424 * @itdb: an #Itdb_iTunesDB
1425 *
1426 * Counts the number of tracks stored in @itdb
1427 *
1428 * Returns: the number of tracks in @itdb
1429 */
itdb_tracks_number(Itdb_iTunesDB * itdb)1430 guint32 itdb_tracks_number (Itdb_iTunesDB *itdb)
1431 {
1432 g_return_val_if_fail (itdb, 0);
1433
1434 return g_list_length (itdb->tracks);
1435 }
1436
1437 /**
1438 * itdb_tracks_number_nontransferred:
1439 * @itdb: an #Itdb_iTunesDB
1440 *
1441 * Counts the number of non-transferred tracks in @itdb
1442 *
1443 * Returns: the number of tracks in @itdb that haven't been transferred
1444 * to the iPod yet (ie the number of #Itdb_Track in which the transferred field
1445 * is false)
1446 */
itdb_tracks_number_nontransferred(Itdb_iTunesDB * itdb)1447 guint32 itdb_tracks_number_nontransferred (Itdb_iTunesDB *itdb)
1448 {
1449 guint n = 0;
1450 GList *gl;
1451 g_return_val_if_fail (itdb, 0);
1452
1453 for (gl=itdb->tracks; gl; gl=gl->next)
1454 {
1455 Itdb_Track *track = gl->data;
1456 g_return_val_if_fail (track, 0);
1457 if (!track->transferred) ++n;
1458 }
1459 return n;
1460 }
1461
1462 /**
1463 * itdb_new:
1464 *
1465 * Creates a new Itdb_iTunesDB with the unknowns filled in to reasonable
1466 * values.
1467 *
1468 * Returns: a newly created Itdb_iTunesDB to be freed with itdb_free()
1469 * when it's no longer needed
1470 */
itdb_new(void)1471 Itdb_iTunesDB *itdb_new (void)
1472 {
1473 Itdb_iTunesDB *itdb;
1474
1475 #if !GLIB_CHECK_VERSION(2, 36, 0)
1476 /* g_type_init() is obsolete with newer glib versions */
1477 static GOnce g_type_init_once = G_ONCE_INIT;
1478 g_once (&g_type_init_once, (GThreadFunc)g_type_init, NULL);
1479 #endif
1480
1481 itdb = g_new0 (Itdb_iTunesDB, 1);
1482 itdb->priv = g_new0 (Itdb_iTunesDB_Private, 1);
1483 itdb->device = itdb_device_new ();
1484 itdb->version = 0x13;
1485 itdb->id = ((guint64)g_random_int () << 32) |
1486 ((guint64)g_random_int ());
1487 itdb->priv->pid = ((guint64)g_random_int () << 32) |
1488 ((guint64)g_random_int ());
1489 itdb->priv->lang = 0x656e;
1490 itdb->priv->platform = 1; /* Mac */
1491 return itdb;
1492 }
1493
1494 /* Returns the type of the mhod and the length *ml. *ml is set to -1
1495 * on error (e.g. because there's no mhod at @seek). */
1496 /* A return value of -1 and no error set means that no mhod was found
1497 at @seek */
get_mhod_type(FContents * cts,glong seek,guint32 * ml)1498 static gint32 get_mhod_type (FContents *cts, glong seek, guint32 *ml)
1499 {
1500 gint32 type = -1;
1501
1502 #if ITUNESDB_DEBUG
1503 fprintf(stderr, "get_mhod_type seek: %x\n", (int)seek);
1504 #endif
1505
1506 if (ml) *ml = -1;
1507
1508 if (check_header_seek (cts, "mhod", seek))
1509 {
1510 guint32 len = get32lint (cts, seek+8); /* total length */
1511 if (cts->error) return -1;
1512 if (ml) *ml = len;
1513 type = get32lint (cts, seek+12); /* mhod_id */
1514 if (cts->error) return -1;
1515 }
1516 return type;
1517 }
1518
extract_mhod_string(FContents * cts,glong seek)1519 static char *extract_mhod_string (FContents *cts, glong seek)
1520 {
1521 gunichar2 *entry_utf16;
1522 char *entry_utf8;
1523 gint string_type;
1524 gsize len;
1525
1526 /* type of string: 0x02: UTF8, 0x01 or 0x00: UTF16 LE */
1527 string_type = get32lint (cts, seek);
1528 len = get32lint (cts, seek+4); /* length of string */
1529 g_return_val_if_fail (len < G_MAXUINT - 2, NULL);
1530 if (string_type != 0x02) {
1531 /* UTF-16 string */
1532 entry_utf16 = g_new0 (gunichar2, (len+2)/2);
1533 if (seek_get_n_bytes (cts, (gchar *)entry_utf16, seek+16, len)) {
1534 fixup_little_utf16 (entry_utf16);
1535 entry_utf8 = utf16_to_utf8_with_partial (entry_utf16);
1536 g_free (entry_utf16);
1537 } else {
1538 g_free (entry_utf16);
1539 return NULL;
1540 }
1541 } else {
1542 /* UTF-8 string */
1543 entry_utf8 = g_new0 (gchar, len+1);
1544 if (!seek_get_n_bytes (cts, entry_utf8, seek+16, len)) {
1545 g_free (entry_utf8);
1546 return NULL;
1547 }
1548 }
1549
1550 if ((entry_utf8 != NULL) && g_utf8_validate (entry_utf8, -1, NULL)) {
1551 return entry_utf8;
1552 } else {
1553 g_free (entry_utf8);
1554 return NULL;
1555 }
1556 }
1557
1558 /* Returns the contents of the mhod at position @mhod_seek. This can
1559 be a simple string or something more complicated as in the case for
1560 Itdb_SPLPREF OR Itdb_SPLRULES.
1561
1562 *mhod_len is set to the total length of the mhod (-1 in case an
1563 *error occured).
1564
1565 MHODData.valid is set to FALSE in case of any error. cts->error
1566 will be set accordingly.
1567
1568 MHODData.type is set to the type of the mhod. The data (or a
1569 pointer to the data) will be stored in
1570 .playlist_id/.string/.chapterdata/.splp/.splrs
1571 */
1572
get_mhod(FImport * fimp,glong mhod_seek,guint32 * ml)1573 static MHODData get_mhod (FImport *fimp, glong mhod_seek, guint32 *ml)
1574 {
1575 MHODData result;
1576 gint32 xl;
1577 guint32 mhod_len;
1578 gint32 header_length;
1579 gulong seek;
1580 FContents *cts;
1581
1582 cts = fimp->fcontents;
1583
1584 result.valid = FALSE;
1585 result.type = -1;
1586 g_return_val_if_fail (ml, result);
1587 *ml = -1;
1588
1589 g_return_val_if_fail (cts, result);
1590 g_return_val_if_fail (!cts->error, result);
1591
1592 #if ITUNESDB_DEBUG
1593 fprintf(stderr, "get_mhod seek: %ld\n", mhod_seek);
1594 #endif
1595
1596 result.type = get_mhod_type (cts, mhod_seek, &mhod_len);
1597
1598 if (mhod_len == -1)
1599 {
1600 if (!cts->error)
1601 { /* set error */
1602 g_set_error (&cts->error,
1603 ITDB_FILE_ERROR,
1604 ITDB_FILE_ERROR_CORRUPT,
1605 _("iTunesDB corrupt: no MHOD at offset %ld in file '%s'."),
1606 mhod_seek, cts->filename);
1607 }
1608 return result;
1609 }
1610
1611 if (!check_seek (cts, mhod_seek, mhod_len))
1612 return result;
1613
1614
1615 header_length = get32lint (cts, mhod_seek+4); /* header length */
1616
1617 seek = mhod_seek + header_length;
1618
1619 #if ITUNESDB_DEBUG
1620 fprintf(stderr, "ml: %x type: %x\n", *ml, result.type);
1621 #endif
1622
1623 switch ((enum MHOD_ID)result.type)
1624 {
1625 case MHOD_ID_LIBPLAYLISTINDEX:
1626 /* this is not yet supported */
1627 case MHOD_ID_PLAYLIST:
1628 /* return the position indicator */
1629 result.data.track_pos = get32lint (cts, mhod_seek+24);
1630 break;
1631 case MHOD_ID_TITLE:
1632 case MHOD_ID_PATH:
1633 case MHOD_ID_ALBUM:
1634 case MHOD_ID_ARTIST:
1635 case MHOD_ID_GENRE:
1636 case MHOD_ID_FILETYPE:
1637 case MHOD_ID_COMMENT:
1638 case MHOD_ID_CATEGORY:
1639 case MHOD_ID_COMPOSER:
1640 case MHOD_ID_GROUPING:
1641 case MHOD_ID_DESCRIPTION:
1642 case MHOD_ID_SUBTITLE:
1643 case MHOD_ID_TVSHOW:
1644 case MHOD_ID_TVEPISODE:
1645 case MHOD_ID_TVNETWORK:
1646 case MHOD_ID_ALBUMARTIST:
1647 case MHOD_ID_KEYWORDS:
1648 case MHOD_ID_SORT_ARTIST:
1649 case MHOD_ID_SORT_TITLE:
1650 case MHOD_ID_SORT_ALBUM:
1651 case MHOD_ID_SORT_ALBUMARTIST:
1652 case MHOD_ID_SORT_COMPOSER:
1653 case MHOD_ID_SORT_TVSHOW:
1654 result.data.string = extract_mhod_string (cts, seek);
1655 if (result.data.string == NULL) {
1656 *ml = mhod_len;
1657 return result;
1658 }
1659 break;
1660 case MHOD_ID_PODCASTURL:
1661 case MHOD_ID_PODCASTRSS:
1662 /* length of string */
1663 xl = mhod_len - header_length;
1664 g_return_val_if_fail (xl < G_MAXUINT - 1, result);
1665 result.data.string = g_new0 (gchar, xl+1);
1666 if (!seek_get_n_bytes (cts, result.data.string, seek, xl))
1667 {
1668 g_free (result.data.string);
1669 return result; /* *ml==-1, result.valid==FALSE */
1670 }
1671 break;
1672 case MHOD_ID_CHAPTERDATA:
1673 result.data.chapterdata = itdb_chapterdata_new();
1674 result.data.chapterdata->unk024 = get32lint (cts, seek);
1675 result.data.chapterdata->unk028 = get32lint (cts, seek+4);
1676 result.data.chapterdata->unk032 = get32lint (cts, seek+8);
1677 seek += 12; /* get past unks */
1678 if (check_header_seek (cts, "sean", seek+4))
1679 {
1680 gint i;
1681 guint32 numchapters;
1682 numchapters = get32bint (cts, seek+12) - 1; /* minus 1 for hedr atom */
1683 seek += 20; /* move to atom data */
1684 for (i=0; i<numchapters; ++i)
1685 {
1686 if (check_header_seek (cts, "chap", seek+4))
1687 {
1688 guint32 length;
1689 guint32 childlength;
1690 guint32 startpos;
1691 guint32 children;
1692 gint j;
1693 gunichar2 *string_utf16;
1694 startpos = get32bint (cts, seek+8);
1695 children = get32bint (cts, seek+12);
1696 seek += 20;
1697 for (j=0; j<children; ++j)
1698 {
1699 childlength = get32bint (cts, seek);
1700 if (check_header_seek (cts, "name", seek+4))
1701 {
1702 length = get16bint (cts, seek+20);
1703 string_utf16 = g_new0 (gunichar2, (length+1));
1704 if (!seek_get_n_bytes (cts, (gchar *)string_utf16, seek+22, length*2))
1705 {
1706 g_free (string_utf16);
1707 itdb_chapterdata_free (result.data.chapterdata);
1708 return result; /* *ml==-1, result.valid==FALSE */
1709 }
1710 fixup_big_utf16 (string_utf16);
1711 itdb_chapterdata_add_chapter(result.data.chapterdata,startpos,g_utf16_to_utf8 (
1712 string_utf16, -1, NULL, NULL, NULL));
1713 g_free (string_utf16);
1714 }
1715 seek += childlength;
1716 }
1717 }
1718 }
1719 if (check_header_seek (cts, "hedr", seek+4))
1720 {
1721 guint32 hedrlength = get32bint(cts, seek);
1722 seek += hedrlength;
1723 }
1724
1725 }
1726 break;
1727 case MHOD_ID_SPLPREF: /* Settings for smart playlist */
1728 if (!check_seek (cts, seek, 14))
1729 return result; /* *ml==-1, result.valid==FALSE */
1730 result.data.splpref = g_new0 (Itdb_SPLPref, 1);
1731 result.data.splpref->liveupdate = get8int (cts, seek);
1732 result.data.splpref->checkrules = get8int (cts, seek+1);
1733 result.data.splpref->checklimits = get8int (cts, seek+2);
1734 result.data.splpref->limittype = get8int (cts, seek+3);
1735 result.data.splpref->limitsort = get8int (cts, seek+4);
1736 result.data.splpref->limitvalue = get32lint (cts, seek+8);
1737 result.data.splpref->matchcheckedonly = get8int (cts, seek+12);
1738 /* if the opposite flag is on (seek+13), set limitsort's high
1739 bit -- see note in itunesdb.h for more info */
1740 if (get8int (cts, seek+13))
1741 result.data.splpref->limitsort |= 0x80000000;
1742 break;
1743 case MHOD_ID_SPLRULES: /* Rules for smart playlist */
1744 if (check_header_seek (cts, "SLst", seek))
1745 {
1746 /* !!! for some reason the SLst part is the only part of the
1747 iTunesDB with big-endian encoding, including UTF16
1748 strings */
1749 gint i;
1750 guint32 numrules;
1751 if (!check_seek (cts, seek, 136))
1752 return result; /* *ml==-1, result.valid==FALSE */
1753 result.data.splrules = g_new0 (Itdb_SPLRules, 1);
1754 result.data.splrules->unk004 = get32bint (cts, seek+4);
1755 numrules = get32bint (cts, seek+8);
1756 result.data.splrules->match_operator = get32bint (cts, seek+12);
1757 seek += 136; /* I can't find this value stored in the
1758 iTunesDB :-( */
1759 for (i=0; i<numrules; ++i)
1760 {
1761 guint32 length;
1762 ItdbSPLFieldType ft;
1763 gunichar2 *string_utf16;
1764 Itdb_SPLRule *splr = g_new0 (Itdb_SPLRule, 1);
1765 result.data.splrules->rules = g_list_append (
1766 result.data.splrules->rules, splr);
1767 if (!check_seek (cts, seek, 56))
1768 goto splrules_error;
1769 splr->field = get32bint (cts, seek);
1770 splr->action = get32bint (cts, seek+4);
1771
1772 if (!itdb_spl_action_known (splr->action))
1773 {
1774 g_warning (_("Unknown smart rule action at %ld: %x. Trying to continue.\n"), seek, splr->action);
1775 }
1776
1777 seek += 52;
1778 length = get32bint (cts, seek);
1779 g_return_val_if_fail (length < G_MAXUINT-2, result);
1780
1781 ft = itdb_splr_get_field_type (splr);
1782 switch (ft)
1783 {
1784 case ITDB_SPLFT_STRING:
1785 string_utf16 = g_new0 (gunichar2, (length+2)/2);
1786 if (!seek_get_n_bytes (cts, (gchar *)string_utf16,
1787 seek+4, length))
1788 {
1789 g_free (string_utf16);
1790 goto splrules_error;
1791 }
1792 fixup_big_utf16 (string_utf16);
1793 splr->string = g_utf16_to_utf8 (
1794 string_utf16, -1, NULL, NULL, NULL);
1795 g_free (string_utf16);
1796 break;
1797 case ITDB_SPLFT_INT:
1798 case ITDB_SPLFT_DATE:
1799 case ITDB_SPLFT_BOOLEAN:
1800 case ITDB_SPLFT_PLAYLIST:
1801 case ITDB_SPLFT_UNKNOWN:
1802 case ITDB_SPLFT_BINARY_AND:
1803 if (length != 0x44)
1804 {
1805 g_warning (_("Length of smart playlist rule field (%d) not as expected. Trying to continue anyhow.\n"), length);
1806 }
1807 if (!check_seek (cts, seek, 72))
1808 goto splrules_error;
1809 splr->fromvalue = get64bint (cts, seek+4);
1810 splr->fromdate = get64bint (cts, seek+12);
1811 splr->fromunits = get64bint (cts, seek+20);
1812 splr->tovalue = get64bint (cts, seek+28);
1813 splr->todate = get64bint (cts, seek+36);
1814 splr->tounits = get64bint (cts, seek+44);
1815 /* ITDB_SPLFIELD_PLAYLIST seems to use these unknowns*/
1816 splr->unk052 = get32bint (cts, seek+52);
1817 splr->unk056 = get32bint (cts, seek+56);
1818 splr->unk060 = get32bint (cts, seek+60);
1819 splr->unk064 = get32bint (cts, seek+64);
1820 splr->unk068 = get32bint (cts, seek+68);
1821
1822 if (ft == ITDB_SPLFT_DATE) {
1823 ItdbSPLActionType at;
1824 at = itdb_splr_get_action_type (splr);
1825 if ((at == ITDB_SPLAT_RANGE_DATE) ||
1826 (at == ITDB_SPLAT_DATE))
1827 {
1828 Itdb_iTunesDB *itdb = fimp->itdb;
1829 splr->fromvalue = device_time_mac_to_time_t (itdb->device,
1830 splr->fromvalue);
1831 splr->tovalue = device_time_mac_to_time_t (itdb->device,
1832 splr->tovalue);
1833 }
1834 }
1835
1836 break;
1837 }
1838 seek += length+4;
1839 }
1840 }
1841 else
1842 {
1843 if (!cts->error)
1844 { /* set error */
1845 g_set_error (&cts->error,
1846 ITDB_FILE_ERROR,
1847 ITDB_FILE_ERROR_CORRUPT,
1848 _("iTunesDB corrupt: no SLst at offset %ld in file '%s'."),
1849 seek, cts->filename);
1850 }
1851 return result; /* *ml==-1, result.valid==FALSE */
1852 }
1853 break;
1854 splrules_error:
1855 g_list_foreach (result.data.splrules->rules,
1856 (GFunc)(itdb_splr_free), NULL);
1857 g_list_free (result.data.splrules->rules);
1858 g_free (result.data.splrules);
1859 return result; /* *ml==-1, result.valid==FALSE */
1860 default:
1861 g_warning (_("Encountered unknown MHOD type (%d) while parsing the iTunesDB. Ignoring.\n\n"), result.type);
1862 *ml = mhod_len;
1863 return result;
1864 }
1865
1866 *ml = mhod_len;
1867 result.valid = TRUE;
1868 return result;
1869 }
1870
1871 /* Returns the value of a string type mhod. return the length of the
1872 mhod *ml, the mhod type *mty, and a string with the entry (in
1873 UTF8). After use you must free the string with g_free(). Returns
1874 NULL if no string is avaible. *ml is set to -1 in case of error and
1875 cts->error is set appropriately. */
get_mhod_string(FImport * fimp,glong seek,guint32 * ml,gint32 * mty)1876 static gchar *get_mhod_string (FImport *fimp, glong seek, guint32 *ml, gint32 *mty)
1877 {
1878 MHODData mhoddata;
1879 FContents *cts;
1880
1881 cts = fimp->fcontents;
1882
1883 *mty = get_mhod_type (cts, seek, ml);
1884 if (cts->error) return NULL;
1885
1886 if (*ml != -1) switch ((enum MHOD_ID)*mty)
1887 {
1888 case MHOD_ID_TITLE:
1889 case MHOD_ID_PATH:
1890 case MHOD_ID_ALBUM:
1891 case MHOD_ID_ARTIST:
1892 case MHOD_ID_GENRE:
1893 case MHOD_ID_FILETYPE:
1894 case MHOD_ID_COMMENT:
1895 case MHOD_ID_CATEGORY:
1896 case MHOD_ID_COMPOSER:
1897 case MHOD_ID_GROUPING:
1898 case MHOD_ID_DESCRIPTION:
1899 case MHOD_ID_PODCASTURL:
1900 case MHOD_ID_PODCASTRSS:
1901 case MHOD_ID_SUBTITLE:
1902 case MHOD_ID_TVSHOW:
1903 case MHOD_ID_TVEPISODE:
1904 case MHOD_ID_TVNETWORK:
1905 case MHOD_ID_ALBUMARTIST:
1906 case MHOD_ID_KEYWORDS:
1907 case MHOD_ID_SORT_ARTIST:
1908 case MHOD_ID_SORT_TITLE:
1909 case MHOD_ID_SORT_ALBUM:
1910 case MHOD_ID_SORT_ALBUMARTIST:
1911 case MHOD_ID_SORT_COMPOSER:
1912 case MHOD_ID_SORT_TVSHOW:
1913 case MHOD_ID_ALBUM_ALBUM:
1914 case MHOD_ID_ALBUM_ARTIST:
1915 case MHOD_ID_ALBUM_ARTIST_MHII:
1916 case MHOD_ID_ALBUM_SORT_ARTIST:
1917 mhoddata = get_mhod (fimp, seek, ml);
1918 if ((*ml != -1) && mhoddata.valid)
1919 return mhoddata.data.string;
1920 else
1921 return NULL;
1922 case MHOD_ID_SPLPREF:
1923 case MHOD_ID_SPLRULES:
1924 case MHOD_ID_LIBPLAYLISTINDEX:
1925 case MHOD_ID_PLAYLIST:
1926 case MHOD_ID_CHAPTERDATA:
1927 case MHOD_ID_LIBPLAYLISTJUMPTABLE:
1928 /* these do not have a string entry */
1929 return NULL;
1930 }
1931 #if ITUNESDB_MHIT_DEBUG
1932 fprintf (stderr, "Ignoring unknown MHOD of type %d at offset %ld\n", *mty, seek);
1933 #endif
1934 return NULL;
1935 }
1936
1937
1938 /* convenience function: set error for zero length hunk */
set_error_zero_length_hunk(GError ** error,glong seek,const gchar * filename)1939 static void set_error_zero_length_hunk (GError **error, glong seek,
1940 const gchar *filename)
1941 {
1942 g_set_error (error,
1943 ITDB_FILE_ERROR,
1944 ITDB_FILE_ERROR_CORRUPT,
1945 _("iTunesDB corrupt: hunk length 0 for hunk at %ld in file '%s'."),
1946 seek, filename);
1947 }
1948
1949 /* convenience function: set error for missing hunk */
set_error_a_not_found_in_b(GError ** error,const gchar * a,const gchar * b,glong b_seek)1950 static void set_error_a_not_found_in_b (GError **error,
1951 const gchar *a,
1952 const gchar *b,
1953 glong b_seek)
1954 {
1955 g_set_error (error,
1956 ITDB_FILE_ERROR,
1957 ITDB_FILE_ERROR_CORRUPT,
1958 _("iTunesDB corrupt: no section '%s' found in section '%s' starting at %ld."),
1959 a, b, b_seek);
1960 }
1961
1962 /* convenience function: set error if header is smaller than expected */
set_error_a_header_smaller_than_b(GError ** error,const gchar * a,guint32 b,guint32 len,glong a_seek,const gchar * filename)1963 static void set_error_a_header_smaller_than_b (GError **error,
1964 const gchar *a,
1965 guint32 b, guint32 len,
1966 glong a_seek,
1967 const gchar *filename)
1968 {
1969 g_set_error (error,
1970 ITDB_FILE_ERROR,
1971 ITDB_FILE_ERROR_CORRUPT,
1972 _("header length of '%s' smaller than expected (%d < %d) at offset %ld in file '%s'."),
1973 a, b, len, a_seek, filename);
1974 }
1975
1976
1977 /* finds next occurence of section @a in section b (@b_seek) starting
1978 at @start_seek
1979 */
1980 /* Return value:
1981 -1 and cts->error not set: section @a could not be found
1982 -1 and cts->error set: some error occured
1983 >=0: start of next occurence of section @a
1984 */
find_next_a_in_b(FContents * cts,const gchar * a,glong b_seek,glong start_seek)1985 static glong find_next_a_in_b (FContents *cts,
1986 const gchar *a,
1987 glong b_seek, glong start_seek)
1988 {
1989 glong b_len;
1990 glong offset, len;
1991
1992 g_return_val_if_fail (a, -1);
1993 g_return_val_if_fail (cts, -1);
1994 g_return_val_if_fail (strlen (a) == 4, -1);
1995 g_return_val_if_fail (b_seek>=0, -1);
1996 g_return_val_if_fail (start_seek >= b_seek, -1);
1997
1998 /* printf ("%s: b_seek: %lx, start_seek: %lx\n", a, b_seek, start_seek); */
1999
2000 b_len = get32lint (cts, b_seek+8);
2001 if (cts->error) return -1;
2002
2003 offset = start_seek - b_seek;
2004 do
2005 { /* skip headers inside the b hunk (b_len) until we find header
2006 @a */
2007 len = get32lint (cts, b_seek+offset+4);
2008 if (cts->error) return -1;
2009 if (len == 0)
2010 { /* This needs to be checked, otherwise we might hang */
2011 set_error_zero_length_hunk (&cts->error, b_seek+offset,
2012 cts->filename);
2013 return -1;
2014 }
2015 offset += len;
2016 /* printf ("offset: %lx, b_len: %lx, bseek+offset: %lx\n", */
2017 /* offset, b_len, b_seek+offset); */
2018 } while ((offset < b_len-4) &&
2019 !check_header_seek (cts, a, b_seek+offset));
2020 if (cts->error) return -1;
2021
2022 if (offset >= b_len) return -1;
2023
2024 /* printf ("%s found at %lx\n", a, b_seek+offset); */
2025
2026 return b_seek+offset;
2027 }
2028
2029
2030
2031
2032 /* return the position of mhsd with type @type */
2033 /* Return value:
2034 -1 if mhsd cannot be found. cts->error will not be set
2035
2036 0 and cts->error is set if some other error occurs.
2037 Since the mhsd can never be at position 0 (a mhbd must be there),
2038 a return value of 0 always indicates an error.
2039 */
find_mhsd(FContents * cts,guint32 type)2040 static glong find_mhsd (FContents *cts, guint32 type)
2041 {
2042 guint32 i, len, mhsd_num;
2043 glong seek;
2044
2045 len = get32lint (cts, 4);
2046 if (cts->error) return 0;
2047 /* all the headers I know are 0x68 long -- if this one is longer
2048 we can could simply ignore the additional information */
2049 /* Since 'we' (parse_fimp()) only need data from the first 32
2050 bytes, don't complain unless it's smaller than that */
2051 if (len < 32)
2052 {
2053 g_set_error (&cts->error,
2054 ITDB_FILE_ERROR,
2055 ITDB_FILE_ERROR_CORRUPT,
2056 _("iTunesDB ('%s'): header length of mhsd hunk smaller than expected (%d<32). Aborting."),
2057 cts->filename, len);
2058 return FALSE;
2059 }
2060
2061 mhsd_num = get32lint (cts, 20);
2062 if (cts->error) return 0;
2063
2064 seek = 0;
2065 for (i=0; i<mhsd_num; ++i)
2066 {
2067 guint32 mhsd_type;
2068
2069 seek += len;
2070 if (!check_header_seek (cts, "mhsd", seek))
2071 {
2072 if (!cts->error)
2073 { /* set error */
2074 g_set_error (&cts->error,
2075 ITDB_FILE_ERROR,
2076 ITDB_FILE_ERROR_CORRUPT,
2077 _("iTunesDB '%s' corrupt: mhsd expected at %ld."),
2078 cts->filename, seek);
2079 }
2080 return 0;
2081 }
2082 len = get32lint (cts, seek+8);
2083 if (cts->error) return 0;
2084
2085 mhsd_type = get32lint (cts, seek+12);
2086 if (cts->error) return 0;
2087
2088 if (mhsd_type == type) return seek;
2089 }
2090 return -1;
2091 }
2092
2093
2094 /* sort in reverse order */
pos_comp(gconstpointer a,gconstpointer b)2095 static gint pos_comp (gconstpointer a, gconstpointer b)
2096 {
2097 const PosEntry *pa = (const PosEntry*)a;
2098 const PosEntry *pb = (const PosEntry*)b;
2099
2100 return pb->track_pos - pa->track_pos;
2101 }
2102
2103
2104 /* Read and process the mhip at @seek. Return a pointer to the next
2105 possible mhip. */
2106 /* Return value: -1 if no mhip is present at @seek */
get_mhip(FImport * fimp,glong mhip_seek)2107 static glong get_mhip (FImport *fimp, glong mhip_seek)
2108 {
2109 gboolean first_entry = TRUE;
2110 FContents *cts;
2111 guint32 mhip_hlen, mhip_len, mhod_num, mhod_seek;
2112 gint32 i;
2113 gint32 mhod_type;
2114 guint32 trackid;
2115
2116
2117 g_return_val_if_fail (fimp, -1);
2118
2119 cts = fimp->fcontents;
2120
2121 if (!check_header_seek (cts, "mhip", mhip_seek))
2122 {
2123 CHECK_ERROR (fimp, -1);
2124 return -1;
2125 }
2126
2127 mhip_hlen = get32lint (cts, mhip_seek+4);
2128 CHECK_ERROR (fimp, -1);
2129
2130 if (mhip_hlen < 36)
2131 {
2132 set_error_a_header_smaller_than_b (&fimp->error,
2133 "mhip",
2134 mhip_hlen, 36,
2135 mhip_seek, cts->filename);
2136 return -1;
2137 }
2138
2139 /* Check if entire mhip header can be read -- that way we won't
2140 have to check for read errors every time we access a single
2141 byte */
2142
2143 check_seek (cts, mhip_seek, mhip_hlen);
2144 CHECK_ERROR (fimp, -1);
2145
2146 mhip_len = get32lint (cts, mhip_seek+8);
2147 mhod_num = get32lint (cts, mhip_seek+12);
2148 trackid = get32lint(cts, mhip_seek+24);
2149
2150 mhod_seek = mhip_seek + mhip_hlen;
2151
2152 /* the mhod that follows gives us the position in the
2153 playlist (type 100). Just for flexibility, we scan all
2154 following mhods and pick the type 100 */
2155 for (i=0; i<mhod_num; ++i)
2156 {
2157 guint32 mhod_len;
2158
2159 mhod_type = get_mhod_type (cts, mhod_seek, &mhod_len);
2160 CHECK_ERROR (fimp, -1);
2161 if (mhod_type == MHOD_ID_PLAYLIST)
2162 {
2163 MHODData mhod;
2164 mhod = get_mhod (fimp, mhod_seek, &mhod_len);
2165 CHECK_ERROR (fimp, -1);
2166 if (mhod.valid && first_entry)
2167 {
2168 PosEntry *entry = g_new(PosEntry, 1);
2169 entry->trackid = trackid;
2170 entry->track_pos = mhod.data.track_pos;
2171 fimp->pos_glist = g_list_prepend (fimp->pos_glist, entry);
2172 /* don't call this section more than once (it never
2173 should happen except in the case of corrupted
2174 iTunesDBs...) */
2175 first_entry = FALSE;
2176 }
2177 }
2178 else
2179 {
2180 if (mhod_len == -1)
2181 {
2182 g_warning (_("Number of MHODs in mhip at %ld inconsistent in file '%s'."),
2183 mhip_seek, cts->filename);
2184 break;
2185 }
2186 }
2187 mhod_seek += mhod_len;
2188 }
2189
2190 /* Up to iTunesd V4.7 or so the mhip_len was set incorrectly
2191 (mhip_len == mhip_hlen). In that case we need to find the seek
2192 to the next mhip by going through all mhods.
2193 */
2194 if ((mhip_len == mhip_hlen) && (mhod_num > 0))
2195 return mhod_seek;
2196 else
2197 return mhip_seek+mhip_len;
2198 }
2199
2200
2201
2202
2203 /* Get a playlist. Returns the position where the next playlist should
2204 be. On error -1 is returned and fimp->error is set
2205 appropriately. */
2206 /* get_mhyp */
get_playlist(FImport * fimp,guint mhsd_type,glong mhyp_seek)2207 static glong get_playlist (FImport *fimp, guint mhsd_type, glong mhyp_seek)
2208 {
2209 guint32 i, mhipnum, mhod_num;
2210 glong nextseek, mhod_seek, mhip_seek;
2211 guint32 header_len;
2212 Itdb_Playlist *plitem = NULL;
2213 FContents *cts;
2214 GList *gl;
2215
2216 #if ITUNESDB_DEBUG
2217 fprintf(stderr, "mhyp seek: %x\n", (int)mhyp_seek);
2218 #endif
2219 g_return_val_if_fail (fimp, -1);
2220 g_return_val_if_fail (fimp->idtree, -1);
2221 g_return_val_if_fail (fimp->pos_glist == NULL, -1);
2222
2223 cts = fimp->fcontents;
2224
2225 if (!check_header_seek (cts, "mhyp", mhyp_seek))
2226 {
2227 if (cts->error)
2228 g_propagate_error (&fimp->error, cts->error);
2229 return -1;
2230 }
2231 header_len = get32lint (cts, mhyp_seek+4); /* length of header */
2232 CHECK_ERROR (fimp, -1);
2233
2234 if (header_len < 48)
2235 {
2236 set_error_a_header_smaller_than_b (&fimp->error,
2237 "mhyp",
2238 header_len, 48,
2239 mhyp_seek, cts->filename);
2240 return -1;
2241 }
2242
2243 /* Check if entire mhyp can be read -- that way we won't have to
2244 * check for read errors every time we access a single byte */
2245
2246 check_seek (cts, mhyp_seek, header_len);
2247 CHECK_ERROR (fimp, -1);
2248
2249 nextseek = mhyp_seek + get32lint (cts, mhyp_seek+8);/* possible begin of next PL */
2250 mhod_num = get32lint (cts, mhyp_seek+12); /* number of MHODs we expect */
2251 mhipnum = get32lint (cts, mhyp_seek+16); /* number of tracks
2252 (mhips) in playlist */
2253
2254 plitem = itdb_playlist_new (NULL, FALSE);
2255
2256 plitem->num = mhipnum;
2257 /* Some Playlists have added 256 to their type -- I don't know what
2258 it's for, so we just ignore it for now -> & 0xff */
2259 plitem->type = get8int (cts, mhyp_seek+20);
2260 plitem->flag1 = get8int (cts, mhyp_seek+21);
2261 plitem->flag2 = get8int (cts, mhyp_seek+22);
2262 plitem->flag3 = get8int (cts, mhyp_seek+23);
2263 plitem->timestamp = get32lint (cts, mhyp_seek+24);
2264 plitem->timestamp = device_time_mac_to_time_t (fimp->itdb->device, plitem->timestamp);
2265 plitem->id = get64lint (cts, mhyp_seek+28);
2266 /* plitem->mhodcount = get32lint (cts, mhyp_seek+36); */
2267 /* plitem->libmhodcount = get16lint (cts, mhyp_seek+40);*/
2268 plitem->podcastflag = get16lint (cts, mhyp_seek+42);
2269 plitem->sortorder = get32lint (cts, mhyp_seek+44);
2270 if (header_len >= 0x6C) {
2271 plitem->priv->mhsd5_type = get16lint (cts, mhyp_seek+0x50);
2272 }
2273
2274 mhod_seek = mhyp_seek + header_len;
2275
2276 for (i=0; i < mhod_num; ++i)
2277 {
2278 gint32 type;
2279 MHODData mhod;
2280
2281 type = get_mhod_type (cts, mhod_seek, &header_len);
2282 CHECK_ERROR (fimp, -1);
2283 if (header_len != -1)
2284 {
2285 switch ((enum MHOD_ID)type)
2286 {
2287 case MHOD_ID_PLAYLIST:
2288 /* here we could do something about the playlist settings */
2289 break;
2290 case MHOD_ID_TITLE:
2291 mhod = get_mhod (fimp, mhod_seek, &header_len);
2292 CHECK_ERROR (fimp, -1);
2293 if (mhod.valid && mhod.data.string)
2294 {
2295 /* sometimes there seem to be two mhod TITLE headers */
2296 g_free (plitem->name);
2297 plitem->name = mhod.data.string;
2298 mhod.valid = FALSE;
2299 }
2300 break;
2301 case MHOD_ID_SPLPREF:
2302 mhod = get_mhod (fimp, mhod_seek, &header_len);
2303 CHECK_ERROR (fimp, -1);
2304 if (mhod.valid && mhod.data.splpref)
2305 {
2306 plitem->is_spl = TRUE;
2307 memcpy (&plitem->splpref, mhod.data.splpref,
2308 sizeof (Itdb_SPLPref));
2309 g_free (mhod.data.splpref);
2310 mhod.valid = FALSE;
2311 }
2312 break;
2313 case MHOD_ID_SPLRULES:
2314 mhod = get_mhod (fimp, mhod_seek, &header_len);
2315 CHECK_ERROR (fimp, -1);
2316 if (mhod.valid && mhod.data.splrules)
2317 {
2318 plitem->is_spl = TRUE;
2319 memcpy (&plitem->splrules, mhod.data.splrules,
2320 sizeof (Itdb_SPLRules));
2321 g_free (mhod.data.splrules);
2322 mhod.valid = FALSE;
2323 }
2324 break;
2325 case MHOD_ID_PATH:
2326 case MHOD_ID_ALBUM:
2327 case MHOD_ID_ARTIST:
2328 case MHOD_ID_GENRE:
2329 case MHOD_ID_FILETYPE:
2330 case MHOD_ID_COMMENT:
2331 case MHOD_ID_CATEGORY:
2332 case MHOD_ID_COMPOSER:
2333 case MHOD_ID_GROUPING:
2334 case MHOD_ID_DESCRIPTION:
2335 case MHOD_ID_PODCASTURL:
2336 case MHOD_ID_PODCASTRSS:
2337 case MHOD_ID_SUBTITLE:
2338 case MHOD_ID_TVSHOW:
2339 case MHOD_ID_TVEPISODE:
2340 case MHOD_ID_TVNETWORK:
2341 case MHOD_ID_ALBUMARTIST:
2342 case MHOD_ID_KEYWORDS:
2343 case MHOD_ID_CHAPTERDATA:
2344 case MHOD_ID_SORT_ARTIST:
2345 case MHOD_ID_SORT_TITLE:
2346 case MHOD_ID_SORT_ALBUM:
2347 case MHOD_ID_SORT_ALBUMARTIST:
2348 case MHOD_ID_SORT_COMPOSER:
2349 case MHOD_ID_SORT_TVSHOW:
2350 case MHOD_ID_ALBUM_ALBUM:
2351 case MHOD_ID_ALBUM_ARTIST:
2352 case MHOD_ID_ALBUM_ARTIST_MHII:
2353 case MHOD_ID_ALBUM_SORT_ARTIST:
2354 case MHOD_ID_LIBPLAYLISTJUMPTABLE:
2355 /* these are not expected here */
2356 break;
2357 case MHOD_ID_LIBPLAYLISTINDEX:
2358 /* this I don't know how to handle */
2359 break;
2360 }
2361 mhod_seek += header_len;
2362 }
2363 else
2364 {
2365 g_warning (_("Number of MHODs in mhyp at %ld inconsistent in file '%s'."),
2366 mhyp_seek, cts->filename);
2367 break;
2368 }
2369 }
2370
2371 if (!plitem->name)
2372 { /* we did not read a valid mhod TITLE header -> */
2373 /* we simply make up our own name */
2374 if (itdb_playlist_is_mpl (plitem))
2375 plitem->name = g_strdup (_("Master-PL"));
2376 else
2377 {
2378 if (itdb_playlist_is_podcasts (plitem))
2379 plitem->name = g_strdup (_("Podcasts"));
2380 else
2381 plitem->name = g_strdup (_("Playlist"));
2382 }
2383 }
2384
2385 #if ITUNESDB_DEBUG
2386 fprintf(stderr, "pln: %s(%d Itdb_Tracks) \n", plitem->name, (int)plitem->num);
2387 #endif
2388
2389 /* add new playlist */
2390 if (mhsd_type == 5) {
2391 itdb_playlist_add_mhsd5_playlist (fimp->itdb, plitem, -1);
2392 } else {
2393 itdb_playlist_add (fimp->itdb, plitem, -1);
2394 }
2395
2396 mhip_seek = mhod_seek;
2397
2398 /* tracks read */
2399 for (i=0; i < mhipnum; ++i)
2400 {
2401 mhip_seek = get_mhip (fimp, mhip_seek);
2402 if (mhip_seek == -1)
2403 {
2404 g_set_error (&fimp->error,
2405 ITDB_FILE_ERROR,
2406 ITDB_FILE_ERROR_CORRUPT,
2407 _("iTunesDB corrupt: number of mhip sections inconsistent in mhyp starting at %ld in file '%s'."),
2408 mhyp_seek, cts->filename);
2409 return -1;
2410 }
2411 }
2412
2413 /* sort in reverse order */
2414 fimp->pos_glist = g_list_sort (fimp->pos_glist, pos_comp);
2415 for (gl = fimp->pos_glist; gl; gl = g_list_next (gl))
2416 {
2417 PosEntry* entry = (PosEntry*)gl->data;
2418 Itdb_Track *tr = itdb_track_id_tree_by_id (fimp->idtree, entry->trackid);
2419 if (tr)
2420 {
2421 /* preprend because we sorted in reverse order */
2422 itdb_playlist_add_track (plitem, tr, 0);
2423 }
2424 else
2425 {
2426 if (plitem->podcastflag == ITDB_PL_FLAG_NORM)
2427 {
2428 g_warning (_("Itdb_Track ID '%d' not found.\n"), entry->trackid);
2429 }
2430 }
2431 g_free(entry);
2432 }
2433
2434 g_list_free (fimp->pos_glist);
2435 fimp->pos_glist = NULL;
2436 return nextseek;
2437 }
2438
2439
2440 /* returns a pointer to the next header or -1 on error. fimp->error is
2441 set appropriately. If no "mhit" header is found at the location
2442 specified, -1 is returned but no error is set. */
get_mhit(FImport * fimp,glong mhit_seek)2443 static glong get_mhit (FImport *fimp, glong mhit_seek)
2444 {
2445 Itdb_Track *track;
2446 gchar *entry_utf8;
2447 gint32 type;
2448 guint32 header_len;
2449 guint32 zip;
2450 struct playcount *playcount;
2451 guint32 i, mhod_nums;
2452 FContents *cts;
2453 glong seek = mhit_seek;
2454 gboolean free_playcount;
2455
2456 #if ITUNESDB_DEBUG
2457 fprintf(stderr, "get_mhit seek: %x\n", (int)seek);
2458 #endif
2459
2460 g_return_val_if_fail (fimp, -1);
2461
2462 cts = fimp->fcontents;
2463
2464 if (!check_header_seek (cts, "mhit", seek))
2465 {
2466 if (cts->error)
2467 g_propagate_error (&fimp->error, cts->error);
2468 return -1;
2469 }
2470
2471 header_len = get32lint (cts, seek+4);
2472 CHECK_ERROR (fimp, -1);
2473
2474 /* size of the mhit header: For dbversion <= 0x0b (iTunes 4.7 and
2475 earlier), the length is 0x9c. As of dbversion 0x0c and 0x0d
2476 (iTunes 4.7.1 - iTunes 4.9), the size is 0xf4. */
2477 if (header_len < 0x9c)
2478 {
2479 set_error_a_header_smaller_than_b (&fimp->error,
2480 "mhit",
2481 header_len, 0x9c,
2482 seek, cts->filename);
2483 return -1;
2484 }
2485
2486 /* Check if entire mhit can be read -- that way we won't have to
2487 * check for read errors every time we access a single byte */
2488
2489 check_seek (cts, seek, header_len);
2490
2491 mhod_nums = get32lint (cts, seek+12);
2492 CHECK_ERROR (fimp, -1);
2493
2494
2495 track = itdb_track_new ();
2496
2497 if (header_len >= 0x9c)
2498 {
2499 guint32 val32;
2500 track->id = get32lint(cts, seek+16); /* iPod ID */
2501 track->visible = get32lint (cts, seek+20);
2502 track->filetype_marker = get32lint (cts, seek+24);
2503 track->type1 = get8int (cts, seek+28);
2504 track->type2 = get8int (cts, seek+29);
2505 track->compilation = get8int (cts, seek+30);
2506 track->rating = get8int (cts, seek+31);
2507 track->time_modified = get32lint(cts, seek+32); /* time added */
2508 track->time_modified = device_time_mac_to_time_t (fimp->itdb->device,
2509 track->time_modified);
2510 track->size = get32lint(cts, seek+36); /* file size */
2511 track->tracklen = get32lint(cts, seek+40); /* time */
2512 track->track_nr = get32lint(cts, seek+44); /* track number */
2513 track->tracks = get32lint(cts, seek+48); /* nr of tracks */
2514 track->year = get32lint(cts, seek+52); /* year */
2515 track->bitrate = get32lint(cts, seek+56); /* bitrate */
2516 val32 = get32lint (cts, seek+60);
2517 track->samplerate = val32 >> 16; /* sample rate */
2518 track->samplerate_low = val32 & 0xffff; /* remaining bits */
2519 track->volume = get32lint(cts, seek+64); /* volume adjust */
2520 track->starttime = get32lint (cts, seek+68);
2521 track->stoptime = get32lint (cts, seek+72);
2522 track->soundcheck = get32lint (cts, seek+76);/* soundcheck */
2523 track->playcount = get32lint (cts, seek+80); /* playcount */
2524 track->playcount2 = get32lint (cts, seek+84);
2525 track->time_played = get32lint(cts, seek+88);/* last time played */
2526 track->time_played = device_time_mac_to_time_t (fimp->itdb->device,
2527 track->time_played);
2528 track->cd_nr = get32lint(cts, seek+92); /* CD nr */
2529 track->cds = get32lint(cts, seek+96); /* CD nr of.. */
2530 /* Apple Store/Audible User ID (for DRM'ed files only, set to 0
2531 otherwise). */
2532 track->drm_userid = get32lint (cts, seek+100);
2533 track->time_added = get32lint(cts, seek+104);/* last mod. time */
2534 track->time_added = device_time_mac_to_time_t (fimp->itdb->device,
2535 track->time_added);
2536 track->bookmark_time = get32lint (cts, seek+108);/*time bookmarked*/
2537 track->dbid = get64lint (cts, seek+112);
2538 track->checked = get8int (cts, seek+120); /*Checked/Unchecked: 0/1*/
2539 /* The rating set by the application, as opposed to the rating
2540 set on the iPod itself */
2541 track->app_rating = get8int (cts, seek+121);
2542 track->BPM = get16lint (cts, seek+122);
2543 track->artwork_count = get16lint (cts, seek+124);
2544 track->unk126 = get16lint (cts, seek+126);
2545 track->artwork_size = get32lint (cts, seek+128);
2546 track->unk132 = get32lint (cts, seek+132);
2547 track->samplerate2 = get32lfloat (cts, seek+136);
2548 track->time_released = get32lint (cts, seek+140);
2549 track->time_released = device_time_mac_to_time_t (fimp->itdb->device,
2550 track->time_released);
2551 track->unk144 = get16lint (cts, seek+144);
2552 track->explicit_flag = get16lint (cts, seek+146);
2553 track->unk148 = get32lint (cts, seek+148);
2554 track->unk152 = get32lint (cts, seek+152);
2555 }
2556 if (header_len >= 0xf4)
2557 {
2558 track->skipcount = get32lint (cts, seek+156);
2559 track->last_skipped = get32lint (cts, seek+160);
2560 track->last_skipped = device_time_mac_to_time_t (fimp->itdb->device,
2561 track->last_skipped);
2562 track->has_artwork = get8int (cts, seek+164);
2563 track->skip_when_shuffling = get8int (cts, seek+165);
2564 track->remember_playback_position = get8int (cts, seek+166);
2565 track->flag4 = get8int (cts, seek+167);
2566 track->dbid2 = get64lint (cts, seek+168);
2567 track->lyrics_flag = get8int (cts, seek+176);
2568 track->movie_flag = get8int (cts, seek+177);
2569 track->mark_unplayed = get8int (cts, seek+178);
2570 track->unk179 = get8int (cts, seek+179);
2571 track->unk180 = get32lint (cts, seek+180);
2572 track->pregap = get32lint (cts, seek+184);
2573 track->samplecount = get64lint (cts, seek+188);
2574 track->unk196 = get32lint (cts, seek+196);
2575 track->postgap = get32lint (cts, seek+200);
2576 track->unk204 = get32lint (cts, seek+204);
2577 track->mediatype = get32lint (cts, seek+208);
2578 track->season_nr = get32lint (cts, seek+212);
2579 track->episode_nr = get32lint (cts, seek+216);
2580 track->unk220 = get32lint (cts, seek+220);
2581 track->unk224 = get32lint (cts, seek+224);
2582 track->unk228 = get32lint (cts, seek+228);
2583 track->unk232 = get32lint (cts, seek+232);
2584 track->unk236 = get32lint (cts, seek+236);
2585 track->unk240 = get32lint (cts, seek+240);
2586 }
2587 if (header_len >= 0x148)
2588 {
2589 track->unk244 = get32lint (cts, seek+244);
2590 track->gapless_data = get32lint (cts, seek+248);
2591 track->unk252 = get32lint (cts, seek+252);
2592 track->gapless_track_flag = get16lint (cts, seek+256);
2593 track->gapless_album_flag = get16lint (cts, seek+258);
2594 }
2595 if (header_len >= 0x184)
2596 {
2597 track->mhii_link = get32lint (cts, seek+352);
2598 }
2599
2600 track->transferred = TRUE; /* track is on iPod! */
2601
2602 seek += get32lint (cts, seek+4); /* 1st mhod starts here! */
2603 CHECK_ERROR (fimp, -1);
2604
2605 for (i=0; i<mhod_nums; ++i)
2606 {
2607 entry_utf8 = get_mhod_string (fimp, seek, &zip, &type);
2608 CHECK_ERROR (fimp, -1);
2609 if (entry_utf8 != NULL)
2610 {
2611 switch ((enum MHOD_ID)type)
2612 {
2613 case MHOD_ID_TITLE:
2614 track->title = entry_utf8;
2615 break;
2616 case MHOD_ID_PATH:
2617 track->ipod_path = entry_utf8;
2618 break;
2619 case MHOD_ID_ALBUM:
2620 track->album = entry_utf8;
2621 break;
2622 case MHOD_ID_ARTIST:
2623 track->artist = entry_utf8;
2624 break;
2625 case MHOD_ID_GENRE:
2626 track->genre = entry_utf8;
2627 break;
2628 case MHOD_ID_FILETYPE:
2629 track->filetype = entry_utf8;
2630 break;
2631 case MHOD_ID_COMMENT:
2632 track->comment = entry_utf8;
2633 break;
2634 case MHOD_ID_CATEGORY:
2635 track->category = entry_utf8;
2636 break;
2637 case MHOD_ID_COMPOSER:
2638 track->composer = entry_utf8;
2639 break;
2640 case MHOD_ID_GROUPING:
2641 track->grouping = entry_utf8;
2642 break;
2643 case MHOD_ID_DESCRIPTION:
2644 track->description = entry_utf8;
2645 break;
2646 case MHOD_ID_PODCASTURL:
2647 track->podcasturl = entry_utf8;
2648 break;
2649 case MHOD_ID_PODCASTRSS:
2650 track->podcastrss = entry_utf8;
2651 break;
2652 case MHOD_ID_SUBTITLE:
2653 track->subtitle = entry_utf8;
2654 break;
2655 case MHOD_ID_TVSHOW:
2656 track->tvshow = entry_utf8;
2657 break;
2658 case MHOD_ID_TVEPISODE:
2659 track->tvepisode = entry_utf8;
2660 break;
2661 case MHOD_ID_TVNETWORK:
2662 track->tvnetwork = entry_utf8;
2663 break;
2664 case MHOD_ID_ALBUMARTIST:
2665 track->albumartist = entry_utf8;
2666 break;
2667 case MHOD_ID_KEYWORDS:
2668 track->keywords = entry_utf8;
2669 break;
2670 case MHOD_ID_SORT_ARTIST:
2671 track->sort_artist = entry_utf8;
2672 break;
2673 case MHOD_ID_SORT_TITLE:
2674 track->sort_title = entry_utf8;
2675 break;
2676 case MHOD_ID_SORT_ALBUM:
2677 track->sort_album = entry_utf8;
2678 break;
2679 case MHOD_ID_SORT_ALBUMARTIST:
2680 track->sort_albumartist = entry_utf8;
2681 break;
2682 case MHOD_ID_SORT_COMPOSER:
2683 track->sort_composer = entry_utf8;
2684 break;
2685 case MHOD_ID_SORT_TVSHOW:
2686 track->sort_tvshow = entry_utf8;
2687 break;
2688 case MHOD_ID_SPLPREF:
2689 case MHOD_ID_SPLRULES:
2690 case MHOD_ID_LIBPLAYLISTINDEX:
2691 case MHOD_ID_LIBPLAYLISTJUMPTABLE:
2692 case MHOD_ID_PLAYLIST:
2693 case MHOD_ID_CHAPTERDATA:
2694 case MHOD_ID_ALBUM_ALBUM:
2695 case MHOD_ID_ALBUM_ARTIST:
2696 case MHOD_ID_ALBUM_ARTIST_MHII:
2697 case MHOD_ID_ALBUM_SORT_ARTIST:
2698 g_free (entry_utf8);
2699 break;
2700 }
2701 }
2702 else
2703 {
2704 MHODData mhod;
2705 switch (type)
2706 {
2707 case MHOD_ID_CHAPTERDATA:
2708 mhod = get_mhod (fimp, seek, &zip);
2709 if (mhod.valid && mhod.data.chapterdata)
2710 {
2711 track->chapterdata = mhod.data.chapterdata;
2712 mhod.valid = FALSE;
2713 }
2714 break;
2715 default:
2716 /*
2717 printf ("found mhod type %d at %lx inside mhit starting at %lx\n",
2718 type, seek, mhit_seek);*/
2719 break;
2720 }
2721 }
2722 seek += zip;
2723 }
2724
2725 playcount = playcount_take_next (fimp);
2726 free_playcount = TRUE;
2727 if (!playcount && fimp->pcounts2)
2728 {
2729 playcount = g_hash_table_lookup (fimp->pcounts2, &track->dbid);
2730 free_playcount = FALSE;
2731 }
2732 if (playcount)
2733 {
2734 if (playcount->rating != NO_PLAYCOUNT)
2735 {
2736 if (track->rating != playcount->rating)
2737 {
2738 /* backup original rating to app_rating */
2739 track->app_rating = track->rating;
2740 track->rating = playcount->rating;
2741 }
2742 }
2743 if (playcount->time_played)
2744 track->time_played = playcount->time_played;
2745
2746 if (playcount->bookmark_time)
2747 track->bookmark_time = playcount->bookmark_time;
2748
2749 track->playcount += playcount->playcount;
2750 if (playcount->playcount != 0)
2751 { /* unmark the 'unplayed' flag */
2752 track->mark_unplayed = 0x01;
2753 }
2754 track->recent_playcount = playcount->playcount;
2755
2756 track->skipcount += playcount->skipcount;
2757 track->recent_skipcount = playcount->skipcount;
2758 if (free_playcount)
2759 g_free (playcount);
2760 }
2761 fimp->tracks = g_list_prepend(fimp->tracks, track);
2762 return seek;
2763 }
2764
2765
2766 /* Called by read_OTG_playlists(): OTG playlist stored in @cts by
2767 * adding a new playlist (named @plname) with the tracks specified in
2768 * @cts. If @plname is NULL, a standard name will be substituted */
2769 /* Returns FALSE on error, TRUE on success. On error @fimp->error will
2770 * be set apropriately. */
process_OTG_file(FImport * fimp,FContents * cts,const gchar * plname)2771 static gboolean process_OTG_file (FImport *fimp, FContents *cts,
2772 const gchar *plname)
2773 {
2774 guint32 header_length, entry_length, entry_num;
2775
2776 g_return_val_if_fail (fimp && cts, FALSE);
2777 g_return_val_if_fail (fimp->itdb, FALSE);
2778
2779 if (!plname) plname = _("OTG Playlist");
2780
2781 if (!check_header_seek (cts, "mhpo", 0))
2782 {
2783 if (cts->error)
2784 {
2785 g_propagate_error (&fimp->error, cts->error);
2786 return FALSE;
2787 }
2788 fcontents_set_reversed (cts, TRUE);
2789 if (!check_header_seek (cts, "mhpo", 0))
2790 {
2791 /* cts->error can't be set as already checked above */
2792 /* set error */
2793 g_return_val_if_fail (cts->filename, FALSE);
2794 g_set_error (&fimp->error,
2795 ITDB_FILE_ERROR,
2796 ITDB_FILE_ERROR_CORRUPT,
2797 _("Not a OTG playlist file: '%s' (missing mhpo header)."),
2798 cts->filename);
2799 return FALSE;
2800 }
2801 }
2802 header_length = get32lint (cts, 4);
2803 CHECK_ERROR (fimp, FALSE);
2804 /* all the headers I know are 0x14 long -- if this one is
2805 longer we can simply ignore the additional information */
2806 if (header_length < 0x14)
2807 {
2808 g_set_error (&fimp->error,
2809 ITDB_FILE_ERROR,
2810 ITDB_FILE_ERROR_CORRUPT,
2811 _("OTG playlist file ('%s'): header length smaller than expected (%d<20)."),
2812 cts->filename, header_length);
2813 return FALSE;
2814 }
2815 entry_length = get32lint (cts, 8);
2816 CHECK_ERROR (fimp, FALSE);
2817 /* all the entries I know are 0x04 long */
2818 if (entry_length < 0x04)
2819 {
2820 g_set_error (&fimp->error,
2821 ITDB_FILE_ERROR,
2822 ITDB_FILE_ERROR_CORRUPT,
2823 _("OTG playlist file ('%s'): entry length smaller than expected (%d<4)."),
2824 cts->filename, entry_length);
2825 return FALSE;
2826 }
2827 /* number of entries */
2828 entry_num = get32lint (cts, 12);
2829 CHECK_ERROR (fimp, FALSE);
2830
2831 if (entry_num > 0)
2832 {
2833 gint i;
2834 Itdb_Playlist *pl;
2835
2836 pl = itdb_playlist_new (plname, FALSE);
2837 /* Add new playlist */
2838 itdb_playlist_add (fimp->itdb, pl, -1);
2839
2840 /* Add items */
2841 for (i=0; i<entry_num; ++i)
2842 {
2843 Itdb_Track *track;
2844 guint32 num = get32lint (cts,
2845 header_length + entry_length *i);
2846 CHECK_ERROR (fimp, FALSE);
2847
2848 track = g_list_nth_data (fimp->itdb->tracks, num);
2849 if (track)
2850 {
2851 itdb_playlist_add_track (pl, track, -1);
2852 }
2853 else
2854 {
2855 g_set_error (&fimp->error,
2856 ITDB_FILE_ERROR,
2857 ITDB_FILE_ERROR_CORRUPT,
2858 _("OTG playlist file '%s': reference to non-existent track (%d)."),
2859 cts->filename, num);
2860 return FALSE;
2861 }
2862 }
2863 }
2864 return TRUE;
2865 }
2866
2867
2868
2869
2870 /* Add the On-The-Go Playlist(s) to the database */
2871 /* The OTG-Files are located in the directory given by
2872 fimp->itdb->itdb_filename.
2873 On error FALSE is returned and fimp->error is set accordingly. */
read_OTG_playlists(FImport * fimp)2874 static gboolean read_OTG_playlists (FImport *fimp)
2875 {
2876 gchar *db[] = {"OTGPlaylistInfo", NULL};
2877 gchar *dirname, *otgname;
2878 gint i=1;
2879
2880 g_return_val_if_fail (fimp, FALSE);
2881 g_return_val_if_fail (fimp->itdb, FALSE);
2882 g_return_val_if_fail (fimp->itdb->filename, FALSE);
2883
2884 dirname = g_path_get_dirname (fimp->itdb->filename);
2885 otgname = itdb_resolve_path (dirname, (const gchar **)db);
2886
2887 /* only parse if "OTGPlaylistInfo" exists */
2888 while (otgname)
2889 {
2890 FContents *cts = fcontents_read (otgname, &fimp->error);
2891 if (cts)
2892 {
2893 gchar *plname = g_strdup_printf (_("OTG Playlist %d"), i);
2894 process_OTG_file (fimp, cts, plname);
2895 g_free (plname);
2896 fcontents_free (cts);
2897 }
2898 g_free (otgname);
2899 if (fimp->error) break;
2900 db[0] = g_strdup_printf ("OTGPlaylistInfo_%d", i);
2901 otgname = itdb_resolve_path (dirname, (const gchar **)db);
2902 g_free (db[0]);
2903 ++i;
2904 }
2905 g_free (dirname);
2906 return TRUE;
2907 }
2908
2909
2910 /* Read the tracklist (mhlt). mhsd_seek must point to type 1 mhsd
2911 (this is treated as a programming error) */
2912 /* Return value:
2913 TRUE: import successful
2914 FALSE: error occured, fimp->error is set */
parse_tracks(FImport * fimp,glong mhsd_seek)2915 static gboolean parse_tracks (FImport *fimp, glong mhsd_seek)
2916 {
2917 FContents *cts;
2918 GList* gl;
2919 glong mhlt_seek, seek;
2920 guint32 nr_tracks, i;
2921
2922 g_return_val_if_fail (fimp, FALSE);
2923 g_return_val_if_fail (fimp->itdb, FALSE);
2924 g_return_val_if_fail (fimp->fcontents, FALSE);
2925 g_return_val_if_fail (fimp->fcontents->filename, FALSE);
2926 g_return_val_if_fail (mhsd_seek >= 0, FALSE);
2927
2928 cts = fimp->fcontents;
2929
2930 g_return_val_if_fail (check_header_seek (cts, "mhsd", mhsd_seek),
2931 FALSE);
2932
2933 /* The mhlt header should be the next after the mhsd header. In
2934 order to allow slight changes in the format, we skip headers
2935 until we find an mhlt inside the given mhsd */
2936
2937 mhlt_seek = find_next_a_in_b (cts, "mhlt", mhsd_seek, mhsd_seek);
2938 CHECK_ERROR (fimp, FALSE);
2939
2940 if (mhlt_seek == -1)
2941 {
2942 set_error_a_not_found_in_b (&fimp->error,
2943 "mhlt", "mhsd", mhsd_seek);
2944 return FALSE;
2945 }
2946
2947 /* Now we are at the mhlt */
2948 nr_tracks = get32lint (cts, mhlt_seek+8);
2949 CHECK_ERROR (fimp, FALSE);
2950
2951 seek = find_next_a_in_b (cts, "mhit", mhsd_seek, mhlt_seek);
2952 CHECK_ERROR (fimp, FALSE);
2953 /* seek should now point to the first mhit */
2954 for (i=0; i<nr_tracks; ++i)
2955 {
2956 /* seek could be -1 if first mhit could not be found */
2957 if (seek != -1)
2958 seek = get_mhit (fimp, seek);
2959 if (fimp->error) return FALSE;
2960 if (seek == -1)
2961 { /* this should not be -- issue warning */
2962 g_warning (_("iTunesDB corrupt: number of tracks (mhit hunks) inconsistent. Trying to continue.\n"));
2963 break;
2964 }
2965 }
2966 for (gl=fimp->tracks; gl; gl=g_list_next(gl)) {
2967 Itdb_Track *track = (Itdb_Track *)gl->data;
2968 itdb_track_add (fimp->itdb, track, 0);
2969 }
2970 return TRUE;
2971 }
2972
2973
2974
2975 /* Read the playlists (mhlp). mhsd_seek must point to type 2 or type 3
2976 mhsd (this is treated as a programming error) */
2977 /* Return value:
2978 TRUE: import successful
2979 FALSE: error occured, fimp->error is set */
parse_playlists(FImport * fimp,glong mhsd_seek)2980 static gboolean parse_playlists (FImport *fimp, glong mhsd_seek)
2981 {
2982 FContents *cts;
2983 glong seek, mhlp_seek;
2984 guint32 nr_playlists, i;
2985 guint mhsd_type;
2986
2987 g_return_val_if_fail (fimp, FALSE);
2988 g_return_val_if_fail (fimp->itdb, FALSE);
2989 g_return_val_if_fail (fimp->fcontents, FALSE);
2990 g_return_val_if_fail (fimp->fcontents->filename, FALSE);
2991 g_return_val_if_fail (mhsd_seek >= 0, FALSE);
2992
2993 cts = fimp->fcontents;
2994
2995 g_return_val_if_fail (check_header_seek (cts, "mhsd", mhsd_seek),
2996 FALSE);
2997 mhsd_type = get32lint (cts, mhsd_seek+12);
2998
2999 /* The mhlp header should be the next after the mhsd header. In
3000 order to allow slight changes in the format, we skip headers
3001 until we find an mhlp inside the given mhsd */
3002 mhlp_seek = find_next_a_in_b (cts, "mhlp", mhsd_seek, mhsd_seek);
3003 CHECK_ERROR (fimp, FALSE);
3004
3005 if (mhlp_seek == -1)
3006 {
3007 set_error_a_not_found_in_b (&fimp->error,
3008 "mhlp", "mhsd", mhsd_seek);
3009 return FALSE;
3010 }
3011 /* Now we are at the mhlp */
3012
3013 nr_playlists = get32lint (cts, mhlp_seek+8);
3014 CHECK_ERROR (fimp, FALSE);
3015
3016 /* Create track-id tree for quicker track lookup */
3017 fimp->idtree = itdb_track_id_tree_create (fimp->itdb);
3018
3019 seek = find_next_a_in_b (cts, "mhyp", mhsd_seek, mhlp_seek);
3020 CHECK_ERROR (fimp, FALSE);
3021 /* seek should now point to the first mhit */
3022 for (i=0; i<nr_playlists; ++i)
3023 {
3024 /* seek could be -1 if first mhyp could not be found */
3025 if (seek != -1)
3026 seek = get_playlist (fimp, mhsd_type, seek);
3027 if (fimp->error) return FALSE;
3028 if (seek == -1)
3029 { /* this should not be -- issue warning */
3030 g_warning (_("iTunesDB possibly corrupt: number of playlists (mhyp hunks) inconsistent. Trying to continue.\n"));
3031 break;
3032 }
3033 }
3034
3035 itdb_track_id_tree_destroy (fimp->idtree);
3036 fimp->idtree = NULL;
3037
3038 return TRUE;
3039 }
3040
parse_genius_mhsd(FImport * fimp,glong mhsd_seek)3041 static gboolean parse_genius_mhsd(FImport *fimp, glong mhsd_seek)
3042 {
3043 FContents *cts;
3044 guint32 hdrlen, mhsdlen;
3045 gint32 len;
3046 gchar *genius_cuid;
3047
3048 g_return_val_if_fail (fimp, FALSE);
3049 g_return_val_if_fail (fimp->itdb, FALSE);
3050 g_return_val_if_fail (fimp->fcontents, FALSE);
3051 g_return_val_if_fail (fimp->fcontents->filename, FALSE);
3052 g_return_val_if_fail (mhsd_seek >= 0, FALSE);
3053
3054 cts = fimp->fcontents;
3055
3056 g_return_val_if_fail (check_header_seek (cts, "mhsd", mhsd_seek),
3057 FALSE);
3058
3059 hdrlen = get32lint (cts, mhsd_seek+4);
3060 mhsdlen = get32lint (cts, mhsd_seek+8);
3061
3062 len = mhsdlen - hdrlen;
3063 if (len < 0) {
3064 return FALSE;
3065 }
3066
3067 if (len != 32) {
3068 g_warning(_("%s: Unexpected length %d for genius_cuid!\n"), __func__, len);
3069 }
3070
3071 genius_cuid = g_new0(gchar, len+1);
3072 if (!seek_get_n_bytes (cts, genius_cuid, mhsd_seek+hdrlen, len)) {
3073 g_free (genius_cuid);
3074 return FALSE;
3075 }
3076
3077 fimp->itdb->priv->genius_cuid = genius_cuid;
3078
3079 return TRUE;
3080 }
3081
looks_like_itunesdb(FImport * fimp)3082 static gboolean looks_like_itunesdb (FImport *fimp)
3083 {
3084 FContents *cts;
3085
3086 cts = fimp->fcontents;
3087
3088 /* Check if it looks like an iTunesDB */
3089 if (!check_header_seek (cts, "mhbd", 0))
3090 {
3091 fcontents_set_reversed (cts, TRUE);
3092 if (cts->error) return 0;
3093 if (!check_header_seek (cts, "mhbd", 0))
3094 {
3095 if (!cts->error)
3096 { /* set error */
3097 g_set_error (&cts->error,
3098 ITDB_FILE_ERROR,
3099 ITDB_FILE_ERROR_CORRUPT,
3100 _("Not a iTunesDB: '%s' (missing mhdb header)."),
3101 cts->filename);
3102 }
3103 return FALSE;
3104 }
3105 }
3106 return TRUE;
3107 }
3108
parse_fimp(FImport * fimp,gboolean compressed)3109 static gboolean parse_fimp (FImport *fimp, gboolean compressed)
3110 {
3111 glong seek=0;
3112 FContents *cts;
3113 glong mhsd_1, mhsd_2, mhsd_3, mhsd_5, mhsd_9;
3114
3115 g_return_val_if_fail (fimp, FALSE);
3116 g_return_val_if_fail (fimp->itdb, FALSE);
3117 g_return_val_if_fail (fimp->fcontents, FALSE);
3118 g_return_val_if_fail (fimp->fcontents->filename, FALSE);
3119
3120 if (!looks_like_itunesdb (fimp)) {
3121 return FALSE;
3122 }
3123
3124 if (compressed) {
3125 itdb_zlib_check_decompress_fimp (fimp);
3126 }
3127
3128 cts = fimp->fcontents;
3129
3130 /* get the positions of the various mhsd */
3131 /* type 1: track list */
3132 mhsd_1 = find_mhsd (cts, 1);
3133 CHECK_ERROR (fimp, FALSE);
3134 /* type 2: standard playlist section -- Podcasts playlist will be
3135 just an ordinary playlist */
3136 mhsd_2 = find_mhsd (cts, 2);
3137 CHECK_ERROR (fimp, FALSE);
3138 /* type 3: playlist section with special version of Podcasts
3139 playlist (optional) */
3140 mhsd_3 = find_mhsd (cts, 3);
3141 CHECK_ERROR (fimp, FALSE);
3142
3143 /* read smart playlists */
3144 mhsd_5 = find_mhsd (cts, 5);
3145 CHECK_ERROR (fimp, FALSE);
3146
3147 /* read genius cuid mhsd */
3148 mhsd_9 = find_mhsd (cts, 9);
3149 CHECK_ERROR (fimp, FALSE);
3150
3151 fimp->itdb->version = get32lint (cts, seek+0x10);
3152 CHECK_ERROR (fimp, FALSE);
3153 fimp->itdb->id = get64lint (cts, seek+0x18);
3154 CHECK_ERROR (fimp, FALSE);
3155 fimp->itdb->priv->platform = get16lint (cts, seek+0x20);
3156 CHECK_ERROR (fimp, FALSE);
3157 fimp->itdb->priv->unk_0x22 = get16lint (cts, seek+0x22);
3158 CHECK_ERROR (fimp, FALSE);
3159 fimp->itdb->priv->id_0x24 = get64lint (cts, seek+0x24);
3160 CHECK_ERROR (fimp, FALSE);
3161 fimp->itdb->priv->lang = get16lint (cts, seek+0x46);
3162 CHECK_ERROR (fimp, FALSE);
3163 fimp->itdb->priv->pid = get64lint (cts, seek+0x48);
3164 CHECK_ERROR (fimp, FALSE);
3165 fimp->itdb->priv->unk_0x50 = get32lint (cts, seek+0x50);
3166 CHECK_ERROR (fimp, FALSE);
3167 fimp->itdb->priv->unk_0x54 = get32lint (cts, seek+0x54);
3168 CHECK_ERROR (fimp, FALSE);
3169 fimp->itdb->tzoffset = get32lint (cts, seek+0x6c);
3170 CHECK_ERROR (fimp, FALSE);
3171 fimp->itdb->priv->audio_language = get16lint (cts, seek+0xA0);
3172 CHECK_ERROR (fimp, FALSE);
3173 fimp->itdb->priv->subtitle_language = get16lint (cts, seek+0xA2);
3174 CHECK_ERROR (fimp, FALSE);
3175 fimp->itdb->priv->unk_0xa4 = get16lint (cts, seek+0xA4);
3176 CHECK_ERROR (fimp, FALSE);
3177 fimp->itdb->priv->unk_0xa6 = get16lint (cts, seek+0xA6);
3178 CHECK_ERROR (fimp, FALSE);
3179 fimp->itdb->priv->unk_0xa8 = get16lint (cts, seek+0xA8);
3180 CHECK_ERROR (fimp, FALSE);
3181 if(fimp->itdb->priv->unk_0xa8 != 0) {
3182 g_warning ("Unknown value for 0xa8 in header: should be 0 for uncompressed, is %d.\n", fimp->itdb->priv->unk_0xa8);
3183 }
3184
3185 if (mhsd_1 == -1)
3186 { /* Very bad: no type 1 mhsd which should hold the tracklist */
3187 g_set_error (&fimp->error,
3188 ITDB_FILE_ERROR,
3189 ITDB_FILE_ERROR_CORRUPT,
3190 _("iTunesDB '%s' corrupt: Could not find tracklist (no mhsd type 1 section found)"),
3191 cts->filename);
3192 return FALSE;
3193 }
3194
3195 /* copy the 'reversed endian flag' */
3196 if (cts->reversed) {
3197 fimp->itdb->device->byte_order = G_BIG_ENDIAN;
3198 } else {
3199 fimp->itdb->device->byte_order = G_LITTLE_ENDIAN;
3200 }
3201 #if 0
3202 fimp->itdb->device->endianess_set = TRUE;
3203 fimp->itdb->device->endianess_reversed = cts->reversed;
3204 #endif
3205
3206 parse_tracks (fimp, mhsd_1);
3207 if (fimp->error) return FALSE;
3208
3209 if (mhsd_3 != -1)
3210 parse_playlists (fimp, mhsd_3);
3211 else if (mhsd_2 != -1)
3212 parse_playlists (fimp, mhsd_2);
3213 else
3214 { /* Very bad: no type 2 or type 3 mhsd which should hold the
3215 playlists */
3216 g_set_error (&fimp->error,
3217 ITDB_FILE_ERROR,
3218 ITDB_FILE_ERROR_CORRUPT,
3219 _("iTunesDB '%s' corrupt: Could not find playlists (no mhsd type 2 or type 3 sections found)"),
3220 cts->filename);
3221 return FALSE;
3222 }
3223
3224 if (mhsd_5 != -1) {
3225 parse_playlists (fimp, mhsd_5);
3226 }
3227
3228 if (mhsd_9 != -1) {
3229 parse_genius_mhsd (fimp, mhsd_9);
3230 }
3231
3232 return TRUE;
3233 }
3234
3235
3236
3237 /* Set @error with standard error message */
error_no_itunes_dir(const gchar * mp,GError ** error)3238 static void error_no_itunes_dir (const gchar *mp, GError **error)
3239 {
3240 gchar *str;
3241
3242 g_return_if_fail (mp);
3243 g_return_if_fail (error);
3244
3245 str = g_build_filename (mp, "iPod_Control", "iTunes", NULL);
3246 g_set_error (error,
3247 ITDB_FILE_ERROR,
3248 ITDB_FILE_ERROR_NOTFOUND,
3249 _("iTunes directory not found: '%s' (or similar)."),
3250 str);
3251 g_free (str);
3252 }
3253
3254 /* Set @error with standard error message */
error_no_music_dir(const gchar * mp,GError ** error)3255 static void error_no_music_dir (const gchar *mp, GError **error)
3256 {
3257 gchar *str;
3258
3259 g_return_if_fail (mp);
3260 g_return_if_fail (error);
3261
3262 str = g_build_filename (mp, "iPod_Control", "Music", NULL);
3263 g_set_error (error,
3264 ITDB_FILE_ERROR,
3265 ITDB_FILE_ERROR_NOTFOUND,
3266 _("Music directory not found: '%s' (or similar)."),
3267 str);
3268 g_free (str);
3269 }
3270
3271 #if 0
3272 /* Set @error with standard error message */
3273 static void error_no_control_dir (const gchar *mp, GError **error)
3274 {
3275 gchar *str;
3276
3277 g_return_if_fail (mp);
3278 g_return_if_fail (error);
3279
3280 str = g_build_filename (mp, "iPod_Control", NULL);
3281 g_set_error (error,
3282 ITDB_FILE_ERROR,
3283 ITDB_FILE_ERROR_NOTFOUND,
3284 _("Control directory not found: '%s' (or similar)."),
3285 str);
3286 g_free (str);
3287 }
3288 #endif
3289
3290
3291 static gboolean
itdb_parse_internal(Itdb_iTunesDB * itdb,gboolean compressed,GError ** error)3292 itdb_parse_internal (Itdb_iTunesDB *itdb, gboolean compressed, GError **error)
3293 {
3294 FImport *fimp;
3295 gboolean success = FALSE;
3296
3297 g_return_val_if_fail (itdb->filename != NULL, FALSE);
3298
3299 fimp = g_new0 (FImport, 1);
3300 fimp->itdb = itdb;
3301
3302 fimp->fcontents = fcontents_read (itdb->filename, error);
3303
3304 if (fimp->fcontents)
3305 {
3306 itdb_hash72_extract_hash_info (fimp->itdb->device,
3307 (guchar *)fimp->fcontents->contents,
3308 fimp->fcontents->length);
3309 if (!playcounts_init (fimp))
3310 {
3311 g_warning ("Error parsing recent playcounts");
3312 }
3313 if (parse_fimp (fimp, compressed))
3314 {
3315 if (read_OTG_playlists (fimp))
3316 {
3317 success = TRUE;
3318 }
3319 }
3320 }
3321
3322 if (fimp->error)
3323 g_propagate_error (error, fimp->error);
3324
3325 itdb_free_fimp (fimp);
3326
3327 return success;
3328 }
3329
3330 /**
3331 * itdb_parse:
3332 * @mp: mount point of the iPod (eg "/mnt/ipod") in local encoding
3333 * @error: return location for a #GError or NULL
3334 *
3335 * Parse the Itdb_iTunesDB of the iPod located at @mp
3336 *
3337 * Returns: a newly allocated #Itdb_iTunesDB struct holding the tracks and
3338 * the playlists present on the iPod at @mp, NULL if @mp isn't an iPod mount
3339 * point. If non-NULL, the #Itdb_iTunesDB is to be freed with itdb_free() when
3340 * it's no longer needed
3341 */
itdb_parse(const gchar * mp,GError ** error)3342 Itdb_iTunesDB *itdb_parse (const gchar *mp, GError **error)
3343 {
3344 gchar *filename;
3345 Itdb_iTunesDB *itdb = NULL;
3346 gboolean compressed = FALSE;
3347
3348 filename = itdb_get_itunescdb_path (mp);
3349 if (!filename) {
3350 filename = itdb_get_itunesdb_path (mp);
3351 } else {
3352 compressed = TRUE;
3353 }
3354 if (filename)
3355 {
3356 itdb = itdb_new ();
3357
3358 if (itdb)
3359 {
3360 gboolean success;
3361
3362 itdb_set_mountpoint (itdb, mp);
3363 itdb->filename = g_strdup (filename);
3364 success = itdb_parse_internal (itdb, compressed, error);
3365 if (success)
3366 {
3367 /* We don't test the return value of ipod_parse_artwork_db
3368 * since the database content will be consistent even if
3369 * we fail to get the various thumbnails, we ignore the
3370 * error since older ipods don't have thumbnails.
3371
3372 * FIXME: this probably should go into itdb_parse_file,
3373 * but I don't understand its purpose, and
3374 * ipod_parse_artwork_db needs the mountpoint field from
3375 * the itdb, which may not be available in the other
3376 * function
3377
3378 * JCS: itdb_parse_file is used to read local repositories
3379 * (usually repositories stored in
3380 * ~/.gtkpod). ipod_parse_artwork_db (and the
3381 * corresponding artbook write function) should probably
3382 * be expanded to look for (write) the required files into
3383 * the same directory as itdb->filename in case
3384 * itdb->mountpoint does not exist. Because several local
3385 * repositories may exist in the same directory, the names
3386 * should be modified by the repository name.
3387 */
3388 ipod_parse_artwork_db (itdb);
3389 }
3390 else
3391 {
3392 itdb_free (itdb);
3393 itdb = NULL;
3394 }
3395 }
3396 }
3397 else
3398 {
3399 g_set_error (error,
3400 ITDB_FILE_ERROR,
3401 ITDB_FILE_ERROR_NOTFOUND,
3402 _("Couldn't find an iPod database on %s."),
3403 mp);
3404 }
3405 g_free (filename);
3406 return itdb;
3407 }
3408
3409 /**
3410 * itdb_parse_file:
3411 * @filename: path to a file in iTunesDB format
3412 * @error: return location for a #GError or NULL
3413 *
3414 * Same as itunesdb_parse(), but filename is specified directly.
3415 *
3416 * Returns: a newly allocated #Itdb_iTunesDB struct holding the tracks and
3417 * the playlists present in @filename, NULL if @filename isn't a parsable
3418 * iTunesDB file. If non-NULL, the #Itdb_iTunesDB is to be freed with
3419 * itdb_free() when it's no longer needed
3420 */
itdb_parse_file(const gchar * filename,GError ** error)3421 Itdb_iTunesDB *itdb_parse_file (const gchar *filename, GError **error)
3422 {
3423 Itdb_iTunesDB *itdb;
3424 gboolean success;
3425
3426 g_return_val_if_fail (filename, NULL);
3427
3428 itdb = itdb_new ();
3429 itdb->filename = g_strdup (filename);
3430
3431 success = itdb_parse_internal (itdb, FALSE, error);
3432 if (!success)
3433 {
3434 itdb_free (itdb);
3435 itdb = NULL;
3436 }
3437
3438 return itdb;
3439 }
3440
3441
3442 /* up to here we had the functions for reading the iTunesDB */
3443 /* ---------------------------------------------------------------------- */
3444 /* from here on we have the functions for writing the iTunesDB */
3445
3446 /* will expand @cts when necessary in order to accomodate @len bytes
3447 starting at @seek */
wcontents_maybe_expand(WContents * cts,gulong len,gulong seek)3448 static void wcontents_maybe_expand (WContents *cts, gulong len,
3449 gulong seek)
3450 {
3451 g_return_if_fail (cts);
3452
3453 while (cts->pos+len > cts->total)
3454 {
3455 cts->total += WCONTENTS_STEPSIZE;
3456 cts->contents = g_realloc (cts->contents, cts->total);
3457 }
3458 }
3459
3460
3461 /* Write @data, @n bytes long to position @seek. Will always be
3462 * successful because glib terminates when out of memory */
put_data_seek(WContents * cts,gchar * data,gulong len,gulong seek)3463 static void put_data_seek (WContents *cts, gchar *data,
3464 gulong len, gulong seek)
3465 {
3466 g_return_if_fail (cts);
3467
3468 if (len != 0)
3469 {
3470 g_return_if_fail (data);
3471 wcontents_maybe_expand (cts, len, seek);
3472
3473 memcpy (&cts->contents[seek], data, len);
3474 /* adjust end position if necessary */
3475 if (seek+len > cts->pos)
3476 cts->pos = seek+len;
3477 }
3478 }
3479
3480
3481
3482 /* Write @data, @n bytes long to end of @cts. Will always be
3483 * successful because glib terminates when out of memory */
put_data(WContents * cts,gchar * data,gulong len)3484 static void put_data (WContents *cts, gchar *data, gulong len)
3485 {
3486 g_return_if_fail (cts);
3487
3488 put_data_seek (cts, data, len, cts->pos);
3489 }
3490
3491
3492 /* Write @string without trailing Null to end of @cts. Will always be
3493 * successful because glib terminates when out of memory */
put_string(WContents * cts,gchar * string)3494 static void put_string (WContents *cts, gchar *string)
3495 {
3496 g_return_if_fail (cts);
3497 g_return_if_fail (string);
3498
3499 put_data (cts, string, strlen(string));
3500 }
3501
3502 /* Write 4-byte long @header identifcation taking into account
3503 * possible reversed endianess */
put_header(WContents * cts,gchar * header)3504 static void put_header (WContents *cts, gchar *header)
3505 {
3506 gchar rdata[4];
3507 gint i, offset, sign;
3508
3509
3510 g_return_if_fail (cts);
3511 g_return_if_fail (header);
3512 g_return_if_fail (strlen (header) == 4);
3513
3514 /* reverse data for write if necessary */
3515 if (cts->reversed)
3516 {
3517 offset = 3;
3518 sign = -1;
3519 }
3520 else
3521 {
3522 offset = 0;
3523 sign = 1;
3524 }
3525 for (i=0; i<4; ++i)
3526 {
3527 rdata[i] = header[offset + sign*i];
3528 }
3529
3530 put_data (cts, rdata, 4);
3531 }
3532
3533
3534
3535 /* ------------------------------------------------------------
3536 Little Endian
3537 ------------------------------------------------------------ */
3538
3539 /* Write 2-byte integer @n to @cts in little endian order. */
raw_put16lint(WContents * cts,guint16 n)3540 static void raw_put16lint (WContents *cts, guint16 n)
3541 {
3542 n = GUINT16_TO_LE (n);
3543 put_data (cts, (gchar *)&n, 2);
3544 }
3545
3546 /* Write 3-byte integer @n to @cts in big endian order. */
raw_put24lint(WContents * cts,guint32 n)3547 static void raw_put24lint (WContents *cts, guint32 n)
3548 {
3549 gchar buf[3] ;
3550 buf[2] = (n >> 16) & 0xff ;
3551 buf[1] = (n >> 8) & 0xff ;
3552 buf[0] = (n >> 0) & 0xff ;
3553 put_data (cts, buf, 3);
3554 }
3555
3556
3557 /* Write 4-byte integer @n to @cts in little endian order. */
raw_put32lint(WContents * cts,guint32 n)3558 static void raw_put32lint (WContents *cts, guint32 n)
3559 {
3560 n = GUINT32_TO_LE (n);
3561 put_data (cts, (gchar *)&n, 4);
3562 }
3563
3564 /* Write 4 byte floating number */
raw_put32lfloat(WContents * cts,float f)3565 static void raw_put32lfloat (WContents *cts, float f)
3566 {
3567 union
3568 {
3569 guint32 i;
3570 float f;
3571 } flt;
3572
3573 if (sizeof (float) != 4)
3574 {
3575 raw_put32lint (cts, 0);
3576 g_return_if_reached ();
3577 }
3578
3579 flt.f = f;
3580 raw_put32lint (cts, flt.i);
3581 }
3582
3583
3584 /* Write 4-byte integer @n to @cts at position @seek in little
3585 endian order. */
raw_put32lint_seek(WContents * cts,guint32 n,gulong seek)3586 static void raw_put32lint_seek (WContents *cts, guint32 n, gulong seek)
3587 {
3588 n = GUINT32_TO_LE (n);
3589 put_data_seek (cts, (gchar *)&n, 4, seek);
3590 }
3591
3592
3593 /* Write 8-byte integer @n to @cts in big little order. */
raw_put64lint(WContents * cts,guint64 n)3594 static void raw_put64lint (WContents *cts, guint64 n)
3595 {
3596 n = GUINT64_TO_LE (n);
3597 put_data (cts, (gchar *)&n, 8);
3598 }
3599
3600
3601 /* ------------------------------------------------------------
3602 Big Endian
3603 ------------------------------------------------------------ */
3604
3605 /* Write 2-byte integer @n to @cts in big endian order. */
raw_put16bint(WContents * cts,guint16 n)3606 static void raw_put16bint (WContents *cts, guint16 n)
3607 {
3608 n = GUINT16_TO_BE (n);
3609 put_data (cts, (gchar *)&n, 2);
3610 }
3611
3612 /* Write 3-byte integer @n to @cts in big endian order. */
raw_put24bint(WContents * cts,guint32 n)3613 static void raw_put24bint (WContents *cts, guint32 n)
3614 {
3615 gchar buf[3] ;
3616 buf[0] = (n >> 16) & 0xff ;
3617 buf[1] = (n >> 8) & 0xff ;
3618 buf[2] = (n >> 0) & 0xff ;
3619 put_data (cts, buf, 3);
3620 }
3621
3622 /* Write 4-byte integer @n to @cts in big endian order. */
raw_put32bint(WContents * cts,guint32 n)3623 static void raw_put32bint (WContents *cts, guint32 n)
3624 {
3625 n = GUINT32_TO_BE (n);
3626 put_data (cts, (gchar *)&n, 4);
3627 }
3628
3629
3630 /* Write 4-byte integer @n to @cts at position @seek in big
3631 endian order. */
raw_put32bint_seek(WContents * cts,guint32 n,gulong seek)3632 static void raw_put32bint_seek (WContents *cts, guint32 n, gulong seek)
3633 {
3634 n = GUINT32_TO_BE (n);
3635 put_data_seek (cts, (gchar *)&n, 4, seek);
3636 }
3637
3638
3639 /* Write 4 byte floating number */
raw_put32bfloat(WContents * cts,float f)3640 static void raw_put32bfloat (WContents *cts, float f)
3641 {
3642 union
3643 {
3644 guint32 i;
3645 float f;
3646 } flt;
3647
3648 if (sizeof (float) != 4)
3649 {
3650 raw_put32bint (cts, 0);
3651 g_return_if_reached ();
3652 }
3653
3654 flt.f = f;
3655 raw_put32bint (cts, flt.i);
3656 }
3657
3658
3659 /* Write 8-byte integer @n to cts in big endian order. */
raw_put64bint(WContents * cts,guint64 n)3660 static void raw_put64bint (WContents *cts, guint64 n)
3661 {
3662 n = GUINT64_TO_BE (n);
3663 put_data (cts, (gchar *)&n, 8);
3664 }
3665
3666
3667 /* ------------------------------------------------------------
3668 Not Endian Dependent
3669 ------------------------------------------------------------ */
3670
3671 /* Write 1-byte integer @n to @cts */
put8int(WContents * cts,guint8 n)3672 static void put8int (WContents *cts, guint8 n)
3673 {
3674 put_data (cts, (gchar *)&n, 1);
3675 }
3676
3677
3678 /* Append @n times 2-byte-long zeros */
put16_n0(WContents * cts,gulong n)3679 static void put16_n0 (WContents *cts, gulong n)
3680 {
3681 g_return_if_fail (cts);
3682
3683 if (n>0)
3684 {
3685 wcontents_maybe_expand (cts, 2*n, cts->pos);
3686 memset (&cts->contents[cts->pos], 0, 2*n);
3687 cts->pos += 2*n;
3688 }
3689 }
3690
3691 /* Append @n times 4-byte-long zeros */
put32_n0(WContents * cts,gulong n)3692 static void put32_n0 (WContents *cts, gulong n)
3693 {
3694 g_return_if_fail (cts);
3695
3696 if (n>0)
3697 {
3698 wcontents_maybe_expand (cts, 4*n, cts->pos);
3699 memset (&cts->contents[cts->pos], 0, 4*n);
3700 cts->pos += 4*n;
3701 }
3702 }
3703
3704
3705 /* ------------------------------------------------------------
3706 Reversed Endian Sensitive (little endian)
3707 ------------------------------------------------------------ */
3708
put16lint(WContents * cts,guint16 n)3709 static void put16lint (WContents *cts, guint16 n)
3710 {
3711 if (!cts->reversed)
3712 raw_put16lint (cts, n);
3713 else
3714 raw_put16bint (cts, n);
3715 }
3716
3717 #if 0
3718 static void put24lint (WContents *cts, guint32 n)
3719 {
3720 if (!cts->reversed)
3721 raw_put24lint (cts, n);
3722 else
3723 raw_put24bint (cts, n);
3724 }
3725 #endif
put32lint(WContents * cts,guint32 n)3726 static void put32lint (WContents *cts, guint32 n)
3727 {
3728 if (!cts->reversed)
3729 raw_put32lint (cts, n);
3730 else
3731 raw_put32bint (cts, n);
3732 }
3733
put32lfloat(WContents * cts,float f)3734 static void put32lfloat (WContents *cts, float f)
3735 {
3736 if (!cts->reversed)
3737 raw_put32lfloat (cts, f);
3738 else
3739 raw_put32bfloat (cts, f);
3740 }
3741
put32lint_seek(WContents * cts,guint32 n,gulong seek)3742 static void put32lint_seek (WContents *cts, guint32 n, gulong seek)
3743 {
3744 if (!cts->reversed)
3745 raw_put32lint_seek (cts, n, seek);
3746 else
3747 raw_put32bint_seek (cts, n, seek);
3748 }
3749
3750
put64lint(WContents * cts,guint64 n)3751 static void put64lint (WContents *cts, guint64 n)
3752 {
3753 if (!cts->reversed)
3754 raw_put64lint (cts, n);
3755 else
3756 raw_put64bint (cts, n);
3757 }
3758
3759 /* ------------------------------------------------------------
3760 Reversed Endian Sensitive (big endian)
3761 ------------------------------------------------------------ */
put16bint(WContents * cts,guint16 n)3762 static void put16bint (WContents *cts, guint16 n)
3763 {
3764 if (cts->reversed)
3765 raw_put16lint (cts, n);
3766 else
3767 raw_put16bint (cts, n);
3768 }
3769
3770 #if 0
3771 static void put16bint_seek (WContents *cts, guint16 n, gulong seek)
3772 {
3773 if (cts->reversed)
3774 raw_put16lint_seek (cts, n, seek);
3775 else
3776 raw_put16bint_seek (cts, n, seek);
3777 }
3778 #endif
put24bint(WContents * cts,guint32 n)3779 static void put24bint (WContents *cts, guint32 n)
3780 {
3781 if (cts->reversed)
3782 raw_put24lint (cts, n);
3783 else
3784 raw_put24bint (cts, n);
3785 }
3786
put32bint(WContents * cts,guint32 n)3787 static void put32bint (WContents *cts, guint32 n)
3788 {
3789 if (cts->reversed)
3790 raw_put32lint (cts, n);
3791 else
3792 raw_put32bint (cts, n);
3793 }
3794 #if 0
3795 static void put32bfloat (WContents *cts, float f)
3796 {
3797 if (cts->reversed)
3798 raw_put32lfloat (cts, f);
3799 else
3800 raw_put32bfloat (cts, f);
3801 }
3802 #endif
3803
put32bint_seek(WContents * cts,guint32 n,gulong seek)3804 static void put32bint_seek (WContents *cts, guint32 n, gulong seek)
3805 {
3806 if (cts->reversed)
3807 raw_put32lint_seek (cts, n, seek);
3808 else
3809 raw_put32bint_seek (cts, n, seek);
3810 }
3811
put64bint(WContents * cts,guint64 n)3812 static void put64bint (WContents *cts, guint64 n)
3813 {
3814 if (cts->reversed)
3815 raw_put64lint (cts, n);
3816 else
3817 raw_put64bint (cts, n);
3818 }
3819
3820
3821
3822 /* Write out the mhbd header. Size will be written later */
mk_mhbd(FExport * fexp,guint32 children)3823 static void mk_mhbd (FExport *fexp, guint32 children)
3824 {
3825 WContents *cts;
3826
3827 g_return_if_fail (fexp);
3828 g_return_if_fail (fexp->itdb);
3829 g_return_if_fail (fexp->wcontents);
3830
3831 cts = fexp->wcontents;
3832
3833 put_header (cts, "mhbd");
3834 put32lint (cts, 244); /* header size */
3835 put32lint (cts, -1); /* size of whole mhdb -- fill in later */
3836 if (itdb_device_supports_compressed_itunesdb (fexp->itdb->device)) {
3837 /* 2 on iPhone 3.0 and Nano 5G, 1 on iPod Color and iPod Classic
3838 the iPod Classic 3G won't work if set to 2. Tying it to compressed
3839 database support but that's a long shot. */
3840 put32lint (cts, 2);
3841 } else {
3842 put32lint (cts, 1);
3843 }
3844
3845 /* Version number: 0x01: iTunes 2
3846 0x02: iTunes 3
3847 0x09: iTunes 4.2
3848 0x0a: iTunes 4.5
3849 0x0b: iTunes 4.7
3850 0x0c: iTunes 4.71/4.8 (required for shuffle)
3851 0x0d: iTunes 4.9
3852 0x0e: iTunes 5
3853 0x0f: iTunes 6
3854 0x10: iTunes 6.0.1(?)
3855 0x11: iTunes 6.0.2
3856 0x12 = iTunes 6.0.5.
3857 0x13 = iTunes 7
3858 0x14 = iTunes 7.1
3859 0x15 = iTunes 7.2
3860 0x19 = iTunes 7.4
3861 0x28 = iTunes 8.2.1
3862 0x2a = iTunes 9.0.1
3863 0x2e = iTunes 9.1
3864 0x30 = iTunes 9.2
3865 Be aware that newer ipods won't work if the library version number is too
3866 old
3867 */
3868 fexp->itdb->version = 0x30;
3869 put32lint (cts, fexp->itdb->version);
3870 put32lint (cts, children);
3871 put64lint (cts, fexp->itdb->id);
3872 /* 0x20 */
3873 put16lint (cts, fexp->itdb->priv->platform); /* OS type, 1 = MacOS X, 2 = Windows */
3874
3875 /* 0x22 */
3876 put16lint (cts, fexp->itdb->priv->unk_0x22); /* unknown */
3877 put64lint (cts, fexp->itdb->priv->id_0x24); /* unkown id */
3878 put32lint (cts, 0); /* unknown */
3879 /* 0x30 */
3880 put16lint (cts, 0); /* set hashing scheme to 0 for now, will be set
3881 * to the appropriate value in
3882 * itdb_device_write_checksum */
3883 put16_n0 (cts, 10); /* unknown */
3884 /* 0x46 */
3885 put16lint (cts, fexp->itdb->priv->lang); /* language (e.g. 'de' for German) */
3886 put64lint (cts, fexp->itdb->priv->pid); /* library persistent ID */
3887 /* 0x50 */
3888 put32lint (cts, fexp->itdb->priv->unk_0x50); /* unknown: seen: 0x05 for nano 3G */
3889 /* seen: 0x01 for iPod Color */
3890 put32lint (cts, fexp->itdb->priv->unk_0x54); /* unknown: seen: 0x4d for nano 3G */
3891 /* seen: 0x0f for iPod Color */
3892 put32_n0 (cts, 5); /* 20 bytes hash58 */
3893 put32lint (cts, fexp->itdb->tzoffset); /* timezone offset in seconds */
3894 /* 0x70 */
3895 switch (itdb_device_get_checksum_type(fexp->itdb->device)) {
3896 case ITDB_CHECKSUM_HASHAB:
3897 put16lint (cts, 4); /* new on 4th gen iOS devices */
3898 break;
3899 case ITDB_CHECKSUM_HASH72:
3900 put16lint (cts, 2);
3901 break;
3902 default:
3903 put16lint (cts, 0);
3904 break;
3905 }
3906 put16lint (cts, 0); /* hash72 */
3907 put32_n0 (cts, 11); /* hash72 */
3908 /* 0xa0 */
3909 put16lint (cts, fexp->itdb->priv->audio_language); /* audio_language */
3910 put16lint (cts, fexp->itdb->priv->subtitle_language); /* subtitle_language */
3911 put16lint (cts, fexp->itdb->priv->unk_0xa4); /* unknown */
3912 put16lint (cts, fexp->itdb->priv->unk_0xa6); /* unknown */
3913 put16lint (cts, fexp->itdb->priv->unk_0xa8); /* unknown */
3914 put8int (cts, 0);
3915 /* 0xab */
3916 put8int (cts, 0); /* hashAB */
3917 put32_n0 (cts, 14); /* hashAB */
3918 put32_n0 (cts, 4); /* dummy space */
3919 }
3920
3921 /* Fill in the length of a standard header */
fix_header(WContents * cts,gulong header_seek)3922 static void fix_header (WContents *cts, gulong header_seek)
3923 {
3924 put32lint_seek (cts, cts->pos-header_seek, header_seek+8);
3925 }
3926
3927 /* Fill in the length of a short header which are common in the itunesSD */
fix_short_header(WContents * cts,gulong header_seek)3928 static void fix_short_header (WContents *cts, gulong header_seek)
3929 {
3930 put32lint_seek (cts, cts->pos-header_seek, header_seek+4);
3931 }
3932
3933 /* Write out the mhsd header. Size will be written later */
mk_mhsd(FExport * fexp,guint32 type)3934 static void mk_mhsd (FExport *fexp, guint32 type)
3935 {
3936 WContents *cts;
3937
3938 g_return_if_fail (fexp);
3939 g_return_if_fail (fexp->itdb);
3940 g_return_if_fail (fexp->wcontents);
3941
3942 cts = fexp->wcontents;
3943
3944 put_header (cts, "mhsd");
3945 put32lint (cts, 96); /* Headersize */
3946 put32lint (cts, -1); /* size of whole mhsd -- fill in later */
3947 put32lint (cts, type); /* type: 1 = track, 2 = playlist */
3948 put32_n0 (cts, 20); /* dummy space */
3949 }
3950
3951
3952 /* Write out the mhlt header. */
mk_mhlt(FExport * fexp,guint32 num)3953 static void mk_mhlt (FExport *fexp, guint32 num)
3954 {
3955 WContents *cts;
3956
3957 g_return_if_fail (fexp);
3958 g_return_if_fail (fexp->itdb);
3959 g_return_if_fail (fexp->wcontents);
3960
3961 cts = fexp->wcontents;
3962
3963 put_header (cts, "mhlt");
3964 put32lint (cts, 92); /* Headersize */
3965 put32lint (cts, num); /* tracks in this itunesdb */
3966 put32_n0 (cts, 20); /* dummy space */
3967 }
3968
3969
3970 /* Write out the mhit header. Size will be written later */
mk_mhit(WContents * cts,Itdb_Track * track)3971 static void mk_mhit (WContents *cts, Itdb_Track *track)
3972 {
3973 guint32 mac_time;
3974 g_return_if_fail (cts);
3975 g_return_if_fail (track);
3976
3977 put_header (cts, "mhit");
3978 put32lint (cts, 0x248);/* header size */
3979 put32lint (cts, -1); /* size of whole mhit -- fill in later */
3980 put32lint (cts, -1); /* nr of mhods in this mhit -- later */
3981 /* +0x10 */
3982 put32lint (cts, track->id); /* track index number */
3983
3984 put32lint (cts, track->visible);
3985 put32lint (cts, track->filetype_marker);
3986 put8int (cts, track->type1);
3987 put8int (cts, track->type2);
3988 put8int (cts, track->compilation);
3989 put8int (cts, track->rating);
3990 /* +0x20 */
3991 mac_time = device_time_time_t_to_mac (track->itdb->device, track->time_modified);
3992 put32lint (cts, mac_time); /* timestamp */
3993 put32lint (cts, track->size); /* filesize */
3994 put32lint (cts, track->tracklen);/* length of track in ms */
3995 put32lint (cts, track->track_nr);/* track number */
3996 /* +0x30 */
3997 put32lint (cts, track->tracks); /* number of tracks */
3998 put32lint (cts, track->year); /* the year */
3999 put32lint (cts, track->bitrate); /* bitrate */
4000 put32lint (cts, (((guint32)track->samplerate)<<16) |
4001 ((guint32)track->samplerate_low));
4002 /* +0x40 */
4003 put32lint (cts, track->volume); /* volume adjust */
4004 put32lint (cts, track->starttime);
4005 put32lint (cts, track->stoptime);
4006 put32lint (cts, track->soundcheck);
4007 /* +0x50 */
4008 put32lint (cts, track->playcount);/* playcount */
4009 /* track->playcount2 = track->playcount; */
4010 put32lint (cts, track->playcount2); /* playcount2, keep original value */
4011 mac_time = device_time_time_t_to_mac (track->itdb->device, track->time_played);
4012 put32lint (cts, mac_time); /* last time played */
4013 put32lint (cts, track->cd_nr); /* CD number */
4014 /* +0x60 */
4015 put32lint (cts, track->cds); /* number of CDs */
4016 put32lint (cts, track->drm_userid);
4017 mac_time = device_time_time_t_to_mac (track->itdb->device, track->time_added);
4018 put32lint (cts, mac_time); /* timestamp */
4019 put32lint (cts, track->bookmark_time);
4020 /* +0x70 */
4021 put64lint (cts, track->dbid);
4022 if (track->checked) put8int (cts, 1);
4023 else put8int (cts, 0);
4024 put8int (cts, track->app_rating);
4025 put16lint (cts, track->BPM);
4026 put16lint (cts, track->artwork_count);
4027 put16lint (cts, track->unk126);
4028 /* +0x80 */
4029 put32lint (cts, track->artwork_size);
4030 put32lint (cts, track->unk132);
4031 put32lfloat (cts, track->samplerate2);
4032 mac_time = device_time_time_t_to_mac (track->itdb->device, track->time_released);
4033 put32lint (cts, mac_time);
4034 /* +0x90 */
4035 put16lint (cts, track->unk144);
4036 put16lint (cts, track->explicit_flag);
4037 put32lint (cts, track->unk148);
4038 put32lint (cts, track->unk152);
4039 /* since iTunesDB version 0x0c */
4040 put32lint (cts, track->skipcount);
4041 /* +0xA0 */
4042 mac_time = device_time_time_t_to_mac (track->itdb->device, track->last_skipped);
4043 put32lint (cts, mac_time);
4044 put8int (cts, track->has_artwork);
4045 put8int (cts, track->skip_when_shuffling);
4046 put8int (cts, track->remember_playback_position);
4047 put8int (cts, track->flag4);
4048 put64lint (cts, track->dbid2);
4049 /* +0xB0 */
4050 put8int (cts, track->lyrics_flag);
4051 put8int (cts, track->movie_flag);
4052 put8int (cts, track->mark_unplayed);
4053 put8int (cts, track->unk179);
4054 put32lint (cts, track->unk180);
4055 put32lint (cts, track->pregap);
4056 put64lint (cts, track->samplecount);
4057 put32lint (cts, track->unk196);
4058 put32lint (cts, track->postgap);
4059 put32lint (cts, track->unk204);
4060 /* +0xD0 */
4061 put32lint (cts, track->mediatype);
4062 put32lint (cts, track->season_nr);
4063 put32lint (cts, track->episode_nr);
4064 put32lint (cts, track->unk220);
4065 /* +0xE0 */
4066 put32lint (cts, track->unk224);
4067 put32lint (cts, track->unk228);
4068 put32lint (cts, track->unk232);
4069 put32lint (cts, track->unk236);
4070 /* +0xF0 */
4071 put32lint (cts, track->unk240);
4072 put32lint (cts, track->unk244);
4073 put32lint (cts, track->gapless_data);
4074 put32lint (cts, track->unk252);
4075 /* +0x100 */
4076 put16lint (cts, track->gapless_track_flag);
4077 put16lint (cts, track->gapless_album_flag);
4078 put32_n0 (cts, 7);
4079 /* +0x120 */
4080 put32lint (cts, track->priv->album_id);
4081 put64lint (cts, track->itdb->priv->id_0x24); /* same as mhbd+0x24, purpose unknown */
4082 put32lint (cts, track->size); /* seems to be filesize again */
4083 /* +0x130 */
4084 put32lint (cts, 0);
4085 put64lint (cts, 0x808080808080LL); /* what the heck is this?! */
4086 put32lint (cts, 0);
4087 /* +0x140 */
4088 put32_n0 (cts, 2);
4089 if ((track->mediatype & ITDB_MEDIATYPE_EPUB_BOOK)
4090 || (track->mediatype & ITDB_MEDIATYPE_PDF_BOOK)) {
4091 put16lint (cts, 1);
4092 put16lint (cts, 1);
4093 } else {
4094 put32lint (cts, 0);
4095 }
4096 put32_n0 (cts, 5);
4097 /* +0x160 */
4098 /* mhii_link is needed on fat nanos/ipod classic to get artwork
4099 * in the right sidepane. This matches mhii::song_id in the ArtworkDB */
4100 put32lint (cts, track->mhii_link);
4101 put32lint (cts, 0);
4102 put32lint (cts, 1); /* unknown */
4103 put32lint (cts, 0);
4104 /* +0x170 */
4105 put32_n0 (cts, 28);
4106 /* +0x1E0 */
4107 put32lint (cts, track->priv->artist_id);
4108 put32_n0 (cts, 4);
4109 /* +0x1F4 */
4110 put32lint (cts, track->priv->composer_id); /* FIXME: this is another ID for something, composer?! */
4111 put32_n0 (cts, 20); /* padding */
4112 }
4113
4114
4115 /* Fill in the missing items of the mhit header:
4116 total size and number of mhods */
fix_mhit(WContents * cts,gulong mhit_seek,guint32 mhod_num)4117 static void fix_mhit (WContents *cts, gulong mhit_seek, guint32 mhod_num)
4118 {
4119 g_return_if_fail (cts);
4120
4121 /* size of whole mhit */
4122 put32lint_seek (cts, cts->pos-mhit_seek, mhit_seek+8);
4123 /* nr of mhods */
4124 put32lint_seek (cts, mhod_num, mhit_seek+12);
4125 }
4126
4127 /* Returns:
4128 - track->sort_artist if track->sort_artist is not NULL
4129
4130 - 'Artist, The' if track->sort_artist is NULL and track->artist of
4131 of type 'The Artist'.
4132
4133 - NULL if track->sort_artist is NULL and track->artist is not of
4134 type 'The Artist'.
4135
4136 You must g_free() the returned string */
get_sort_artist(Itdb_Track * track)4137 static gchar *get_sort_artist (Itdb_Track *track)
4138 {
4139 g_return_val_if_fail (track, NULL);
4140
4141 if (track->sort_artist && *track->sort_artist)
4142 {
4143 return g_strdup (track->sort_artist);
4144 }
4145
4146 if (!(track->artist && *track->artist))
4147 {
4148 return NULL;
4149 }
4150
4151 /* check if artist is of type 'The Artist' */
4152 if (g_ascii_strncasecmp ("The ", track->artist, 4) == 0)
4153 { /* return 'Artist, The', followed by five 0x01 chars
4154 (analogous to iTunes) */
4155 return g_strdup_printf ("%s, The%c%c%c%c%c",
4156 track->artist+4,
4157 0x01, 0x01, 0x01, 0x01, 0x01);
4158 }
4159
4160 return NULL;
4161 }
4162
4163
4164
4165
mhod52_sort_title(const struct mhod52track * a,const struct mhod52track * b)4166 static gint mhod52_sort_title (const struct mhod52track *a, const struct mhod52track *b)
4167 {
4168 return strcmp (a->title, b->title);
4169 }
4170
4171
mhod52_sort_album(const struct mhod52track * a,const struct mhod52track * b)4172 static gint mhod52_sort_album (const struct mhod52track *a, const struct mhod52track *b)
4173 {
4174 gint result;
4175
4176 result = strcmp (a->album, b->album);
4177 if (result == 0)
4178 result = a->cd_nr - b->cd_nr;
4179 if (result == 0)
4180 result = a->track_nr - b->track_nr;
4181 if (result == 0)
4182 result = strcmp (a->title, b->title);
4183 return result;
4184 }
4185
mhod52_sort_artist(struct mhod52track * a,struct mhod52track * b)4186 static gint mhod52_sort_artist (struct mhod52track *a, struct mhod52track *b)
4187 {
4188 gint result;
4189
4190 result = strcmp (a->artist, b->artist);
4191 if (result == 0)
4192 result = strcmp (a->album, b->album);
4193 if (result == 0)
4194 result = a->cd_nr - b->cd_nr;
4195 if (result == 0)
4196 result = a->track_nr - b->track_nr;
4197 if (result == 0)
4198 result = strcmp (a->title, b->title);
4199 return result;
4200 }
4201
mhod52_sort_genre(struct mhod52track * a,struct mhod52track * b)4202 static gint mhod52_sort_genre (struct mhod52track *a, struct mhod52track *b)
4203 {
4204 gint result;
4205
4206 result = strcmp (a->genre, b->genre);
4207 if (result == 0)
4208 result = strcmp (a->artist, b->artist);
4209 if (result == 0)
4210 result = strcmp (a->album, b->album);
4211 if (result == 0)
4212 result = a->cd_nr - b->cd_nr;
4213 if (result == 0)
4214 result = a->track_nr - b->track_nr;
4215 if (result == 0)
4216 result = strcmp (a->title, b->title);
4217 return result;
4218 }
4219
mhod52_sort_composer(struct mhod52track * a,struct mhod52track * b)4220 static gint mhod52_sort_composer (struct mhod52track *a, struct mhod52track *b)
4221 {
4222 gint result;
4223
4224 result = strcmp (a->composer, b->composer);
4225 if (result == 0)
4226 result = strcmp (a->album, b->album);
4227 if (result == 0)
4228 result = a->cd_nr - b->cd_nr;
4229 if (result == 0)
4230 result = a->track_nr - b->track_nr;
4231 if (result == 0)
4232 result = strcmp (a->title, b->title);
4233 return result;
4234 }
4235
4236
4237 /* Return the first alpha-numeric character in the string
4238 Return upper-case for alpha, return 0 for all numbers */
4239
jump_table_letter(gchar * p)4240 static gunichar2 jump_table_letter (gchar *p)
4241 {
4242 gunichar chr = 0;
4243 gboolean found_alnum_chars = FALSE;
4244
4245 g_return_val_if_fail (p != NULL, '0');
4246 g_return_val_if_fail (g_utf8_validate (p, -1, NULL), '0');
4247
4248 while (*p != '\0') {
4249 chr = g_utf8_get_char (p);
4250 if (g_unichar_isalnum (chr))
4251 {
4252 found_alnum_chars = TRUE;
4253 break;
4254 }
4255 p = g_utf8_find_next_char (p, NULL);
4256 }
4257
4258 if (!found_alnum_chars) {
4259 /* Return number 0 if no alphanumerics in string */
4260 return '0';
4261 }
4262
4263 if (g_unichar_isalpha(chr))
4264 {
4265 gunichar2 utf16chr;
4266 gunichar upperchr;
4267 gunichar2 *str;
4268 GError *err = NULL;
4269
4270 upperchr = g_unichar_toupper (chr);
4271 str = g_ucs4_to_utf16 (&upperchr, 1, NULL, NULL, &err);
4272 if (err != NULL) {
4273 fprintf (stderr, "Error in UCS4 to UTF16 conversion: %s, original unichar: %x, toupper unichar: %x\n", err->message, chr, upperchr);
4274 g_error_free (err);
4275 return '0';
4276 }
4277 else
4278 {
4279 utf16chr = str[0];
4280 g_free (str);
4281 return utf16chr;
4282 }
4283 }
4284
4285 return '0'; /* Return number 0 for any digit */
4286 }
4287
4288
4289 /* Create a new list containing all tracks but with collate keys
4290 instead of the actual strings (title, artist, album, genre,
4291 composer) */
mhod52_make_collate_keys(GList * tracks)4292 static GList *mhod52_make_collate_keys (GList *tracks)
4293 {
4294 gint numtracks = g_list_length (tracks);
4295 GList *gl, *coltracks = NULL;
4296 gint index=0;
4297
4298 for (gl=tracks; gl; gl=gl->next)
4299 {
4300 gchar *str;
4301 struct mhod52track *ct;
4302 Itdb_Track *tr = gl->data;
4303 g_return_val_if_fail (tr, NULL);
4304
4305 ct = g_new0 (struct mhod52track, 1);
4306 coltracks = g_list_prepend (coltracks, ct);
4307
4308 /* album */
4309 if (tr->sort_album && *tr->sort_album)
4310 {
4311 ct->album = g_utf8_collate_key (tr->sort_album, -1);
4312 ct->letter_album = jump_table_letter (tr->sort_album);
4313 }
4314 else if (tr->album)
4315 {
4316 ct->album = g_utf8_collate_key (tr->album, -1);
4317 ct->letter_album = jump_table_letter (tr->album);
4318 }
4319 else
4320 {
4321 ct->album = g_strdup ("");
4322 ct->letter_album = '0';
4323 }
4324
4325 /* title */
4326 if (tr->sort_title && *tr->sort_title)
4327 {
4328 ct->title = g_utf8_collate_key (tr->sort_title, -1);
4329 ct->letter_title = jump_table_letter (tr->sort_title);
4330 }
4331 else if (tr->title)
4332 {
4333 ct->title = g_utf8_collate_key (tr->title, -1);
4334 ct->letter_title = jump_table_letter (tr->title);
4335 }
4336 else
4337 {
4338 ct->title = g_strdup ("");
4339 ct->letter_title = '0';
4340 }
4341
4342 /* artist */
4343 str = get_sort_artist (tr);
4344 if (str)
4345 {
4346 ct->artist = g_utf8_collate_key (str, -1);
4347 ct->letter_artist = jump_table_letter (str);
4348 g_free (str);
4349 }
4350 else if (tr->artist)
4351 {
4352 ct->artist = g_utf8_collate_key (tr->artist, -1);
4353 ct->letter_artist = jump_table_letter (tr->artist);
4354 }
4355 else
4356 {
4357 ct->artist = g_strdup ("");
4358 ct->letter_artist = '0';
4359 }
4360
4361 /* genre */
4362 if (tr->genre)
4363 {
4364 ct->genre = g_utf8_collate_key (tr->genre, -1);
4365 ct->letter_genre = jump_table_letter (tr->genre);
4366 }
4367 else
4368 {
4369 ct->genre = g_strdup ("");
4370 ct->letter_genre = '0';
4371 }
4372
4373 /* composer */
4374 if (tr->sort_composer && *tr->sort_composer)
4375 {
4376 ct->composer = g_utf8_collate_key (tr->sort_composer, -1);
4377 ct->letter_composer = jump_table_letter (tr->sort_composer);
4378 }
4379 else if (tr->composer)
4380 {
4381 ct->composer = g_utf8_collate_key (tr->composer, -1);
4382 ct->letter_composer = jump_table_letter (tr->composer);
4383 }
4384 else
4385 {
4386 ct->composer = g_strdup ("");
4387 ct->letter_composer = '0';
4388 }
4389
4390 ct->track_nr = tr->track_nr;
4391 ct->cd_nr = tr->cd_nr;
4392 ct->numtracks = numtracks;
4393 ct->index = index++;
4394 }
4395 return coltracks;
4396 }
4397
4398
4399 /* Free all memory used up by the collate keys */
mhod52_free_collate_keys(GList * coltracks)4400 static void mhod52_free_collate_keys (GList *coltracks)
4401 {
4402 GList *gl;
4403
4404 for (gl=coltracks; gl; gl=gl->next)
4405 {
4406 struct mhod52track *ct = gl->data;
4407 g_return_if_fail (ct);
4408 g_free (ct->album);
4409 g_free (ct->title);
4410 g_free (ct->artist);
4411 g_free (ct->genre);
4412 g_free (ct->composer);
4413 g_free (ct);
4414 }
4415 g_list_free (coltracks);
4416 }
4417
4418 static void
itdb_chapterdata_build_chapter_blob_internal(WContents * cts,Itdb_Chapterdata * chapterdata)4419 itdb_chapterdata_build_chapter_blob_internal (WContents *cts,
4420 Itdb_Chapterdata *chapterdata)
4421 {
4422 gulong atom_len_seek;
4423 gint numchapters;
4424 GList *ch_gl = NULL;
4425 /* printf("[%s] -- inserting into \"chapter\"\n", __func__); */
4426
4427 numchapters = g_list_length (chapterdata->chapters);
4428
4429 put32lint (cts, chapterdata->unk024); /* unknown */
4430 put32lint (cts, chapterdata->unk028); /* unknown */
4431 put32lint (cts, chapterdata->unk032); /* unknown */
4432 atom_len_seek = cts->pos; /* needed to fix length */
4433 put32bint (cts, -1); /* total length of sean atom, fix later */
4434 put_header (cts, "sean");
4435 put32bint (cts, 1); /* unknown */
4436 put32bint (cts, numchapters+1); /* children */
4437 put32bint (cts, 0); /* unknown */
4438 for (ch_gl=chapterdata->chapters; ch_gl; ch_gl=ch_gl->next)
4439 {
4440 gunichar2 *title_utf16;
4441 Itdb_Chapter *chapter = ch_gl->data;
4442 glong len;
4443 title_utf16 = g_utf8_to_utf16 (chapter->chaptertitle, -1, NULL, &len, NULL);
4444 fixup_big_utf16 (title_utf16);
4445 put32bint (cts, 42+2*len); /* total length */
4446 put_header (cts, "chap");
4447 put32bint (cts, chapter->startpos); /* should we check if startpos=0 here? */
4448 put32bint (cts, 1); /* children */
4449 put32bint (cts, 0); /* unknown */
4450 put32bint (cts, 22+2*len); /* length */
4451 put_header (cts, "name");
4452 put32bint (cts, 1); /* unknown */
4453 put32_n0 (cts, 2); /* unknown */
4454 put16bint (cts, len);
4455 put_data (cts, (gchar *)title_utf16, 2*len);
4456 g_free (title_utf16);
4457 }
4458 put32bint (cts, 28); /* size */
4459 put_header (cts, "hedr");
4460 put32bint (cts, 1); /* unknown */
4461 put32bint (cts, 0); /* children */
4462 put32_n0 (cts, 2); /* unknown */
4463 put32bint (cts, 1); /* unknown */
4464
4465 put32bint_seek (cts, cts->pos-atom_len_seek, atom_len_seek); /* size */
4466 }
4467
4468 /* Write out one mhod header.
4469 type: see enum of MHMOD_IDs;
4470 data: utf8 string for text items
4471 position indicator for MHOD_ID_PLAYLIST
4472 Itdb_SPLPref for MHOD_ID_SPLPREF
4473 Itdb_SPLRules for MHOD_ID_SPLRULES */
mk_mhod(FExport * fexp,MHODData * mhod)4474 static void mk_mhod (FExport *fexp, MHODData *mhod)
4475 {
4476 WContents *cts = fexp->wcontents;
4477
4478 g_return_if_fail (cts);
4479 g_return_if_fail (mhod->valid);
4480
4481 switch ((enum MHOD_ID)mhod->type)
4482 {
4483 case MHOD_ID_TITLE:
4484 case MHOD_ID_PATH:
4485 case MHOD_ID_ALBUM:
4486 case MHOD_ID_ARTIST:
4487 case MHOD_ID_GENRE:
4488 case MHOD_ID_FILETYPE:
4489 case MHOD_ID_COMMENT:
4490 case MHOD_ID_CATEGORY:
4491 case MHOD_ID_COMPOSER:
4492 case MHOD_ID_GROUPING:
4493 case MHOD_ID_DESCRIPTION:
4494 case MHOD_ID_SUBTITLE:
4495 case MHOD_ID_TVSHOW:
4496 case MHOD_ID_TVEPISODE:
4497 case MHOD_ID_TVNETWORK:
4498 case MHOD_ID_ALBUMARTIST:
4499 case MHOD_ID_KEYWORDS:
4500 case MHOD_ID_SORT_ARTIST:
4501 case MHOD_ID_SORT_TITLE:
4502 case MHOD_ID_SORT_ALBUM:
4503 case MHOD_ID_SORT_ALBUMARTIST:
4504 case MHOD_ID_SORT_COMPOSER:
4505 case MHOD_ID_SORT_TVSHOW:
4506 case MHOD_ID_ALBUM_ALBUM:
4507 case MHOD_ID_ALBUM_ARTIST:
4508 case MHOD_ID_ALBUM_SORT_ARTIST:
4509 case MHOD_ID_ALBUM_ARTIST_MHII:
4510 g_return_if_fail (mhod->data.string);
4511 /* normal iTunesDBs seem to take utf16 strings), endian-inversed
4512 iTunesDBs seem to take utf8 strings */
4513 if (!cts->reversed)
4514 {
4515 /* convert to utf16 */
4516 glong len;
4517 gunichar2 *entry_utf16 = g_utf8_to_utf16 (mhod->data.string, -1,
4518 NULL, &len, NULL);
4519 fixup_little_utf16 (entry_utf16);
4520 put_header (cts, "mhod"); /* header */
4521 put32lint (cts, 24); /* size of header */
4522 put32lint (cts, sizeof (gunichar2)*len+40); /* size of header + body */
4523 put32lint (cts, mhod->type);/* type of the mhod */
4524 put32_n0 (cts, 2); /* unknown */
4525 /* end of header, start of data */
4526 put32lint (cts, 1); /* string type UTF16 */
4527 put32lint (cts, sizeof (gunichar2)*len); /* size of string */
4528 put32lint (cts, 1); /* unknown, but is set to 1 */
4529 put32lint (cts, 0);
4530 put_data (cts, (gchar *)entry_utf16, sizeof (gunichar2)*len);/* the string */
4531 g_free (entry_utf16);
4532 }
4533 else
4534 {
4535 guint32 len = strlen (mhod->data.string);
4536 put_header (cts, "mhod"); /* header */
4537 put32lint (cts, 24); /* size of header */
4538 put32lint (cts, len+40); /* size of header + body */
4539 put32lint (cts, mhod->type);/* type of the mhod */
4540 put32_n0 (cts, 2); /* unknown */
4541 /* end of header, start of data */
4542 put32lint (cts, 2); /* string type UTF 8 */
4543 put32lint (cts, len); /* size of string */
4544 put8int (cts, 1); /* unknown */
4545 put8int (cts, 0); /* unknown */
4546 put8int (cts, 0); /* unknown */
4547 put8int (cts, 0); /* unknown */
4548 put32lint (cts, 0); /* unknown */
4549 put_string (cts, mhod->data.string);/* the string */
4550 }
4551 break;
4552 case MHOD_ID_PODCASTURL:
4553 case MHOD_ID_PODCASTRSS:
4554 g_return_if_fail (mhod->data.string);
4555 {
4556 guint32 len = strlen (mhod->data.string);
4557 put_header (cts, "mhod"); /* header */
4558 put32lint (cts, 24); /* size of header */
4559 put32lint (cts, 24+len); /* size of header + data */
4560 put32lint (cts, mhod->type);/* type of the mhod */
4561 put32_n0 (cts, 2); /* unknown */
4562 put_string (cts, mhod->data.string);/* the string */
4563 }
4564 break;
4565 case MHOD_ID_PLAYLIST:
4566 put_header (cts, "mhod"); /* header */
4567 put32lint (cts, 24); /* size of header */
4568 put32lint (cts, 44); /* size of header + body */
4569 put32lint (cts, mhod->type); /* type of the entry */
4570 put32_n0 (cts, 2); /* unknown */
4571 /* end of header, start of data */
4572 put32lint (cts, mhod->data.track_pos);/* position of track in playlist */
4573 put32_n0 (cts, 4); /* unknown */
4574 break;
4575 case MHOD_ID_CHAPTERDATA:
4576 g_return_if_fail (mhod->data.chapterdata);
4577 {
4578 gulong header_seek = cts->pos; /* needed to fix length */
4579 put_header (cts, "mhod"); /* header */
4580 put32lint (cts, 24); /* header size */
4581 put32lint (cts, -1); /* total length, fix later */
4582 put32lint (cts, mhod->type); /* entry type */
4583 put32_n0 (cts, 2); /* unknown */
4584 itdb_chapterdata_build_chapter_blob_internal (cts, mhod->data.chapterdata);
4585 fix_header (cts, header_seek);
4586 }
4587 break;
4588 case MHOD_ID_SPLPREF:
4589 g_return_if_fail (mhod->data.splpref);
4590 put_header (cts, "mhod"); /* header */
4591 put32lint (cts, 24); /* size of header */
4592 put32lint (cts, 96); /* size of header + body */
4593 put32lint (cts, mhod->type);/* type of the entry */
4594 put32_n0 (cts, 2); /* unknown */
4595 /* end of header, start of data */
4596 put8int (cts, mhod->data.splpref->liveupdate);
4597 put8int (cts, mhod->data.splpref->checkrules? 1:0);
4598 put8int (cts, mhod->data.splpref->checklimits);
4599 put8int (cts, mhod->data.splpref->limittype);
4600 put8int (cts, mhod->data.splpref->limitsort & 0xff);
4601 put8int (cts, 0); /* unknown */
4602 put8int (cts, 0); /* unknown */
4603 put8int (cts, 0); /* unknown */
4604 put32lint (cts, mhod->data.splpref->limitvalue);
4605 put8int (cts, mhod->data.splpref->matchcheckedonly);
4606 /* for the following see note at definitions of limitsort
4607 types in itunesdb.h */
4608 put8int (cts, (mhod->data.splpref->limitsort & 0x80000000) ? 1:0);
4609 put8int (cts, 0); /* unknown */
4610 put8int (cts, 0); /* unknown */
4611 put32_n0 (cts, 14); /* unknown */
4612 break;
4613 case MHOD_ID_SPLRULES:
4614 g_return_if_fail (mhod->data.splrules);
4615 {
4616 gulong header_seek = cts->pos; /* needed to fix length */
4617 GList *gl;
4618 gint numrules = g_list_length (mhod->data.splrules->rules);
4619
4620 put_header (cts, "mhod"); /* header */
4621 put32lint (cts, 24); /* size of header */
4622 put32lint (cts, -1); /* total length, fix later */
4623 put32lint (cts, mhod->type);/* type of the entry */
4624 put32_n0 (cts, 2); /* unknown */
4625 /* end of header, start of data */
4626 /* For some reason this is the only part of the iTunesDB
4627 that uses big endian */
4628 put_header (cts, "SLst"); /* header */
4629 put32bint (cts, mhod->data.splrules->unk004); /* unknown*/
4630 put32bint (cts, numrules);
4631 put32bint (cts, mhod->data.splrules->match_operator);
4632 put32_n0 (cts, 30); /* unknown */
4633 /* end of header, now follow the rules */
4634 for (gl=mhod->data.splrules->rules; gl; gl=gl->next)
4635 {
4636 Itdb_SPLRule *splr = gl->data;
4637 ItdbSPLFieldType ft;
4638 glong len;
4639 gunichar2 *entry_utf16;
4640 g_return_if_fail (splr);
4641 ft = itdb_splr_get_field_type (splr);
4642 /* printf ("%p: field: %d ft: %d\n", splr, splr->field, ft);*/
4643 itdb_splr_validate (splr);
4644 put32bint (cts, splr->field);
4645 put32bint (cts, splr->action);
4646 put32_n0 (cts, 11); /* unknown */
4647 switch (ft)
4648 {
4649 case ITDB_SPLFT_STRING:
4650 /* write string-type rule */
4651 len = 0;
4652 entry_utf16 = NULL;
4653 /* splr->string may be NULL */
4654 if (splr->string)
4655 entry_utf16 = g_utf8_to_utf16 (splr->string,
4656 -1,NULL,&len,NULL);
4657 fixup_big_utf16 (entry_utf16);
4658 put32bint (cts, sizeof (gunichar2)*len); /* length of string */
4659 put_data (cts, (gchar *)entry_utf16, sizeof (gunichar2)*len);
4660 g_free (entry_utf16);
4661 break;
4662 case ITDB_SPLFT_DATE:
4663 case ITDB_SPLFT_INT:
4664 case ITDB_SPLFT_BOOLEAN:
4665 case ITDB_SPLFT_PLAYLIST:
4666 case ITDB_SPLFT_UNKNOWN:
4667 case ITDB_SPLFT_BINARY_AND: {
4668 guint64 fromvalue;
4669 guint64 tovalue;
4670
4671 fromvalue = splr->fromvalue;
4672 tovalue = splr->tovalue;
4673
4674 if (ft == ITDB_SPLFT_DATE) {
4675 ItdbSPLActionType at;
4676 at = itdb_splr_get_action_type (splr);
4677 if ((at == ITDB_SPLAT_RANGE_DATE) ||
4678 (at == ITDB_SPLAT_DATE))
4679 {
4680 Itdb_iTunesDB *itdb = fexp->itdb;
4681 fromvalue = device_time_time_t_to_mac (itdb->device, fromvalue);
4682 tovalue = device_time_time_t_to_mac (itdb->device, tovalue);
4683 }
4684 }
4685
4686 /* write non-string-type rule */
4687 put32bint (cts, 0x44); /* length of data */
4688 /* data */
4689 put64bint (cts, fromvalue);
4690 put64bint (cts, splr->fromdate);
4691 put64bint (cts, splr->fromunits);
4692 put64bint (cts, tovalue);
4693 put64bint (cts, splr->todate);
4694 put64bint (cts, splr->tounits);
4695 put32bint (cts, splr->unk052);
4696 put32bint (cts, splr->unk056);
4697 put32bint (cts, splr->unk060);
4698 put32bint (cts, splr->unk064);
4699 put32bint (cts, splr->unk068);
4700 break;
4701 }
4702 }
4703 }
4704 /* insert length of mhod junk */
4705 fix_header (cts, header_seek);
4706 }
4707 break;
4708 case MHOD_ID_LIBPLAYLISTINDEX:
4709 g_return_if_fail (mhod->data.mhod52coltracks);
4710 g_return_if_fail (mhod->data.mhod52coltracks->data);
4711 {
4712 struct mhod52track *ct = mhod->data.mhod52coltracks->data;
4713 gint numtracks = ct->numtracks;
4714 GList *gl;
4715 gpointer compfunc = NULL;
4716 gunichar2 sortkey = 0;
4717 gunichar2 lastsortkey = 0;
4718 guint32 mhod53index = 0;
4719 struct mhod53_entry *m53 = NULL;
4720
4721 /* sort list */
4722 switch (mhod->mhod52sorttype)
4723 {
4724 case MHOD52_SORTTYPE_TITLE:
4725 compfunc = mhod52_sort_title;
4726 break;
4727 case MHOD52_SORTTYPE_ALBUM:
4728 compfunc = mhod52_sort_album;
4729 break;
4730 case MHOD52_SORTTYPE_ARTIST:
4731 compfunc = mhod52_sort_artist;
4732 break;
4733 case MHOD52_SORTTYPE_GENRE:
4734 compfunc = mhod52_sort_genre;
4735 break;
4736 case MHOD52_SORTTYPE_COMPOSER:
4737 compfunc = mhod52_sort_composer;
4738 break;
4739 }
4740 g_return_if_fail (compfunc);
4741
4742 /* sort the tracks */
4743 mhod->data.mhod52coltracks = g_list_sort (mhod->data.mhod52coltracks,
4744 compfunc);
4745 /* Write the MHOD */
4746 put_header (cts, "mhod"); /* header */
4747 put32lint (cts, 24); /* size of header */
4748 put32lint (cts, 4*numtracks+72); /* size of header + body */
4749 put32lint (cts, mhod->type); /* type of the mhod */
4750 put32_n0 (cts, 2); /* unknown */
4751 /* end of header, start of data */
4752 put32lint (cts, mhod->mhod52sorttype); /* sort type */
4753 put32lint (cts, numtracks); /* number of entries */
4754 put32_n0 (cts, 10); /* unknown */
4755 for (gl=mhod->data.mhod52coltracks; gl; gl=gl->next)
4756 {
4757 ct = gl->data;
4758 g_return_if_fail (ct);
4759 put32lint (cts, ct->index);
4760
4761 /* This is for the type 53s */
4762 switch (mhod->mhod52sorttype)
4763 {
4764 case MHOD52_SORTTYPE_TITLE:
4765 sortkey = ct->letter_title;
4766 break;
4767 case MHOD52_SORTTYPE_ALBUM:
4768 sortkey = ct->letter_album;
4769 break;
4770 case MHOD52_SORTTYPE_ARTIST:
4771 sortkey = ct->letter_artist;
4772 break;
4773 case MHOD52_SORTTYPE_GENRE:
4774 sortkey = ct->letter_genre;
4775 break;
4776 case MHOD52_SORTTYPE_COMPOSER:
4777 sortkey = ct->letter_composer;
4778 break;
4779 }
4780
4781 if (sortkey != lastsortkey) {
4782 m53 = g_new0 (struct mhod53_entry, 1);
4783 m53->letter = sortkey;
4784 m53->start = mhod53index;
4785 m53->count = 0;
4786 mhod->mhod53_list = g_list_append (mhod->mhod53_list, m53);
4787 lastsortkey = sortkey;
4788 }
4789 mhod53index++;
4790 g_assert (m53 != NULL);
4791 m53->count++;
4792 }
4793 }
4794 break;
4795 case MHOD_ID_LIBPLAYLISTJUMPTABLE:
4796 {
4797 GList *gl;
4798 guint32 numentries;
4799 struct mhod53_entry *m53;
4800
4801 numentries = g_list_length (mhod->mhod53_list);
4802 put_header (cts, "mhod"); /* header */
4803 put32lint (cts, 24); /* size of header */
4804 put32lint (cts, 12*numentries+40); /* size of header + body */
4805 put32lint (cts, mhod->type); /* type of the mhod */
4806 put32_n0 (cts, 2); /* unknown */
4807 put32lint (cts, mhod->mhod52sorttype); /* sort type */
4808 put32lint (cts, numentries); /* number of entries */
4809 put32_n0 (cts, 2); /* unknown */
4810
4811 for (gl=mhod->mhod53_list; gl; gl=gl->next)
4812 {
4813 m53 = gl->data;
4814 put16lint (cts, m53->letter);
4815 put16lint (cts, 0);
4816 put32lint (cts, m53->start);
4817 put32lint (cts, m53->count);
4818 }
4819 }
4820 break;
4821 }
4822 }
4823
mk_mhia(gpointer key,gpointer value,gpointer user_data)4824 static void mk_mhia (gpointer key, gpointer value, gpointer user_data)
4825 {
4826 FExport *fexp;
4827 WContents *cts;
4828 Itdb_Track *track;
4829 Itdb_Item_Id *id;
4830 MHODData mhod;
4831 guint mhod_num;
4832 gulong mhia_seek;
4833
4834 track = (Itdb_Track *)key;
4835 g_return_if_fail (track != NULL);
4836
4837 id = (Itdb_Item_Id *)value;
4838 g_return_if_fail (id != NULL);
4839
4840 fexp = (FExport *)user_data;
4841 g_return_if_fail (fexp);
4842 g_return_if_fail (fexp->wcontents);
4843 cts = fexp->wcontents;
4844 mhia_seek = cts->pos;
4845
4846 put_header (cts, "mhia"); /* header */
4847 put32lint (cts, 88); /* size of header */
4848 put32lint (cts, -1); /* total size -> later */
4849 put32lint (cts, 2); /* number of children mhods */
4850 put32lint (cts, id->id); /* album id */
4851 put64lint (cts, id->sql_id); /* id used in the sqlite DB */
4852 put32lint (cts, 2); /* unknown */
4853 put32_n0 (cts, 14); /* padding */
4854
4855 mhod.valid = TRUE;
4856 mhod_num = 0;
4857 if (track->album && *track->album) {
4858 mhod.type = MHOD_ID_ALBUM_ALBUM;
4859 mhod.data.string = track->album;
4860 mk_mhod (fexp, &mhod);
4861 ++mhod_num;
4862 }
4863
4864 if (track->albumartist && *track->albumartist) {
4865 mhod.type = MHOD_ID_ALBUM_ARTIST;
4866 mhod.data.string = track->albumartist;
4867 mk_mhod (fexp, &mhod);
4868 ++mhod_num;
4869 } else if (track->artist && *track->artist) {
4870 mhod.type = MHOD_ID_ALBUM_ARTIST;
4871 mhod.data.string = track->artist;
4872 mk_mhod (fexp, &mhod);
4873 ++mhod_num;
4874 }
4875
4876 if (track->sort_albumartist && *track->sort_albumartist) {
4877 mhod.type = MHOD_ID_ALBUM_SORT_ARTIST;
4878 mhod.data.string = track->sort_albumartist;
4879 mk_mhod (fexp, &mhod);
4880 ++mhod_num;
4881 } else if (track->sort_artist && *track->sort_artist) {
4882 mhod.type = MHOD_ID_ALBUM_SORT_ARTIST;
4883 mhod.data.string = track->sort_artist;
4884 mk_mhod (fexp, &mhod);
4885 ++mhod_num;
4886 }
4887 fix_mhit (cts, mhia_seek, mhod_num);
4888 }
4889
mk_mhla(FExport * fexp)4890 static void mk_mhla (FExport *fexp)
4891 {
4892 WContents *cts;
4893
4894 g_return_if_fail (fexp);
4895 g_return_if_fail (fexp->wcontents);
4896 g_return_if_fail (fexp->albums);
4897
4898 cts = fexp->wcontents;
4899
4900 put_header (cts, "mhla"); /* header */
4901 put32lint (cts, 92); /* size of header */
4902 /* albums on iPod */
4903 put32lint (cts, g_hash_table_size (fexp->albums));
4904 put32_n0 (cts, 20); /* dummy space */
4905 g_hash_table_foreach (fexp->albums, mk_mhia, fexp);
4906 }
4907
4908 /* Write out the mhlp header. Size will be written later */
mk_mhlp(FExport * fexp)4909 static void mk_mhlp (FExport *fexp)
4910 {
4911 WContents *cts;
4912
4913 g_return_if_fail (fexp);
4914 g_return_if_fail (fexp->wcontents);
4915
4916 cts = fexp->wcontents;
4917
4918 put_header (cts, "mhlp"); /* header */
4919 put32lint (cts, 92); /* size of header */
4920 /* playlists on iPod (including main!) */
4921 put32lint (cts, -1); /* filled in later */
4922 put32_n0 (cts, 20); /* dummy space */
4923 }
4924
mk_mhii(gpointer key,gpointer value,gpointer user_data)4925 static void mk_mhii (gpointer key, gpointer value, gpointer user_data)
4926 {
4927 FExport *fexp;
4928 WContents *cts;
4929 Itdb_Track *track;
4930 Itdb_Item_Id *id;
4931 MHODData mhod;
4932 guint mhod_num;
4933 gulong mhii_seek;
4934
4935 track = (Itdb_Track *)key;
4936 g_return_if_fail (track != NULL);
4937
4938 id = (Itdb_Item_Id *)value;
4939 g_return_if_fail (id != NULL);
4940
4941 fexp = (FExport *)user_data;
4942 g_return_if_fail (fexp);
4943 g_return_if_fail (fexp->wcontents);
4944 cts = fexp->wcontents;
4945 mhii_seek = cts->pos;
4946
4947 put_header (cts, "mhii"); /* header */
4948 put32lint (cts, 80); /* size of header */
4949 put32lint (cts, -1); /* total size -> later */
4950 put32lint (cts, 1); /* number of children mhods */
4951 put32lint (cts, id->id); /* artist id */
4952 put64lint (cts, id->sql_id); /* artist SQL id */
4953 put32lint (cts, 2); /* unknown */
4954 put32_n0 (cts, 12); /* padding */
4955
4956 mhod.valid = TRUE;
4957 mhod_num = 0;
4958
4959 if (track->artist && *track->artist) {
4960 mhod.type = MHOD_ID_ALBUM_ARTIST_MHII;
4961 mhod.data.string = track->artist;
4962 mk_mhod (fexp, &mhod);
4963 ++mhod_num;
4964 }
4965
4966 fix_mhit (cts, mhii_seek, mhod_num);
4967 }
4968
mk_mhli(FExport * fexp)4969 static void mk_mhli (FExport *fexp)
4970 {
4971 WContents *cts;
4972
4973 g_return_if_fail (fexp);
4974 g_return_if_fail (fexp->wcontents);
4975 g_return_if_fail (fexp->artists);
4976
4977 cts = fexp->wcontents;
4978
4979 put_header (cts, "mhli"); /* header */
4980 put32lint (cts, 92); /* size of header */
4981 /* albums on iPod (including main!) */
4982 put32lint (cts, g_hash_table_size (fexp->artists));
4983 put32_n0 (cts, 20); /* dummy space */
4984 g_hash_table_foreach (fexp->artists, mk_mhii, fexp);
4985 }
4986
4987 /* Write out the long MHOD_ID_PLAYLIST mhod header.
4988 This seems to be an itunespref thing.. dunno know this
4989 but if we set everything to 0, itunes doesn't show any data
4990 even if you drag an mp3 to your ipod: nothing is shown, but itunes
4991 will copy the file!
4992 .. so we create a hardcoded-pref.. this will change in future
4993 Seems to be a Preferences mhod, every PL has such a thing
4994 FIXME !!! */
mk_long_mhod_id_playlist(FExport * fexp,Itdb_Playlist * pl)4995 static void mk_long_mhod_id_playlist (FExport *fexp, Itdb_Playlist *pl)
4996 {
4997 WContents *cts;
4998
4999 g_return_if_fail (fexp);
5000 g_return_if_fail (fexp->wcontents);
5001 g_return_if_fail (pl);
5002
5003 cts = fexp->wcontents;
5004
5005 put_header (cts, "mhod"); /* header */
5006 put32lint (cts, 0x18); /* size of header ? */
5007 put32lint (cts, 0x0288); /* size of header + body */
5008 put32lint (cts, MHOD_ID_PLAYLIST); /* type of the entry */
5009 put32_n0 (cts, 6);
5010 put32lint (cts, 0x010084); /* ? */
5011 put32lint (cts, 0x05); /* ? */
5012 put32lint (cts, 0x09); /* ? */
5013 put32lint (cts, 0x03); /* ? */
5014 put32lint (cts, 0x120001); /* ? */
5015 put32_n0 (cts, 3);
5016 put32lint (cts, 0xc80002); /* ? */
5017 put32_n0 (cts, 3);
5018 put32lint (cts, 0x3c000d); /* ? */
5019 put32_n0 (cts, 3);
5020 put32lint (cts, 0x7d0004); /* ? */
5021 put32_n0 (cts, 3);
5022 put32lint (cts, 0x7d0003); /* ? */
5023 put32_n0 (cts, 3);
5024 put32lint (cts, 0x640008); /* ? */
5025 put32_n0 (cts, 3);
5026 put32lint (cts, 0x640017); /* ? */
5027 put32lint (cts, 0x01); /* bool? (visible? / colums?) */
5028 put32_n0 (cts, 2);
5029 put32lint (cts, 0x500014); /* ? */
5030 put32lint (cts, 0x01); /* bool? (visible?) */
5031 put32_n0 (cts, 2);
5032 put32lint (cts, 0x7d0015); /* ? */
5033 put32lint (cts, 0x01); /* bool? (visible?) */
5034 put32_n0 (cts, 114);
5035 }
5036
5037
5038
5039 /* Header for new PL item */
5040 /* @pos: position in playlist */
mk_mhip(FExport * fexp,guint32 childcount,guint32 podcastgroupflag,guint32 podcastgroupid,guint32 trackid,guint32 timestamp,guint32 podcastgroupref)5041 static void mk_mhip (FExport *fexp,
5042 guint32 childcount,
5043 guint32 podcastgroupflag,
5044 guint32 podcastgroupid,
5045 guint32 trackid,
5046 guint32 timestamp,
5047 guint32 podcastgroupref)
5048 {
5049 WContents *cts;
5050
5051 g_return_if_fail (fexp);
5052 g_return_if_fail (fexp->wcontents);
5053
5054 cts = fexp->wcontents;
5055
5056 put_header (cts, "mhip");
5057 put32lint (cts, 76); /* 4 */
5058 put32lint (cts, -1); /* fill in later */ /* 8 */
5059 put32lint (cts, childcount); /* 12 */
5060 put32lint (cts, podcastgroupflag); /* 16 */
5061 put32lint (cts, podcastgroupid); /* 20 */
5062 put32lint (cts, trackid); /* 24 */
5063 timestamp = device_time_time_t_to_mac (fexp->itdb->device, timestamp);
5064 put32lint (cts, timestamp); /* 28 */
5065 put32lint (cts, podcastgroupref); /* 32 */
5066 put32_n0 (cts, 10); /* 36 */
5067 }
5068
5069
5070 /* Write first mhsd hunk. Return FALSE in case of error and set
5071 * fexp->error */
write_mhsd_tracks(FExport * fexp)5072 static gboolean write_mhsd_tracks (FExport *fexp)
5073 {
5074 GList *gl;
5075 gulong mhsd_seek;
5076 WContents *cts;
5077
5078 g_return_val_if_fail (fexp, FALSE);
5079 g_return_val_if_fail (fexp->itdb, FALSE);
5080 g_return_val_if_fail (fexp->wcontents, FALSE);
5081
5082 cts = fexp->wcontents;
5083
5084 mhsd_seek = cts->pos; /* get position of mhsd header */
5085 mk_mhsd (fexp, 1); /* write header: type 1: tracks */
5086 /* write header with nr. of tracks */
5087 mk_mhlt (fexp, g_list_length (fexp->itdb->tracks));
5088 for (gl=fexp->itdb->tracks; gl; gl=gl->next) /* Write each track */
5089 {
5090 Itdb_Track *track = gl->data;
5091 guint32 mhod_num = 0;
5092 gulong mhit_seek = cts->pos;
5093 MHODData mhod;
5094
5095 g_return_val_if_fail (track, FALSE);
5096
5097 mhod.valid = TRUE;
5098
5099 mk_mhit (cts, track);
5100 if (track->title && *track->title)
5101 {
5102 mhod.type = MHOD_ID_TITLE;
5103 mhod.data.string = track->title;
5104 mk_mhod (fexp, &mhod);
5105 ++mhod_num;
5106 }
5107 if (track->artist && *track->artist)
5108 {
5109 mhod.type = MHOD_ID_ARTIST;
5110 mhod.data.string = track->artist;
5111 mk_mhod (fexp, &mhod);
5112 ++mhod_num;
5113 }
5114 if (track->album && *track->album)
5115 {
5116 mhod.type = MHOD_ID_ALBUM;
5117 mhod.data.string = track->album;
5118 mk_mhod (fexp, &mhod);
5119 ++mhod_num;
5120 }
5121 if (track->filetype && *track->filetype)
5122 {
5123 mhod.type = MHOD_ID_FILETYPE;
5124 mhod.data.string = track->filetype;
5125 mk_mhod (fexp, &mhod);
5126 ++mhod_num;
5127 }
5128 if (track->comment && *track->comment)
5129 {
5130 mhod.type = MHOD_ID_COMMENT;
5131 mhod.data.string = track->comment;
5132 mk_mhod (fexp, &mhod);
5133 ++mhod_num;
5134 }
5135 if (track->ipod_path && *track->ipod_path)
5136 {
5137 mhod.type = MHOD_ID_PATH;
5138 mhod.data.string = track->ipod_path;
5139 mk_mhod (fexp, &mhod);
5140 ++mhod_num;
5141 }
5142 if (track->genre && *track->genre)
5143 {
5144 mhod.type = MHOD_ID_GENRE;
5145 mhod.data.string = track->genre;
5146 mk_mhod (fexp, &mhod);
5147 ++mhod_num;
5148 }
5149 if (track->category && *track->category)
5150 {
5151 mhod.type = MHOD_ID_CATEGORY;
5152 mhod.data.string = track->category;
5153 mk_mhod (fexp, &mhod);
5154 ++mhod_num;
5155 }
5156 if (track->composer && *track->composer)
5157 {
5158 mhod.type = MHOD_ID_COMPOSER;
5159 mhod.data.string = track->composer;
5160 mk_mhod (fexp, &mhod);
5161 ++mhod_num;
5162 }
5163 if (track->grouping && *track->grouping)
5164 {
5165 mhod.type = MHOD_ID_GROUPING;
5166 mhod.data.string = track->grouping;
5167 mk_mhod (fexp, &mhod);
5168 ++mhod_num;
5169 }
5170 if (track->description && *track->description)
5171 {
5172 mhod.type = MHOD_ID_DESCRIPTION;
5173 mhod.data.string = track->description;
5174 mk_mhod (fexp, &mhod);
5175 ++mhod_num;
5176 }
5177 if (track->subtitle && *track->subtitle)
5178 {
5179 mhod.type = MHOD_ID_SUBTITLE;
5180 mhod.data.string = track->subtitle;
5181 mk_mhod (fexp, &mhod);
5182 ++mhod_num;
5183 }
5184 if (track->tvshow && *track->tvshow)
5185 {
5186 mhod.type = MHOD_ID_TVSHOW;
5187 mhod.data.string = track->tvshow;
5188 mk_mhod (fexp, &mhod);
5189 ++mhod_num;
5190 }
5191 if (track->tvepisode && *track->tvepisode)
5192 {
5193 mhod.type = MHOD_ID_TVEPISODE;
5194 mhod.data.string = track->tvepisode;
5195 mk_mhod (fexp, &mhod);
5196 ++mhod_num;
5197 }
5198 if (track->tvnetwork && *track->tvnetwork)
5199 {
5200 mhod.type = MHOD_ID_TVNETWORK;
5201 mhod.data.string = track->tvnetwork;
5202 mk_mhod (fexp, &mhod);
5203 ++mhod_num;
5204 }
5205 if (track->albumartist && *track->albumartist)
5206 {
5207 mhod.type = MHOD_ID_ALBUMARTIST;
5208 mhod.data.string = track->albumartist;
5209 mk_mhod (fexp, &mhod);
5210 ++mhod_num;
5211 }
5212 if (track->keywords && *track->keywords)
5213 {
5214 mhod.type = MHOD_ID_KEYWORDS;
5215 mhod.data.string = track->keywords;
5216 mk_mhod (fexp, &mhod);
5217 ++mhod_num;
5218 }
5219 if (track->podcasturl && *track->podcasturl)
5220 {
5221 mhod.type = MHOD_ID_PODCASTURL;
5222 mhod.data.string = track->podcasturl;
5223 mk_mhod (fexp, &mhod);
5224 ++mhod_num;
5225 }
5226 if (track->podcastrss && *track->podcastrss)
5227 {
5228 mhod.type = MHOD_ID_PODCASTRSS;
5229 mhod.data.string = track->podcastrss;
5230 mk_mhod (fexp, &mhod);
5231 ++mhod_num;
5232 }
5233 if (track->sort_artist && *track->sort_artist)
5234 {
5235 mhod.type = MHOD_ID_SORT_ARTIST;
5236 mhod.data.string = track->sort_artist;
5237 mk_mhod (fexp, &mhod);
5238 ++mhod_num;
5239 }
5240 if (track->sort_title && *track->sort_title)
5241 {
5242 mhod.type = MHOD_ID_SORT_TITLE;
5243 mhod.data.string = track->sort_title;
5244 mk_mhod (fexp, &mhod);
5245 ++mhod_num;
5246 }
5247 if (track->sort_album && *track->sort_album)
5248 {
5249 mhod.type = MHOD_ID_SORT_ALBUM;
5250 mhod.data.string = track->sort_album;
5251 mk_mhod (fexp, &mhod);
5252 ++mhod_num;
5253 }
5254 if (track->sort_albumartist && *track->sort_albumartist)
5255 {
5256 mhod.type = MHOD_ID_SORT_ALBUMARTIST;
5257 mhod.data.string = track->sort_albumartist;
5258 mk_mhod (fexp, &mhod);
5259 ++mhod_num;
5260 }
5261 if (track->sort_composer && *track->sort_composer)
5262 {
5263 mhod.type = MHOD_ID_SORT_COMPOSER;
5264 mhod.data.string = track->sort_composer;
5265 mk_mhod (fexp, &mhod);
5266 ++mhod_num;
5267 }
5268 if (track->sort_tvshow && *track->sort_tvshow)
5269 {
5270 mhod.type = MHOD_ID_SORT_TVSHOW;
5271 mhod.data.string = track->sort_tvshow;
5272 mk_mhod (fexp, &mhod);
5273 ++mhod_num;
5274 }
5275 if (track->chapterdata && track->chapterdata->chapters)
5276 {
5277 mhod.type = MHOD_ID_CHAPTERDATA;
5278 mhod.data.chapterdata = track->chapterdata;
5279 mk_mhod (fexp, &mhod);
5280 ++mhod_num;
5281 }
5282 /* Fill in the missing items of the mhit header */
5283 fix_mhit (cts, mhit_seek, mhod_num);
5284 }
5285 fix_header (cts, mhsd_seek);
5286 return TRUE;
5287 }
5288
5289
5290 /* write out the mhip/mhod pairs for playlist @pl. @mhyp_seek points
5291 to the start of the corresponding mhyp, but is not used yet. */
write_playlist_mhips(FExport * fexp,Itdb_Playlist * pl,glong mhyp_seek)5292 static gboolean write_playlist_mhips (FExport *fexp,
5293 Itdb_Playlist *pl,
5294 glong mhyp_seek)
5295 {
5296 GList *gl;
5297 WContents *cts;
5298 guint32 i=0;
5299 guint32 mhip_num;
5300
5301 g_return_val_if_fail (fexp, FALSE);
5302 g_return_val_if_fail (fexp->itdb, FALSE);
5303 g_return_val_if_fail (fexp->wcontents, FALSE);
5304 g_return_val_if_fail (pl, FALSE);
5305
5306 cts = fexp->wcontents;
5307
5308 for (gl=pl->members; gl; gl=gl->next)
5309 {
5310 Itdb_Track *track = gl->data;
5311 glong mhip_seek = cts->pos;
5312 MHODData mhod;
5313
5314 g_return_val_if_fail (track, FALSE);
5315
5316 mk_mhip (fexp, 1, 0, 0, track->id, 0, 0);
5317 mhod.valid = TRUE;
5318 mhod.type = MHOD_ID_PLAYLIST;
5319 mhod.data.track_pos = i;
5320 mk_mhod (fexp, &mhod);
5321 /* note: with iTunes 4.9 the mhod is counted as a child to
5322 mhip, so we have put the total length of the mhip and mhod
5323 into the mhip header */
5324 fix_header (cts, mhip_seek);
5325 ++i;
5326 }
5327
5328 /* set number of mhips */
5329 mhip_num = g_list_length (pl->members);
5330 put32lint_seek (cts, mhip_num, mhyp_seek+16);
5331
5332 return TRUE;
5333 }
5334
5335
5336 static void free_memberlist (gpointer data);
5337 static void write_one_podcast_group (gpointer key, gpointer value, gpointer userdata);
free_memberlist(gpointer data)5338 static void free_memberlist (gpointer data)
5339 {
5340 GList **memberlist = data;
5341 if (memberlist)
5342 {
5343 g_list_free (*memberlist);
5344 g_free (memberlist);
5345 }
5346 }
write_one_podcast_group(gpointer key,gpointer value,gpointer userdata)5347 static void write_one_podcast_group (gpointer key, gpointer value,
5348 gpointer userdata)
5349 {
5350 gchar *album = key;
5351 GList **memberlist = value;
5352 FExport *fexp = userdata;
5353 GList *gl;
5354 WContents *cts;
5355 glong mhip_seek;
5356 guint32 groupid;
5357 MHODData mhod;
5358
5359 g_return_if_fail (album);
5360 g_return_if_fail (memberlist);
5361 g_return_if_fail (fexp);
5362 g_return_if_fail (fexp->itdb);
5363 g_return_if_fail (fexp->wcontents);
5364
5365 cts = fexp->wcontents;
5366 mhip_seek = cts->pos;
5367
5368 groupid = fexp->next_id++;
5369 mk_mhip (fexp, 1, 256, groupid, 0, 0, 0);
5370
5371 mhod.valid = TRUE;
5372 mhod.type = MHOD_ID_TITLE;
5373 mhod.data.string = album;
5374 mk_mhod (fexp, &mhod);
5375 fix_header (cts, mhip_seek);
5376
5377 /* write members */
5378 for (gl=*memberlist; gl; gl=gl->next)
5379 {
5380 Itdb_Track *track = gl->data;
5381 guint32 mhip_id;
5382
5383 g_return_if_fail (track);
5384
5385 mhip_seek = cts->pos;
5386 mhip_id = fexp->next_id++;
5387 mk_mhip (fexp, 1, 0, mhip_id, track->id, 0, groupid);
5388 mhod.type = MHOD_ID_PLAYLIST;
5389 mhod.data.track_pos = mhip_id;
5390 mk_mhod (fexp, &mhod);
5391 fix_header (cts, mhip_seek);
5392 }
5393 }
5394
5395
5396
5397 /* write out the mhip/mhod pairs for the podcast playlist
5398 @pl. @mhyp_seek points to the start of the corresponding mhyp, but
5399 is not used yet. */
write_podcast_mhips(FExport * fexp,Itdb_Playlist * pl,glong mhyp_seek)5400 static gboolean write_podcast_mhips (FExport *fexp,
5401 Itdb_Playlist *pl,
5402 glong mhyp_seek)
5403 {
5404 GList *gl;
5405 WContents *cts;
5406 guint32 mhip_num;
5407 GHashTable *album_hash;
5408
5409 g_return_val_if_fail (fexp, FALSE);
5410 g_return_val_if_fail (fexp->itdb, FALSE);
5411 g_return_val_if_fail (fexp->wcontents, FALSE);
5412 g_return_val_if_fail (pl, FALSE);
5413
5414 cts = fexp->wcontents;
5415
5416 /* Create a list with all available album names because we have to
5417 group the podcasts according to albums */
5418 album_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
5419 NULL, free_memberlist);
5420 for (gl=pl->members; gl; gl=gl->next)
5421 {
5422 GList **memberlist;
5423 gchar *album;
5424 Itdb_Track *track = gl->data;
5425 g_return_val_if_fail (track, FALSE);
5426
5427 if (track->album)
5428 album = track->album;
5429 else
5430 album = "";
5431
5432 memberlist = g_hash_table_lookup (album_hash, album);
5433
5434 if (!memberlist)
5435 {
5436 memberlist = g_new0 (GList *, 1);
5437 g_hash_table_insert (album_hash, album, memberlist);
5438 }
5439 *memberlist = g_list_append (*memberlist, track);
5440 }
5441
5442 g_hash_table_foreach (album_hash, write_one_podcast_group, fexp);
5443
5444 /* set number of mhips */
5445 mhip_num = g_list_length (pl->members)+g_hash_table_size (album_hash);
5446 put32lint_seek (cts, mhip_num, mhyp_seek+16);
5447
5448 g_hash_table_destroy (album_hash);
5449
5450 return TRUE;
5451 }
5452
5453 /* Write one type 52 and the corresponding type 53 mhod */
mk_mhod52(enum MHOD52_SORTTYPE mhod52sorttype,FExport * fexp,MHODData * mhod)5454 static void mk_mhod52 (enum MHOD52_SORTTYPE mhod52sorttype, FExport *fexp,
5455 MHODData *mhod)
5456 {
5457 mhod->mhod52sorttype = mhod52sorttype;
5458 mhod->type = MHOD_ID_LIBPLAYLISTINDEX;
5459 mk_mhod (fexp, mhod);
5460 }
5461
mk_mhod53(enum MHOD52_SORTTYPE mhod52sorttype,FExport * fexp,MHODData * mhod)5462 static void mk_mhod53 (enum MHOD52_SORTTYPE mhod52sorttype, FExport *fexp,
5463 MHODData *mhod)
5464 {
5465 mhod->type = MHOD_ID_LIBPLAYLISTJUMPTABLE;
5466 mk_mhod (fexp, mhod);
5467 g_list_foreach (mhod->mhod53_list, (GFunc)g_free, NULL);
5468 g_list_free (mhod->mhod53_list);
5469 mhod->mhod53_list = NULL;
5470 }
5471
5472
5473 /* corresponds to mk_mhyp */
5474 /* mhsd_type: 2: write normal playlists
5475 3: write podcast playlist in special format */
5476 /* Return FALSE in case of error and set fexp->error */
write_playlist(FExport * fexp,Itdb_Playlist * pl,guint32 mhsd_type)5477 static gboolean write_playlist (FExport *fexp,
5478 Itdb_Playlist *pl, guint32 mhsd_type)
5479 {
5480 gulong mhyp_seek;
5481 WContents *cts;
5482 gboolean result = TRUE;
5483 MHODData mhod;
5484 gint mhodnum;
5485 guint32 mac_time;
5486
5487 g_return_val_if_fail (fexp, FALSE);
5488 g_return_val_if_fail (fexp->itdb, FALSE);
5489 g_return_val_if_fail (fexp->wcontents, FALSE);
5490 g_return_val_if_fail (pl, FALSE);
5491
5492 cts = fexp->wcontents;
5493 mhyp_seek = cts->pos;
5494
5495 #if ITUNESDB_DEBUG
5496 fprintf(stderr, "Playlist: %s (%d tracks)\n", pl->name, g_list_length (pl->members));
5497 #endif
5498
5499 put_header (cts, "mhyp"); /* header */
5500 put32lint (cts, 108); /* length */
5501 put32lint (cts, -1); /* size -> later */
5502 mhodnum = 2;
5503 if (pl->is_spl)
5504 { /* 2 more SPL MHODs */
5505 mhodnum += 2;
5506 }
5507 else
5508 {
5509 if ((pl->type == ITDB_PL_TYPE_MPL) && pl->members)
5510 { /* 5 more MHOD 52 lists and 5 more MHOD 53 lists*/
5511 mhodnum += 10;
5512 }
5513 }
5514 put32lint (cts, mhodnum); /* nr of mhods */
5515 /* number of tracks in plist */
5516 put32lint (cts, -1); /* number of mhips -> later */
5517 put8int (cts, pl->type); /* 1 = main, 0 = visible */
5518 put8int (cts, pl->flag1); /* unknown */
5519 put8int (cts, pl->flag2); /* unknown */
5520 put8int (cts, pl->flag3); /* unknown */
5521 mac_time = device_time_time_t_to_mac (fexp->itdb->device, pl->timestamp);
5522 put32lint (cts, mac_time); /* some timestamp */
5523 put64lint (cts, pl->id); /* 64 bit ID */
5524 put32lint (cts, 0); /* unknown, always 0? */
5525 put16lint (cts, 1); /* string mhod count (1) */
5526 put16lint (cts, pl->podcastflag);
5527 put32lint (cts, pl->sortorder);
5528 if (mhsd_type == 5) {
5529 put32_n0 (cts, 8); /* ? */
5530 /* +0x50 */
5531 put16lint (cts, pl->priv->mhsd5_type);
5532 put16lint (cts, pl->priv->mhsd5_type); /* same as 0x50 ? */
5533 /* +0x54 */
5534 if ((pl->priv->mhsd5_type == ITDB_PLAYLIST_MHSD5_MOVIE_RENTALS)
5535 || (pl->priv->mhsd5_type == ITDB_PLAYLIST_MHSD5_RINGTONES)){
5536 put32lint (cts, 1); /* unknown, 1 for Movie rentals + Ringtones */
5537 } else {
5538 put32lint (cts, 0); /* 0 otherwise */
5539 }
5540 put32_n0 (cts, 5); /* ? */
5541
5542
5543 } else {
5544 put32_n0 (cts, 15); /* ? */
5545 }
5546
5547 mhod.valid = TRUE;
5548 mhod.type = MHOD_ID_TITLE;
5549 mhod.data.string = pl->name;
5550 mk_mhod (fexp, &mhod);
5551 mk_long_mhod_id_playlist (fexp, pl);
5552
5553 if ((pl->type == ITDB_PL_TYPE_MPL) && pl->members)
5554 { /* write out the MHOD 52 and MHOD 53 lists */
5555 /* We have to sort all tracks five times. To speed this up,
5556 translate the utf8 keys into collate_keys and use the
5557 faster strcmp() for comparison */
5558 mhod.valid = TRUE;
5559 mhod.data.mhod52coltracks = mhod52_make_collate_keys (pl->members);
5560 mhod.mhod53_list = NULL;
5561
5562 mk_mhod52 (MHOD52_SORTTYPE_TITLE, fexp, &mhod);
5563 mk_mhod53 (MHOD52_SORTTYPE_TITLE, fexp, &mhod);
5564 mk_mhod52 (MHOD52_SORTTYPE_ARTIST, fexp, &mhod);
5565 mk_mhod53 (MHOD52_SORTTYPE_ARTIST, fexp, &mhod);
5566 mk_mhod52 (MHOD52_SORTTYPE_ALBUM, fexp, &mhod);
5567 mk_mhod53 (MHOD52_SORTTYPE_ALBUM, fexp, &mhod);
5568 mk_mhod52 (MHOD52_SORTTYPE_GENRE, fexp, &mhod);
5569 mk_mhod53 (MHOD52_SORTTYPE_GENRE, fexp, &mhod);
5570 mk_mhod52 (MHOD52_SORTTYPE_COMPOSER, fexp, &mhod);
5571 mk_mhod53 (MHOD52_SORTTYPE_COMPOSER, fexp, &mhod);
5572
5573 mhod52_free_collate_keys (mhod.data.mhod52coltracks);
5574 }
5575 else if (pl->is_spl)
5576 { /* write the smart rules */
5577 mhod.type = MHOD_ID_SPLPREF;
5578 mhod.data.splpref = &pl->splpref;
5579 mk_mhod (fexp, &mhod);
5580
5581 mhod.type = MHOD_ID_SPLRULES;
5582 mhod.data.splrules = &pl->splrules;
5583 mk_mhod (fexp, &mhod);
5584 }
5585
5586 if (itdb_playlist_is_podcasts(pl) && (mhsd_type == 3))
5587 {
5588 /* write special podcast playlist */
5589 result = write_podcast_mhips (fexp, pl, mhyp_seek);
5590 }
5591 else if (mhsd_type == 5)
5592 {
5593 /* set number of mhips to 0 for mhsd_type 5 */
5594 put32lint_seek (cts, 0, mhyp_seek+16);
5595 }
5596 else
5597 {
5598 /* write standard playlist hard-coded tracks */
5599 result = write_playlist_mhips (fexp, pl, mhyp_seek);
5600 }
5601 fix_header (cts, mhyp_seek);
5602 return result;
5603 }
5604
5605
5606 /* Expects the master playlist to be the first in the list */
5607 /* Return FALSE in case of error and set fexp->error */
write_mhsd_playlists(FExport * fexp,guint32 mhsd_type)5608 static gboolean write_mhsd_playlists (FExport *fexp, guint32 mhsd_type)
5609 {
5610 GList *gl;
5611 glong mhsd_seek;
5612 glong mhlp_seek;
5613 WContents *cts;
5614 int plcnt = 0;
5615 GList *playlists;
5616
5617 g_return_val_if_fail (fexp, FALSE);
5618 g_return_val_if_fail (fexp->itdb, FALSE);
5619 g_return_val_if_fail (fexp->wcontents, FALSE);
5620 g_return_val_if_fail ((mhsd_type == 2) || (mhsd_type == 3) || (mhsd_type == 5), FALSE);
5621
5622 cts = fexp->wcontents;
5623 mhsd_seek = cts->pos; /* get position of mhsd header */
5624 mk_mhsd (fexp, mhsd_type); /* write header */
5625 /* write header with nr. of playlists */
5626 mhlp_seek = cts->pos;
5627 mk_mhlp (fexp);
5628 if (mhsd_type == 5) {
5629 playlists = fexp->itdb->priv->mhsd5_playlists;
5630 } else {
5631 playlists = fexp->itdb->playlists;
5632 }
5633 for (gl=playlists; gl; gl=gl->next)
5634 {
5635 Itdb_Playlist *pl = gl->data;
5636 g_return_val_if_fail (pl, FALSE);
5637
5638 write_playlist (fexp, pl, mhsd_type);
5639 plcnt++;
5640 if (fexp->error) return FALSE;
5641 }
5642
5643 put32lint_seek (cts, plcnt, mhlp_seek+8);
5644
5645 fix_header (cts, mhsd_seek);
5646 return TRUE;
5647 }
5648
write_mhsd_albums(FExport * fexp)5649 static gboolean write_mhsd_albums (FExport *fexp)
5650 {
5651 gulong mhsd_seek;
5652 WContents *cts;
5653
5654 g_return_val_if_fail (fexp, FALSE);
5655 g_return_val_if_fail (fexp->itdb, FALSE);
5656 g_return_val_if_fail (fexp->wcontents, FALSE);
5657
5658 cts = fexp->wcontents;
5659 mhsd_seek = cts->pos; /* get position of mhsd header */
5660 mk_mhsd (fexp, 4); /* write header */
5661 mk_mhla (fexp);
5662 fix_header (cts, mhsd_seek);
5663 return TRUE;
5664 }
5665
write_mhsd_artists(FExport * fexp)5666 static gboolean write_mhsd_artists (FExport *fexp)
5667 {
5668 gulong mhsd_seek;
5669 WContents *cts;
5670
5671 g_return_val_if_fail (fexp, FALSE);
5672 g_return_val_if_fail (fexp->itdb, FALSE);
5673 g_return_val_if_fail (fexp->wcontents, FALSE);
5674
5675 cts = fexp->wcontents;
5676 mhsd_seek = cts->pos; /* get position of mhsd header */
5677 mk_mhsd (fexp, 8); /* write header */
5678 mk_mhli (fexp);
5679 fix_header (cts, mhsd_seek);
5680 return TRUE;
5681 }
5682
write_mhsd_type6(FExport * fexp)5683 static gboolean write_mhsd_type6 (FExport *fexp)
5684 {
5685 gulong mhsd_seek;
5686 WContents *cts;
5687
5688 g_return_val_if_fail (fexp, FALSE);
5689 g_return_val_if_fail (fexp->itdb, FALSE);
5690 g_return_val_if_fail (fexp->wcontents, FALSE);
5691
5692 cts = fexp->wcontents;
5693 mhsd_seek = cts->pos; /* get position of mhsd header */
5694 mk_mhsd (fexp, 6); /* write header */
5695 mk_mhlt (fexp, 0); /* for now, produce an empty set */
5696 fix_header (cts, mhsd_seek);
5697 return TRUE;
5698 }
5699
write_genius_mhsd(FExport * fexp)5700 static gboolean write_genius_mhsd (FExport *fexp)
5701 {
5702 gulong mhsd_seek;
5703 WContents *cts;
5704
5705 g_return_val_if_fail (fexp, FALSE);
5706 g_return_val_if_fail (fexp->itdb, FALSE);
5707 g_return_val_if_fail (fexp->wcontents, FALSE);
5708
5709 if (!fexp->itdb->priv->genius_cuid) {
5710 return TRUE;
5711 }
5712 cts = fexp->wcontents;
5713 mhsd_seek = cts->pos; /* get position of mhsd header */
5714 mk_mhsd (fexp, 9); /* write header */
5715 put_string (cts, fexp->itdb->priv->genius_cuid);
5716 fix_header (cts, mhsd_seek);
5717 return TRUE;
5718 }
5719
write_mhsd_type10(FExport * fexp)5720 static gboolean write_mhsd_type10 (FExport *fexp)
5721 {
5722 gulong mhsd_seek;
5723 WContents *cts;
5724
5725 g_return_val_if_fail (fexp, FALSE);
5726 g_return_val_if_fail (fexp->itdb, FALSE);
5727 g_return_val_if_fail (fexp->wcontents, FALSE);
5728
5729 cts = fexp->wcontents;
5730 mhsd_seek = cts->pos; /* get position of mhsd header */
5731 mk_mhsd (fexp, 10); /* write header */
5732 mk_mhlt (fexp, 0); /* for now, produce an empty set */
5733 fix_header (cts, mhsd_seek);
5734 return TRUE;
5735 }
5736
5737 /* create a WContents structure */
wcontents_new(const gchar * filename)5738 static WContents *wcontents_new (const gchar *filename)
5739 {
5740 WContents *cts;
5741
5742 g_return_val_if_fail (filename, NULL);
5743
5744 cts = g_new0 (WContents, 1);
5745 cts->filename = g_strdup (filename);
5746
5747 return cts;
5748 }
5749
5750
5751 /* write the contents of WContents. Return FALSE on error and set
5752 * cts->error accordingly. */
wcontents_write(WContents * cts)5753 static gboolean wcontents_write (WContents *cts)
5754 {
5755 g_return_val_if_fail (cts, FALSE);
5756 g_return_val_if_fail (cts->filename, FALSE);
5757
5758 cts->error = NULL;
5759 return g_file_set_contents (cts->filename, cts->contents,
5760 cts->pos, &cts->error);
5761 }
5762
5763
5764 /* Free memory associated with WContents @cts */
wcontents_free(WContents * cts)5765 static void wcontents_free (WContents *cts)
5766 {
5767 if (cts)
5768 {
5769 g_free (cts->filename);
5770 g_free (cts->contents);
5771 /* must not g_error_free (cts->error) because the error was
5772 propagated -> might free the error twice */
5773 g_free (cts);
5774 }
5775 }
5776
safe_str_equal(gconstpointer v1,gconstpointer v2)5777 static gboolean safe_str_equal (gconstpointer v1, gconstpointer v2)
5778 {
5779 if ((v1 == NULL) || (v2 == NULL)) {
5780 return (v1 == v2);
5781 } else {
5782 return g_str_equal (v1, v2);
5783 }
5784 }
5785
itdb_album_hash(gconstpointer v)5786 static guint itdb_album_hash (gconstpointer v)
5787 {
5788 Itdb_Track *track = (Itdb_Track *)v;
5789 if (track->album != NULL) {
5790 return g_str_hash (track->album);
5791 } else {
5792 return 0;
5793 }
5794 g_assert_not_reached ();
5795 }
5796
itdb_album_equal(gconstpointer v1,gconstpointer v2)5797 static gboolean itdb_album_equal (gconstpointer v1, gconstpointer v2)
5798 {
5799 Itdb_Track *track1 = (Itdb_Track *)v1;
5800 Itdb_Track *track2 = (Itdb_Track *)v2;
5801 gboolean same_show;
5802 gboolean same_album;
5803
5804 /*album is the same if:
5805 * tvshow is the same, and
5806 * album and albumartist are the same, or
5807 * albumartist is null and artist and album are the same
5808 */
5809 same_show = safe_str_equal (track1->tvshow, track2->tvshow);
5810 if (!same_show) {
5811 return FALSE;
5812 }
5813
5814 same_album = safe_str_equal (track1->album, track2->album);
5815 if (!same_album) {
5816 return FALSE;
5817 }
5818
5819 if ((track1->albumartist != NULL) && (track2->albumartist != NULL)) {
5820 return safe_str_equal (track1->albumartist, track2->albumartist);
5821 } else {
5822 return safe_str_equal (track1->artist, track2->artist);
5823 }
5824 }
5825
add_new_id(GHashTable * ids,Itdb_Track * track,guint new_id)5826 static void add_new_id (GHashTable *ids, Itdb_Track *track, guint new_id)
5827 {
5828 Itdb_Item_Id *id;
5829
5830 id = g_new0 (Itdb_Item_Id, 1);
5831 id->id = new_id;
5832 id->sql_id = ((guint64)g_random_int () << 32) | ((guint64)g_random_int ());
5833
5834 g_hash_table_insert (ids, track, id);
5835 }
5836
itdb_artist_hash(gconstpointer v)5837 static guint itdb_artist_hash (gconstpointer v)
5838 {
5839 Itdb_Track *track = (Itdb_Track *)v;
5840 if (track->artist != NULL) {
5841 return g_str_hash (track->artist);
5842 } else {
5843 return 0;
5844 }
5845 }
5846
itdb_artist_equal(gconstpointer v1,gconstpointer v2)5847 static gboolean itdb_artist_equal (gconstpointer v1, gconstpointer v2)
5848 {
5849 Itdb_Track *track1 = (Itdb_Track *)v1;
5850 Itdb_Track *track2 = (Itdb_Track *)v2;
5851
5852 return safe_str_equal (track1->artist, track2->artist);
5853 }
5854
itdb_composer_hash(gconstpointer v)5855 static guint itdb_composer_hash (gconstpointer v)
5856 {
5857 Itdb_Track *track = (Itdb_Track *)v;
5858 if (track->composer != NULL) {
5859 return g_str_hash (track->composer);
5860 } else {
5861 return 0;
5862 }
5863 }
5864
itdb_composer_equal(gconstpointer v1,gconstpointer v2)5865 static gboolean itdb_composer_equal (gconstpointer v1, gconstpointer v2)
5866 {
5867 Itdb_Track *track1 = (Itdb_Track *)v1;
5868 Itdb_Track *track2 = (Itdb_Track *)v2;
5869
5870 return safe_str_equal (track1->composer, track2->composer);
5871 }
5872
5873 /* - reassign the iPod IDs
5874 - make sure the itdb->tracks are in the same order as the mpl
5875 - assign album IDs to write the MHLA
5876 */
prepare_itdb_for_write(FExport * fexp)5877 static void prepare_itdb_for_write (FExport *fexp)
5878 {
5879 GList *gl;
5880 GList *pl;
5881 Itdb_iTunesDB *itdb;
5882 Itdb_Playlist *mpl;
5883 Itdb_Playlist *playlist;
5884 guint album_id = 1;
5885 guint artist_id = 1;
5886 guint composer_id = 1;
5887
5888 g_return_if_fail (fexp);
5889 itdb = fexp->itdb;
5890 g_return_if_fail (itdb);
5891
5892 /* Arrange itdb->tracks in the same order as mpl->members
5893 (otherwise On-The-Go Playlists will not show the correct
5894 content. Unfortunately we cannot just copy mpl->members to
5895 itdb->tracks because podcasts do not show up in the MPL. */
5896
5897 mpl = itdb_playlist_mpl (itdb);
5898 g_return_if_fail (mpl);
5899
5900 for (gl=g_list_last(mpl->members); gl; gl=gl->prev)
5901 {
5902 GList *link;
5903 Itdb_Track *track = gl->data;
5904 g_return_if_fail (track);
5905 link = g_list_find (itdb->tracks, track);
5906 g_return_if_fail (link);
5907
5908 /* move this track to the beginning of itdb_tracks */
5909 itdb->tracks = g_list_delete_link (itdb->tracks, link);
5910 itdb->tracks = g_list_prepend (itdb->tracks, track);
5911 }
5912
5913 fexp->next_id = FIRST_IPOD_ID;
5914
5915 g_assert (fexp->albums == NULL);
5916 fexp->albums = g_hash_table_new_full (itdb_album_hash, itdb_album_equal,
5917 NULL, g_free);
5918
5919 g_assert (fexp->artists == NULL);
5920 fexp->artists = g_hash_table_new_full (itdb_artist_hash, itdb_artist_equal,
5921 NULL, g_free);
5922
5923 g_assert (fexp->composers == NULL);
5924 fexp->composers = g_hash_table_new_full (itdb_composer_hash, itdb_composer_equal,
5925 NULL, g_free);
5926
5927 /* assign unique IDs and create sort keys */
5928 for (gl=itdb->tracks; gl; gl=gl->next)
5929 {
5930 Itdb_Track *track = gl->data;
5931 Itdb_Item_Id *id;
5932
5933 g_return_if_fail (track);
5934 track->id = fexp->next_id++;
5935
5936 if (track->album != NULL) {
5937 /* album ids are used when writing the mhla header */
5938 id = g_hash_table_lookup (fexp->albums, track);
5939 if (id != NULL) {
5940 track->priv->album_id = id->id;
5941 } else {
5942 add_new_id (fexp->albums, track, album_id);
5943 track->priv->album_id = album_id;
5944 album_id++;
5945 }
5946 }
5947
5948 if (track->artist != NULL) {
5949 /* artist ids are used when writing the mhli header */
5950 id = g_hash_table_lookup (fexp->artists, track);
5951 if (id != NULL) {
5952 track->priv->artist_id = id->id;
5953 } else {
5954 add_new_id (fexp->artists, track, artist_id);
5955 track->priv->artist_id = artist_id;
5956 artist_id++;
5957 }
5958 }
5959
5960 if (track->composer != NULL) {
5961 id = g_hash_table_lookup (fexp->composers, track);
5962 if (id != NULL) {
5963 track->priv->composer_id = id->id;
5964 } else {
5965 add_new_id (fexp->composers, track, composer_id);
5966 track->priv->composer_id = composer_id;
5967 composer_id++;
5968 }
5969 }
5970 }
5971
5972 /* Make sure playlist->num is correct */
5973 for (pl = itdb->playlists; pl; pl = pl->next) {
5974 playlist = pl->data;
5975 g_return_if_fail (playlist);
5976 playlist->num = itdb_playlist_tracks_number (playlist);
5977 }
5978 }
5979
itdb_write_file_internal(Itdb_iTunesDB * itdb,const gchar * filename,GError ** error)5980 static gboolean itdb_write_file_internal (Itdb_iTunesDB *itdb,
5981 const gchar *filename,
5982 GError **error)
5983 {
5984 FExport *fexp;
5985 gulong mhbd_seek = 0;
5986 WContents *cts;
5987 gboolean result = TRUE;
5988 guint32 num_mhsds;
5989
5990 g_return_val_if_fail (itdb, FALSE);
5991 g_return_val_if_fail (itdb->device, FALSE);
5992 g_return_val_if_fail (filename || itdb->filename, FALSE);
5993
5994 if (!filename) filename = itdb->filename;
5995
5996 /* set endianess flag */
5997 if (!itdb->device->byte_order)
5998 itdb_device_autodetect_endianess (itdb->device);
5999
6000 fexp = g_new0 (FExport, 1);
6001 fexp->itdb = itdb;
6002 fexp->wcontents = wcontents_new (filename);
6003 cts = fexp->wcontents;
6004
6005 cts->reversed = (itdb->device->byte_order == G_BIG_ENDIAN);
6006
6007 prepare_itdb_for_write (fexp);
6008
6009 #if HAVE_GDKPIXBUF
6010 /* only write ArtworkDB if we deal with an iPod
6011 FIXME: figure out a way to store the artwork data when storing
6012 to local directories. At the moment it's the application's task
6013 to handle this. */
6014 /* The ArtworkDB must be written after the call to
6015 * prepare_itdb_for_write since it needs Itdb_Track::id to be set
6016 * to its final value to write properly on nano video/ipod classics
6017 */
6018 if (itdb_device_supports_artwork (itdb->device)) {
6019 ipod_write_artwork_db (itdb);
6020 }
6021 #endif
6022
6023 /* default mhsd count */
6024 num_mhsds = 8; /* eight mhsds */
6025
6026 /* if genius_cuid present, we have one more */
6027 if (fexp->itdb->priv->genius_cuid) {
6028 num_mhsds++;
6029 }
6030
6031 mk_mhbd (fexp, num_mhsds);
6032
6033 /* write tracklist (mhsd type 1) */
6034 if (!fexp->error && !write_mhsd_tracks (fexp)) {
6035 g_set_error (&fexp->error,
6036 ITDB_FILE_ERROR,
6037 ITDB_FILE_ERROR_ITDB_CORRUPT,
6038 _("Error writing list of tracks (mhsd type 1)"));
6039 goto err;
6040 }
6041
6042 /* write special podcast version mhsd (mhsd type 3) */
6043 if (!fexp->error && !write_mhsd_playlists (fexp, 3)) {
6044 g_set_error (&fexp->error,
6045 ITDB_FILE_ERROR,
6046 ITDB_FILE_ERROR_ITDB_CORRUPT,
6047 _("Error writing special podcast playlists (mhsd type 3)"));
6048 goto err;
6049 }
6050 /* write standard playlist mhsd (mhsd type 2) */
6051 if (!fexp->error && !write_mhsd_playlists (fexp, 2)) {
6052 g_set_error (&fexp->error,
6053 ITDB_FILE_ERROR,
6054 ITDB_FILE_ERROR_ITDB_CORRUPT,
6055 _("Error writing standard playlists (mhsd type 2)"));
6056 goto err;
6057 }
6058
6059 /* write albums (mhsd type 4) */
6060 if (!write_mhsd_albums (fexp)) {
6061 g_set_error (&fexp->error,
6062 ITDB_FILE_ERROR,
6063 ITDB_FILE_ERROR_ITDB_CORRUPT,
6064 _("Error writing list of albums (mhsd type 4)"));
6065 goto err;
6066 }
6067 /* write artists (mhsd type 8) */
6068 if (!fexp->error && !write_mhsd_artists (fexp)) {
6069 g_set_error (&fexp->error,
6070 ITDB_FILE_ERROR,
6071 ITDB_FILE_ERROR_ITDB_CORRUPT,
6072 _("Error writing list of artists (mhsd type 8)"));
6073 goto err;
6074 }
6075
6076 /* write empty mhsd type 6, whatever it is */
6077 if (!fexp->error && !write_mhsd_type6 (fexp)) {
6078 g_set_error (&fexp->error,
6079 ITDB_FILE_ERROR,
6080 ITDB_FILE_ERROR_ITDB_CORRUPT,
6081 _("Error writing mhsd type 6"));
6082 goto err;
6083 }
6084
6085 /* write empty mhsd type 10, whatever it is */
6086 if (!fexp->error && !write_mhsd_type10 (fexp)) {
6087 g_set_error (&fexp->error,
6088 ITDB_FILE_ERROR,
6089 ITDB_FILE_ERROR_ITDB_CORRUPT,
6090 _("Error writing mhsd type 10"));
6091 goto err;
6092 }
6093
6094 /* write mhsd5 playlists */
6095 if (!fexp->error && !write_mhsd_playlists (fexp, 5)) {
6096 g_set_error (&fexp->error,
6097 ITDB_FILE_ERROR,
6098 ITDB_FILE_ERROR_ITDB_CORRUPT,
6099 _("Error writing mhsd5 playlists"));
6100 goto err;
6101 }
6102
6103 if (fexp->itdb->priv->genius_cuid) {
6104 /* write genius cuid (mhsd type 9) */
6105 if (!fexp->error && !write_genius_mhsd (fexp)) {
6106 g_set_error (&fexp->error,
6107 ITDB_FILE_ERROR,
6108 ITDB_FILE_ERROR_ITDB_CORRUPT,
6109 _("Error writing mhsd type 9"));
6110 goto err;
6111 }
6112 }
6113
6114 fix_header (cts, mhbd_seek);
6115
6116 if (itdb_device_supports_compressed_itunesdb (itdb->device)) {
6117 if (!itdb_zlib_check_compress_fexp (fexp)) {
6118 goto err;
6119 }
6120 }
6121
6122 /* Set checksum (ipods require it starting from Classic and Nano Video) */
6123 itdb_device_write_checksum (itdb->device,
6124 (unsigned char *)fexp->wcontents->contents,
6125 fexp->wcontents->pos,
6126 &fexp->error);
6127 if (fexp->error) {
6128 goto err;
6129 }
6130
6131 if (itdb_device_supports_sqlite_db (itdb->device)) {
6132 if (itdb_sqlite_generate_itdbs(fexp) != 0) {
6133 goto err;
6134 }
6135 }
6136
6137 if (itdb_device_is_shuffle (itdb->device)) {
6138 /* iPod Shuffle uses a simplified database in addition to the
6139 * iTunesDB */
6140 if (!itdb_shuffle_write (itdb, &fexp->error)) {
6141 goto err;
6142 }
6143 }
6144
6145 if (!fexp->error)
6146 {
6147 if (wcontents_write (cts)) {
6148 playcounts_reset (itdb_get_mountpoint (itdb));
6149 } else {
6150 g_propagate_error (&fexp->error, cts->error);
6151 }
6152 }
6153 err:
6154 if (fexp->error)
6155 {
6156 g_propagate_error (error, fexp->error);
6157 result = FALSE;
6158 }
6159 wcontents_free (cts);
6160 if (fexp->albums != NULL) {
6161 g_hash_table_destroy (fexp->albums);
6162 }
6163 if (fexp->artists != NULL) {
6164 g_hash_table_destroy (fexp->artists);
6165 }
6166 g_free (fexp);
6167 if (result == TRUE)
6168 {
6169 gchar *fn = g_strdup (filename);
6170 g_free (itdb->filename);
6171 itdb->filename = fn;
6172 }
6173
6174 /* make sure all buffers are flushed as some people tend to
6175 disconnect as soon as gtkpod returns */
6176 itdb_fsync ();
6177
6178 return result;
6179 }
6180
6181 /**
6182 * itdb_write_file:
6183 * @itdb: the #Itdb_iTunesDB to save
6184 * @filename: filename to save @itdb to
6185 * @error: return location for a #GError or NULL
6186 *
6187 * Write the content of @itdb to @filename. If @filename is NULL, it attempts
6188 * to write to @itdb->filename.
6189 *
6190 * Returns: TRUE if all went well, FALSE otherwise
6191 */
6192
itdb_write_file(Itdb_iTunesDB * itdb,const gchar * filename,GError ** error)6193 gboolean itdb_write_file (Itdb_iTunesDB *itdb, const gchar *filename,
6194 GError **error)
6195 {
6196 return itdb_write_file_internal (itdb, filename, error);
6197 }
6198
6199 /**
6200 * itdb_write:
6201 * @itdb: the #Itdb_iTunesDB to write to disk
6202 * @error: return location for a #GError or NULL
6203 *
6204 * Write out an iTunesDB. It reassigns unique IDs to all tracks.
6205 * An existing "Play Counts" file is renamed to "Play Counts.bak" if
6206 * the export was successful.
6207 * An existing "OTGPlaylistInfo" file is removed if the export was
6208 * successful.
6209 *
6210 * Returns: TRUE on success, FALSE on error, in which case @error is
6211 * set accordingly.
6212 */
itdb_write(Itdb_iTunesDB * itdb,GError ** error)6213 gboolean itdb_write (Itdb_iTunesDB *itdb, GError **error)
6214 {
6215 gchar *itunes_filename, *itunes_path;
6216 gboolean result = FALSE;
6217
6218 g_return_val_if_fail (itdb, FALSE);
6219 g_return_val_if_fail (itdb_get_mountpoint (itdb), FALSE);
6220
6221 /* Now handling regular iPod or iPhone/iPod Touch */
6222
6223 /* First, let's try to write the .ithmb files containing the artwork data
6224 * since this operation modifies the 'artwork_count' and 'artwork_size'
6225 * field in the Itdb_Track contained in the database.
6226 * Errors happening during that operation are considered non fatal since
6227 * they shouldn't corrupt the main database.
6228 */
6229 itunes_path = itdb_get_itunes_dir (itdb_get_mountpoint (itdb));
6230
6231 if(!itunes_path)
6232 {
6233 error_no_itunes_dir (itdb_get_mountpoint (itdb), error);
6234 return FALSE;
6235 }
6236
6237 if (itdb_device_supports_compressed_itunesdb (itdb->device)) {
6238 itunes_filename = g_build_filename (itunes_path, "iTunesCDB", NULL);
6239 } else {
6240 itunes_filename = g_build_filename (itunes_path, "iTunesDB", NULL);
6241 }
6242
6243 itdb_start_sync (itdb);
6244
6245 result = itdb_write_file_internal (itdb, itunes_filename, error);
6246 g_free (itunes_filename);
6247
6248 if (result && itdb_device_supports_compressed_itunesdb (itdb->device)) {
6249 /* the iPhone gets confused when it has both an iTunesCDB and a non
6250 * empty iTunesDB
6251 */
6252 itunes_filename = g_build_filename (itunes_path, "iTunesDB", NULL);
6253 g_file_set_contents(itunes_filename, NULL, 0, NULL);
6254 g_free (itunes_filename);
6255 }
6256
6257 g_free (itunes_path);
6258
6259 if (result != FALSE)
6260 {
6261 /* Write SysInfo file if it has changed */
6262 if (itdb->device->sysinfo_changed)
6263 {
6264 itdb_device_write_sysinfo (itdb->device, error);
6265 }
6266 result = itdb_rename_files (itdb_get_mountpoint (itdb), error);
6267 }
6268
6269 /* make sure all buffers are flushed as some people tend to
6270 disconnect as soon as gtkpod returns */
6271 itdb_fsync ();
6272
6273 itdb_stop_sync (itdb);
6274
6275 return result;
6276 }
6277
6278 /**
6279 * itdb_start_sync:
6280 * @itdb: the #Itdb_iTunesDB that is being sync'ed
6281 *
6282 * Hints libgpod that a series of file copies/database saves/... is about
6283 * to start. On regular iPods, this does nothing (but is safe to be used),
6284 * but on iPhones and iPod Touch this makes sure the "Sync in progress" screen
6285 * is shown for the whole duration of the writing process.
6286 *
6287 * Calls to itdb_start_sync must be paired with calls to itdb_stop_sync. Nesting
6288 * is allowed.
6289 *
6290 * Returns: TRUE on success, FALSE on error
6291 */
itdb_start_sync(Itdb_iTunesDB * itdb)6292 gboolean itdb_start_sync (Itdb_iTunesDB *itdb)
6293 {
6294 g_return_val_if_fail (itdb != NULL, FALSE);
6295 g_return_val_if_fail (itdb->device != NULL, FALSE);
6296
6297 #ifdef HAVE_LIBIMOBILEDEVICE
6298 if (itdb->device->iphone_sync_context != NULL) {
6299 itdb->device->iphone_sync_nest_level++;
6300 return TRUE;
6301 }
6302 if (itdb_device_is_iphone_family (itdb->device)) {
6303 int sync_status;
6304 sync_status = itdb_iphone_start_sync (itdb->device,
6305 &itdb->device->iphone_sync_context);
6306 if (sync_status == 0) {
6307 return TRUE;
6308 } else {
6309 return FALSE;
6310 }
6311 }
6312 #endif
6313
6314 return TRUE;
6315 }
6316
6317 /**
6318 * itdb_stop_sync:
6319 * @itdb: the #Itdb_iTunesDB that is being sync'ed
6320 *
6321 * Hints libgpod that the series of file copies/database saves/... started
6322 * with itdb_start_sync is finished. On regular iPods, this does nothing
6323 * (but is safe to be used). On iPhones and iPod Touch this will hide the
6324 * "Sync in progress" screen.
6325 *
6326 * Calls to itdb_stop_sync must be paired with calls to itdb_start_sync. Nesting
6327 * is allowed, and only the final itdb_stop_sync will actually stop the sync.
6328 *
6329 * Returns: TRUE on success, FALSE on error
6330 */
itdb_stop_sync(Itdb_iTunesDB * itdb)6331 gboolean itdb_stop_sync (Itdb_iTunesDB *itdb)
6332 {
6333 g_return_val_if_fail (itdb != NULL, FALSE);
6334 g_return_val_if_fail (itdb->device != NULL, FALSE);
6335
6336 #ifdef HAVE_LIBIMOBILEDEVICE
6337 if (itdb->device->iphone_sync_nest_level) {
6338 itdb->device->iphone_sync_nest_level--;
6339 return TRUE;
6340 }
6341 if (itdb_device_is_iphone_family (itdb->device)) {
6342 int sync_status;
6343 if (itdb->device->iphone_sync_context == NULL) {
6344 g_warning ("Trying to unlock an already unlocked device");
6345 return FALSE;
6346 }
6347 sync_status = itdb_iphone_stop_sync (itdb->device->iphone_sync_context);
6348 itdb->device->iphone_sync_context = NULL;
6349 if (sync_status != 0) {
6350 return FALSE;
6351 }
6352 }
6353 #endif
6354 return TRUE;
6355 }
6356
6357 /* from here on we have the functions for writing the iTunesDB */
6358 /* -------------------------------------------------------------------- */
6359 /* up to here we had the functions for writing the iTunesSD */
6360
6361 /*
6362 | Copyright (C) 2005 Jorg Schuler <jcsjcs at users.sourceforge.net>
6363 | Part of the gtkpod project.
6364 |
6365 | Based on itunessd.c written by Steve Wahl for gtkpod-0.88:
6366 |
6367 | Copyright 2005 Steve Wahl <steve at pro-ns dot net>
6368 |
6369 | This file contains routines to create the iTunesSD file, as
6370 | used by the ipod shuffle.
6371 |
6372 | Like itunesdb.c, it is derived from the perl script "mktunes.pl"
6373 | (part of the gnupod-tools collection) written by Adrian
6374 | Ulrich <pab at blinkenlights.ch>.
6375 |
6376 | Small(?) portions derived from itunesdb.c, so Jorg Schuler probably
6377 | has some copyright ownership in this file as well.
6378 |
6379 | The code contained in this file is free software; you can redistribute
6380 | it and/or modify it under the terms of the GNU Lesser General Public
6381 | License as published by the Free Software Foundation; either version
6382 | 2.1 of the License, or (at your option) any later version.
6383 |
6384 | This file is distributed in the hope that it will be useful,
6385 | but WITHOUT ANY WARRANTY; without even the implied warranty of
6386 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
6387 | Lesser General Public License for more details.
6388 |
6389 | You should have received a copy of the GNU Lesser General Public
6390 | License along with this code; if not, write to the Free Software
6391 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
6392 |
6393 | iTunes and iPod are trademarks of Apple
6394 |
6395 | This product is not supported/written/published by Apple!
6396 |
6397 */
6398
6399 /* notes:
6400
6401 All software currently seems to write iTunesDB as well as iTunesSD
6402 on the iPod shuffle. I assume that reading from the iTunesSD file
6403 is not necessary. The iTunesStats file is different, but I leave
6404 that for another day.
6405
6406 The iTunesSD file format is as follows (taken from WikiPodLinux, feb
6407 '05):
6408
6409 Offset Field Bytes Value
6410 (hex) (dec)
6411
6412 iTunesSD header (occurs once, at beginning of file):
6413
6414 00 num_songs 3 number of song entries in the file
6415
6416 03 unknown 3 always(?) 0x010600
6417 06 header size 3 size of the header (0x12, 18 bytes)
6418 09 unknown 3 possibly zero padding
6419
6420 iTunesSD song entry format (occurs once for each song)
6421
6422 000 size of entry 3 always(?) 0x00022e (558 bytes)
6423 003 unk1 3 unknown, always(?) 0x5aa501
6424 006 starttime 3 Start Time, in 256 ms increments
6425 e.g. 60s = 0xea (234 dec)
6426 009 unk2 3 unknown (always 0?)
6427 00C unk3 3 unknown, some relationship to starttime
6428 00F stoptime 3 Stop Time, also in 256 ms increments.
6429 Zero means play to end of file.
6430 012 unk4 3 Unknown.
6431 015 unk5 3 Unknown, but associated with stoptime?
6432 018 volume 3 Volume - ranges from 0x00 (-100%) to 0x64
6433 (0%) to 0xc8 (100%)
6434 01B file_type 3 0x01 = MP3, 0x02 = AAC, 0x04=WAV
6435 01E unk6 3 unknown (always 0x200?)
6436 021 filename 522 filename of the song, padded at the end
6437 with 0's. Note: forward slashes are used
6438 here, not colons like in the iTunesDB --
6439 for example,
6440 "/iPod_Control/Music/F00/Song.mp3"
6441 22B shuffleflag 1 If this value is 0x00, the song will be
6442 skipped while the player is in shuffle
6443 mode. Any other value will allow it to be
6444 played in both normal and shuffle modes.
6445 iTunes 4.7.1 sets this to 0 for audio books.
6446 22C bookmarkflag 1 If this flag is 0x00, the song will not be
6447 bookmarkable (i.e. its playback position
6448 won't be saved when switching to a different
6449 song). Any other value wil make it
6450 Bookmarkable. Unlike hard drive based iPods,
6451 all songs can be marked as bookmarkable,
6452 not just .m4b and .aa
6453 22D unknownflag 1 unknown, always? 0x00.
6454
6455 All integers in the iTunesSD file are in BIG endian form...
6456
6457 */
6458
6459 /**
6460 * itdb_shuffle_write:
6461 * @itdb: the #Itdb_iTunesDB to write to disk
6462 * @error: return location for a #GError or NULL
6463 *
6464 * Write out an iTunesSD for the Shuffle.
6465 *
6466 * First reassigns unique IDs to all tracks. An existing "Play
6467 * Counts" file is renamed to "Play Counts.bak" if the export was
6468 * successful. An existing "OTGPlaylistInfo" file is removed if the
6469 * export was successful. @itdb->mountpoint must point to the mount
6470 * point of the iPod, e.g. "/mnt/ipod" and be in local encoding.
6471 *
6472 * Returns: TRUE on success, FALSE on error, in which case @error is
6473 * set accordingly.
6474 */
itdb_shuffle_write(Itdb_iTunesDB * itdb,GError ** error)6475 gboolean itdb_shuffle_write (Itdb_iTunesDB *itdb, GError **error)
6476 {
6477 gchar *itunes_filename, *itunes_path;
6478 gboolean result = FALSE;
6479
6480 g_return_val_if_fail (itdb, FALSE);
6481 g_return_val_if_fail (itdb_get_mountpoint (itdb), FALSE);
6482
6483 itunes_path = itdb_get_itunes_dir (itdb_get_mountpoint (itdb));
6484
6485 if(!itunes_path)
6486 {
6487 gchar *str = g_build_filename (itdb_get_mountpoint (itdb),
6488 "iPod_Control", "iTunes", NULL);
6489 g_set_error (error,
6490 ITDB_FILE_ERROR,
6491 ITDB_FILE_ERROR_NOTFOUND,
6492 _("Path not found: '%s' (or similar)."),
6493 str);
6494 g_free (str);
6495 return FALSE;
6496 }
6497
6498 itunes_filename = g_build_filename (itunes_path, "iTunesSD", NULL);
6499
6500 result = itdb_shuffle_write_file (itdb, itunes_filename, error);
6501
6502 g_free(itunes_filename);
6503 g_free(itunes_path);
6504
6505 if (result == TRUE)
6506 result = itdb_rename_files (itdb_get_mountpoint (itdb), error);
6507
6508 /* make sure all buffers are flushed as some people tend to
6509 disconnect as soon as gtkpod returns */
6510 itdb_fsync ();
6511
6512 return result;
6513 }
6514
6515 /* helper function */
haystack(gchar * filetype,gchar ** desclist)6516 static gboolean haystack (gchar *filetype, gchar **desclist)
6517 {
6518 gchar **dlp;
6519 if (!filetype || !desclist) return FALSE;
6520 for (dlp=desclist; *dlp; ++dlp)
6521 {
6522 if (strstr (filetype, *dlp)) return TRUE;
6523 }
6524 return FALSE;
6525 }
6526
6527 /* Converts a standard itunesDB filetype to the one used in itunesSD */
6528 /* TODO: Find the values for other filetypes */
convert_filetype(gchar * filetype)6529 static guint convert_filetype (gchar *filetype)
6530 {
6531 guint32 stype;
6532 /* Type 0x01 Default value */
6533 /* gchar *mp3_desc[] = {"MPEG", "MP3", "mpeg", "mp3", NULL}; */
6534 /* Type 0x02 */
6535 gchar *mp4_desc[] = {"AAC", "MP4", "M4A", "aac", "mp4", "m4a", NULL};
6536 /* Type 0x04 */
6537 gchar *wav_desc[] = {"WAV", "wav", NULL};
6538
6539 /* Default to mp3 */
6540 stype=0x01;
6541
6542 if (haystack (filetype, mp4_desc))
6543 stype=0x02;
6544 else if (haystack (filetype, wav_desc))
6545 stype=0x04;
6546
6547 return stype;
6548 }
6549
6550 /* Write out the rths header, the iTunesSD track header */
write_rths(WContents * cts,Itdb_Track * track)6551 static gboolean write_rths (WContents *cts, Itdb_Track *track)
6552 {
6553 gulong rths_seek;
6554 gint padlength;
6555 gchar *path;
6556
6557 g_return_val_if_fail (cts, FALSE);
6558 g_return_val_if_fail (track, FALSE);
6559
6560 rths_seek=cts->pos;
6561
6562 /* Prep the path */
6563 /* Correct this if the path field changes length */
6564 path = g_strndup (track->ipod_path, 256);
6565 g_strdelimit (path, ":", '/');
6566
6567 put_header (cts, "rths");
6568 put32lint (cts, -1); /* Length of header to be added later */
6569 put32lint (cts, track->starttime); /* Start pos in ms */
6570 put32lint (cts, track->stoptime); /* Stop pos in ms */
6571 put32lint (cts, track->volume); /* Volume gain */
6572 put32lint (cts, convert_filetype (track->filetype)); /* Filetype see
6573 convert filetype*/
6574 /* If the length of this field changes make sure to correct the
6575 g_strndup above */
6576 put_string (cts, path); /* Path to the song */
6577 for (padlength = 256-strlen (path); padlength > 0; padlength--)
6578 put8int (cts, 0);
6579
6580 put32lint (cts, track->bookmark_time); /* Bookmark time */
6581 /* Don't Skip on shuffle */
6582 /* This field has the exact opposite value as in the iTunesDB */
6583 /* 1 when you want to skip, 0 when you don't want to skip. Causes a
6584 * playlist to be skipped if all songs in that playlist have this set */
6585 put8int (cts, !track->skip_when_shuffling);
6586 put8int (cts, track->remember_playback_position); /* Remember playing pos */
6587 /* put8int (cts, ); In uninterruptable album */
6588 put8int (cts, 0); /* Best guess its gapless album */
6589 put8int (cts, 0); /* Unknown */
6590 put32lint (cts, track->pregap); /* Pregap */
6591 put32lint (cts, track->postgap); /* Postgap */
6592 put32lint (cts, (guint32) track->samplecount); /* Number of Samples */
6593 put32_n0 (cts, 1); /* Unknown */
6594 put32lint (cts, track->gapless_data); /* Gapless Data */
6595 put32_n0 (cts, 1); /* Unknown */
6596 put32lint (cts, track->priv->album_id); /* Album ID */
6597 put16lint (cts, track->track_nr); /* Track number */
6598 put16lint (cts, track->cd_nr); /* Disk number */
6599 put32_n0 (cts, 2); /* Unknown */
6600 put64lint (cts, track->dbid); /* Voiceover Filename also dbid*/
6601 put32lint (cts, track->priv->artist_id); /* Artist ID */
6602 put32_n0 (cts, 8); /* Unknown */
6603
6604 fix_short_header (cts, rths_seek);
6605
6606 g_free (path);
6607
6608 return TRUE;
6609 }
6610
6611 /* Write out the hths header, the iTunesSD track list header*/
write_hths(FExport * fexp)6612 static gboolean write_hths (FExport *fexp)
6613 {
6614 gulong hths_seek, track_seek;
6615 WContents *cts;
6616 GList *gl;
6617 guint32 trackcnt;
6618 guint32 nonstdtrackcnt;
6619
6620 g_return_val_if_fail (fexp, FALSE);
6621 g_return_val_if_fail (fexp->itdb, FALSE);
6622 g_return_val_if_fail (fexp->wcontents, FALSE);
6623
6624 cts = fexp->wcontents;
6625 hths_seek = cts->pos;
6626 trackcnt = itdb_tracks_number(fexp->itdb);
6627 nonstdtrackcnt = 0; /* Counts the number of audiobook and podcast tracks */
6628
6629 /* Add Track list offset to bdhs header */
6630 /* If the bdhs header changes make sure the seek value is still correct */
6631 put32lint_seek(cts, cts->pos, 36);
6632
6633 put_header (cts, "hths");
6634 put32lint (cts, -1); /* Length of header to be added later */
6635 put32lint (cts, trackcnt); /* Number of tracks */
6636 put32_n0 (cts, 2); /* Unknown */
6637 track_seek=cts->pos; /* Put the offset of the first track here */
6638 put32_n0 (cts, trackcnt); /* The offset for each track to be added later */
6639
6640 fix_short_header (cts, hths_seek);
6641
6642 /* Add each tracks header */
6643 for(gl=fexp->itdb->tracks;gl;gl=gl->next)
6644 {
6645 Itdb_Track *track = gl->data;
6646
6647 /* Add this tracks offset then add the track */
6648 put32lint_seek(cts, cts->pos, track_seek);
6649 g_return_val_if_fail (write_rths(cts, track), FALSE);
6650
6651 if ((track->mediatype & ITDB_MEDIATYPE_AUDIOBOOK) ||
6652 (track->mediatype & ITDB_MEDIATYPE_PODCAST))
6653 nonstdtrackcnt++;
6654
6655 /* Go to the offset for the next track */
6656 track_seek += 4;
6657 }
6658 /* Add the number of nonpodcasts to bdhs header */
6659 /* If the bdhs header changes make sure the seek value is still correct */
6660 put32lint_seek(cts, trackcnt-nonstdtrackcnt, 32);
6661 return TRUE;
6662 }
6663
6664 /* Write out the lphs header, the iTunesSD playlist header */
write_lphs(WContents * cts,Itdb_Playlist * pl)6665 static gboolean write_lphs (WContents *cts, Itdb_Playlist *pl)
6666 {
6667 gulong lphs_seek;
6668 GList *tl, *tracks, *current_track;
6669 Itdb_Track *tr, *ctr;
6670 guint64 id;
6671 guint32 stype;
6672 guint32 tracknum;
6673 guint32 nonstdtrackcnt;
6674
6675 g_return_val_if_fail (pl, FALSE);
6676 g_return_val_if_fail (pl->itdb, FALSE);
6677 g_return_val_if_fail (cts, FALSE);
6678
6679 lphs_seek = cts->pos;
6680 tracks = pl->itdb->tracks;
6681 nonstdtrackcnt = 0; /* The number of audiobook and podcast tracks*/
6682 /* Change the playlist itunesDB type into a itunesSD type */
6683 /* 1 is for the master playlist
6684 2 is for a normal playlist
6685 3 is for the podcast playlist
6686 4 is for an audiobook playlist */
6687 if (itdb_playlist_is_mpl (pl))
6688 stype = 1;
6689 else if (itdb_playlist_is_podcasts (pl))
6690 stype = 3;
6691 else if (itdb_playlist_is_audiobooks (pl))
6692 stype = 4;
6693 else /* Everything else we treat as normal */
6694 stype = 2;
6695
6696 put_header (cts, "lphs");
6697 put32lint (cts, -1); /* Length of header to be written later */
6698 put32lint (cts, pl->num); /* Number of tracks in this playlist */
6699 put32lint (cts, -1); /* The number of non podcasts or audiobooks to be added later */
6700
6701 if (stype == 1)
6702 put32_n0 (cts, 2); /* The voiceover for the master is at 0 */
6703 else
6704 put64lint (cts, pl->id); /* Voiceover filename also Playlist ID */
6705
6706 put32lint (cts, stype); /* Type of playlist */
6707 put32_n0 (cts, 4); /* Unknown */
6708 if (tracks) /* It makes no sense to do the following if there is no tracks */
6709 {
6710 /* Walk the playlist and find and write out the track number
6711 of each track in it */
6712 for( tl = pl -> members; tl; tl = tl->next)
6713 {
6714 tracknum = 0;
6715 current_track= tracks;
6716 tr = tl->data;
6717 id = tr->dbid;
6718 ctr = current_track->data;
6719 /* Count the number of podcasts and audiobooks for later use */
6720 if ((tr->mediatype & ITDB_MEDIATYPE_AUDIOBOOK) ||
6721 (tr->mediatype & ITDB_MEDIATYPE_PODCAST))
6722 nonstdtrackcnt++;
6723
6724 while( id != ctr->dbid)
6725 {
6726 tracknum ++;
6727 current_track = current_track->next;
6728 g_return_val_if_fail (current_track, FALSE);
6729 ctr = current_track->data;
6730 }
6731 put32lint (cts, tracknum);
6732 }
6733 }
6734
6735 put32lint_seek (cts, pl->num - nonstdtrackcnt, lphs_seek+12);
6736 fix_short_header (cts, lphs_seek);
6737 return TRUE;
6738
6739 }
6740
6741 /* Write out the hphs header, the iTunesSD playlist list header */
write_hphs(FExport * fexp)6742 static gboolean write_hphs (FExport *fexp)
6743 {
6744 gulong hphs_seek, playlist_seek;
6745 WContents *cts;
6746 GList *gl;
6747 guint16 playlistcnt;
6748 guint16 podcastscnt;
6749 guint16 mastercnt;
6750 guint16 audiobookscnt;
6751
6752 g_return_val_if_fail (fexp, FALSE);
6753 g_return_val_if_fail (fexp->itdb, FALSE);
6754 g_return_val_if_fail (fexp->wcontents, FALSE);
6755
6756 cts = fexp->wcontents;
6757 hphs_seek = cts->pos;
6758 playlistcnt = 0; /* Number of nonempty playlists, this may not be the same
6759 as what itdb_playlists_number returns*/
6760 podcastscnt = 0; /* Number of podcast playlists should be 1 if one exists*/
6761 mastercnt = 0; /* Number of master playlists should be 1 */
6762 audiobookscnt = 0; /* Number of audiobook playlists */
6763
6764 /* We have to walk the playlist list before we can write the needed counts */
6765 for (gl = fexp->itdb->playlists; gl; gl = gl->next) {
6766 Itdb_Playlist *pl = gl->data;
6767
6768 if (!pl->members) {
6769 /* If the playlist has no members skip it */
6770 continue;
6771 }
6772 /* Otherwise count it and count its type */
6773 playlistcnt++;
6774
6775 if (itdb_playlist_is_mpl (pl)){
6776 mastercnt++;
6777 }
6778 else if (itdb_playlist_is_podcasts (pl)) {
6779 podcastscnt++;
6780 }
6781 else if (itdb_playlist_is_audiobooks (pl)) {
6782 audiobookscnt++;
6783 }
6784 }
6785
6786 /* Add nonempty playlist count to bdhs */
6787 put32lint_seek (cts, playlistcnt, 16);
6788
6789 /* Add the Playlist Header Offset */
6790 put32lint_seek (cts, cts->pos, 40);
6791
6792 put_header (cts, "hphs");
6793 put32lint (cts, -1); /* Length of header to be added later */
6794 put16lint (cts, playlistcnt); /* Number of playlists */
6795 put16_n0 (cts, 1); /* Unknown */
6796 put16lint (cts, playlistcnt-podcastscnt); /* Number of non podcast playlists
6797 there should be a maximum of 1
6798 podcast playlist. */
6799 put16lint (cts, mastercnt); /* Number of master playlists there should be a
6800 maximum of 1 master playlist */
6801 put16lint (cts, playlistcnt-audiobookscnt); /* Number of non audiobook playlists */
6802 put16_n0 (cts, 1);
6803
6804 playlist_seek = cts->pos;
6805 put32_n0 (cts, playlistcnt); /* Offsets for each playlist */
6806
6807 fix_short_header (cts, hphs_seek);
6808
6809 for (gl = fexp->itdb->playlists; gl; gl = gl->next)
6810 {
6811 Itdb_Playlist *pl = gl->data;
6812
6813 if (!pl->members) {
6814 continue;
6815 }
6816
6817 /* Write this playlist's header offset */
6818 put32lint_seek (cts, cts->pos, playlist_seek);
6819
6820 g_return_val_if_fail (write_lphs (cts, pl),FALSE);
6821
6822 /* Move to the field for the next header */
6823 playlist_seek += 4;
6824 }
6825 return TRUE;
6826 }
6827
6828 /* Write out the bdhs header, the main iTunesSD header */
write_bdhs(FExport * fexp)6829 static gboolean write_bdhs (FExport *fexp)
6830 {
6831 WContents *cts;
6832 gulong bdhs_seek;
6833 guint32 trackcnt;
6834
6835 g_return_val_if_fail (fexp, FALSE);
6836 g_return_val_if_fail (fexp->itdb, FALSE);
6837 g_return_val_if_fail (fexp->wcontents, FALSE);
6838
6839 cts = fexp->wcontents;
6840 bdhs_seek = cts->pos;
6841 trackcnt = itdb_tracks_number (fexp->itdb);
6842
6843 put_header (cts, "bdhs");
6844 put32lint (cts, 0x02000003); /* Unknown */
6845 put32lint (cts, -1); /* Length of header to be added later*/
6846 put32lint (cts, trackcnt); /* Number of tracks */
6847 put32_n0 (cts, 1); /* Number of nonempty playlists to be written later
6848 in write_hphs see the note below! */
6849 put32_n0 (cts, 2); /* Unknown */
6850 /* TODO: Parse the max volume */
6851 put8int (cts, 0); /* Limit max volume currently ignored and set to off */
6852 /* TODO: Find another source of the voiceover option */
6853 put8int (cts, 1); /* Voiceover currently ignored and set to on */
6854 put16_n0 (cts, 1); /* Unknown */
6855
6856 /* NOTE: If the bdhs header changes the offsets of these fields may change
6857 make sure you correct the field offset in their respective tags */
6858
6859 put32lint (cts, -1); /* Number of tracks excluding podcasts and audiobooks
6860 added in write_hths */
6861 put32lint (cts, -1); /* Track Header Offset added in write_hths */
6862 put32lint (cts, -1); /* Playlist Header Offset added in write_hphs */
6863
6864 put32_n0 (cts, 5); /* Unknown */
6865
6866 fix_header(cts, bdhs_seek);
6867
6868 return TRUE;
6869 }
6870
is_shuffle_2g(Itdb_Device * device)6871 static gboolean is_shuffle_2g (Itdb_Device *device)
6872 {
6873 return (itdb_device_get_shadowdb_version (device) == ITDB_SHADOW_DB_V1);
6874 }
6875
6876 /**
6877 * itdb_shuffle_write_file:
6878 * @itdb: the #Itdb_iTunesDB to write to disk
6879 * @filename: file to write to, cannot be NULL
6880 * @error: return location for a #GError or NULL
6881 *
6882 * Do the actual writing to the iTunesSD
6883 *
6884 * Returns: TRUE on success, FALSE on error, in which case @error is
6885 * set accordingly.
6886 */
itdb_shuffle_write_file(Itdb_iTunesDB * itdb,const gchar * filename,GError ** error)6887 gboolean itdb_shuffle_write_file (Itdb_iTunesDB *itdb,
6888 const gchar *filename, GError **error)
6889 {
6890 FExport *fexp;
6891 GList *gl;
6892 WContents *cts;
6893 gboolean result = TRUE;;
6894
6895 g_return_val_if_fail (itdb, FALSE);
6896 g_return_val_if_fail (filename, FALSE);
6897 g_return_val_if_fail (itdb->device, FALSE);
6898
6899 /* Set endianess flag just in case */
6900 if (!itdb->device->byte_order)
6901 itdb_device_autodetect_endianess (itdb->device);
6902
6903 fexp = g_new0 (FExport, 1);
6904 fexp->itdb = itdb;
6905 fexp->wcontents = wcontents_new (filename);
6906 cts = fexp->wcontents;
6907
6908 cts->reversed = (itdb->device->byte_order == G_BIG_ENDIAN);
6909
6910 prepare_itdb_for_write (fexp);
6911
6912 if (is_shuffle_2g (itdb->device)) {
6913 /* Older iTunesSD format */
6914 put24bint (cts, itdb_tracks_number (itdb));
6915 put24bint (cts, 0x010600);
6916 put24bint (cts, 0x12); /* size of header */
6917 put24bint (cts, 0x0); /* padding? */
6918 put24bint (cts, 0x0);
6919 put24bint (cts, 0x0);
6920
6921 for (gl=itdb->tracks; gl; gl=gl->next)
6922 {
6923 Itdb_Track *tr = gl->data;
6924 gchar *path;
6925 gunichar2 *path_utf16;
6926 glong pathlen;
6927
6928 g_return_val_if_fail (tr, FALSE);
6929
6930 put24bint (cts, 0x00022e);
6931 put24bint (cts, 0x5aa501);
6932 /* starttime is in 256 ms incr. for shuffle */
6933 put24bint (cts, tr->starttime / 256);
6934 put24bint (cts, 0);
6935 put24bint (cts, 0);
6936 put24bint (cts, tr->stoptime / 256);
6937 put24bint (cts, 0);
6938 put24bint (cts, 0);
6939 /* track->volume ranges from -255 to +255 */
6940 /* we want 0 - 200 */
6941 put24bint (cts, ((tr->volume + 255) * 201) / 511);
6942
6943 /* The next one should be 0x01 for MP3,
6944 ** 0x02 for AAC, and 0x04 for WAV, but I can't find
6945 ** a suitable indicator within the track structure? */
6946 /* JCS: let's do heuristic on tr->filetype which would contain
6947 "MPEG audio file", "AAC audio file", "Protected AAC audio
6948 file", "AAC audio book file", "WAV audio file" (or similar
6949 if not written by gtkpod) */
6950
6951 put24bint (cts, convert_filetype (tr->filetype));
6952
6953 put24bint (cts, 0x200);
6954
6955 path = g_strdup (tr->ipod_path);
6956 /* shuffle uses forward slash separator, not colon */
6957 g_strdelimit (path, ":", '/');
6958 path_utf16 = g_utf8_to_utf16 (path, -1, NULL, &pathlen, NULL);
6959 if (pathlen > 261) pathlen = 261;
6960 fixup_little_utf16 (path_utf16);
6961 put_data (cts, (gchar *)path_utf16, sizeof (gunichar2)*pathlen);
6962 /* pad to 522 bytes */
6963 put16_n0 (cts, 261-pathlen);
6964 g_free(path);
6965 g_free(path_utf16);
6966
6967 put8int (cts, tr->skip_when_shuffling); /* Is the song used in shuffle mode */
6968 put8int (cts, tr->remember_playback_position); /* Is the song bookmarkable */
6969 put8int (cts, 0);
6970 }
6971 }else {
6972 /* Newer iTunesSD format */
6973 /* Add the main Header */
6974 write_bdhs(fexp);
6975
6976 /* Add the Tracks Header */
6977 if (!write_hths(fexp)){
6978 g_set_error(&fexp->error,
6979 ITDB_FILE_ERROR,
6980 ITDB_FILE_ERROR_CORRUPT,
6981 _("Error writing list of tracks (hths)"));
6982 goto serr;
6983 }
6984
6985 /* Add the Playlist Header */
6986 if(!fexp->error && !write_hphs(fexp)){
6987 g_set_error(&fexp->error,
6988 ITDB_FILE_ERROR,
6989 ITDB_FILE_ERROR_CORRUPT,
6990 _("Error writing playlists (hphs)"));
6991 goto serr;
6992 }
6993 }
6994 if (!fexp->error)
6995 {
6996 if (!wcontents_write (cts))
6997 g_propagate_error (&fexp->error, cts->error);
6998 }
6999 serr:
7000 if (fexp->error)
7001 {
7002 g_propagate_error (error, fexp->error);
7003 result = FALSE;
7004 }
7005 wcontents_free (cts);
7006 g_free (fexp);
7007
7008 /* make sure all buffers are flushed as some people tend to
7009 disconnect as soon as gtkpod returns */
7010 itdb_fsync ();
7011
7012 return result;
7013 }
7014
7015 /*------------------------------------------------------------------*\
7016 * *
7017 * Other file/filename stuff *
7018 * *
7019 \*------------------------------------------------------------------*/
7020
7021 /**
7022 * itdb_rename_files:
7023 * @mp: mount point of the iPod
7024 * @error: return location for a #GError or NULL
7025 *
7026 * Renames/removes some files on the iPod (Playcounts, OTG
7027 * semaphore). May have to be called if you write the iTunesDB not
7028 * directly to the iPod but to some other location and then manually
7029 * copy the file from there to the iPod.
7030 *
7031 * Returns: FALSE on error and sets @error accordingly
7032 */
itdb_rename_files(const gchar * mp,GError ** error)7033 gboolean itdb_rename_files (const gchar *mp, GError **error)
7034 {
7035 const gchar *db_plc_o[] = {"Play Counts", NULL};
7036 const gchar *db_otg[] = {"OTGPlaylistInfo", NULL};
7037 const gchar *db_shu[] = {"iTunesShuffle", NULL};
7038 const gchar *db_ist[] = {"iTunesStats", NULL};
7039 gchar *itunesdir;
7040 gchar *plcname_o;
7041 gchar *plcname_n;
7042 gchar *otgname;
7043 gchar *shuname;
7044 gchar *istname;
7045 gboolean result = TRUE;
7046
7047 g_return_val_if_fail (mp, FALSE);
7048
7049 itunesdir = itdb_get_itunes_dir (mp);
7050
7051 if(!itunesdir)
7052 {
7053 error_no_itunes_dir (mp, error);
7054 return FALSE;
7055 }
7056
7057 plcname_o = itdb_resolve_path (itunesdir, db_plc_o);
7058 plcname_n = g_build_filename (itunesdir,
7059 "Play Counts.bak", NULL);
7060 otgname = itdb_resolve_path (itunesdir, db_otg);
7061 shuname = itdb_resolve_path (itunesdir, db_shu);
7062 istname = itdb_resolve_path (itunesdir, db_ist);
7063
7064 /* rename "Play Counts" to "Play Counts.bak" */
7065 if (plcname_o)
7066 {
7067 if (rename (plcname_o, plcname_n) == -1)
7068 { /* an error occured */
7069 g_set_error (error,
7070 G_FILE_ERROR,
7071 g_file_error_from_errno (errno),
7072 _("Error renaming '%s' to '%s' (%s)."),
7073 plcname_o, plcname_n, g_strerror (errno));
7074 result = FALSE;
7075 }
7076 }
7077
7078 /* remove "OTGPlaylistInfo" (the iPod will remove the remaining
7079 * files */
7080 if (otgname)
7081 {
7082 if (unlink (otgname) == -1)
7083 {
7084 if (error && !*error)
7085 { /* don't overwrite previous error */
7086 g_set_error (error,
7087 G_FILE_ERROR,
7088 g_file_error_from_errno (errno),
7089 _("Error removing '%s' (%s)."),
7090 otgname, g_strerror (errno));
7091 }
7092 result = FALSE;
7093 }
7094 }
7095
7096 /* remove some Shuffle files */
7097 if (shuname)
7098 {
7099 if (unlink (shuname) == -1)
7100 {
7101 if (error && !*error)
7102 { /* don't overwrite previous error */
7103 g_set_error (error,
7104 G_FILE_ERROR,
7105 g_file_error_from_errno (errno),
7106 _("Error removing '%s' (%s)."),
7107 shuname, g_strerror (errno));
7108 }
7109 result = FALSE;
7110 }
7111 }
7112
7113 /* remove some Shuffle files */
7114 if (istname)
7115 {
7116 if (unlink (istname) == -1)
7117 {
7118 if (error && !*error)
7119 { /* don't overwrite previous error */
7120 g_set_error (error,
7121 G_FILE_ERROR,
7122 g_file_error_from_errno (errno),
7123 _("Error removing '%s' (%s)."),
7124 istname, g_strerror (errno));
7125 }
7126 result = FALSE;
7127 }
7128 }
7129
7130 g_free (plcname_o);
7131 g_free (plcname_n);
7132 g_free (otgname);
7133 g_free (shuname);
7134 g_free (istname);
7135 g_free (itunesdir);
7136
7137 return result;
7138 }
7139
7140
7141 /**
7142 * itdb_filename_fs2ipod:
7143 * @filename: a 'PC-style' filename (eg /iPod_Control/Music/f00/test.mp3)
7144 *
7145 * Convert string from casual PC file name to iPod iTunesDB format
7146 * using ':' instead of G_DIR_SEPARATOR_S (i.e. slashes on Unix-like
7147 * systems). @ipod_file is modified in place.
7148 */
itdb_filename_fs2ipod(gchar * ipod_file)7149 void itdb_filename_fs2ipod (gchar *ipod_file)
7150 {
7151 g_strdelimit (ipod_file, G_DIR_SEPARATOR_S, ':');
7152 }
7153
7154 /**
7155 * itdb_filename_ipod2fs:
7156 * @ipod_file: a 'PC-style' filename (eg /iPod_Control/Music/f00/test.mp3)
7157 *
7158 * Convert string from from iPod iTunesDB format to casual PC file
7159 * name using G_DIR_SEPARATOR (ie slashes on Unix-like systems)
7160 * instead of ':'. @ipod_file is modified in place.
7161 */
itdb_filename_ipod2fs(gchar * ipod_file)7162 void itdb_filename_ipod2fs (gchar *ipod_file)
7163 {
7164 g_strdelimit (ipod_file, ":", G_DIR_SEPARATOR);
7165 }
7166
7167 /**
7168 * itdb_set_mountpoint:
7169 * @itdb: an #Itdb_iTunesDB
7170 * @mp: new mount point
7171 *
7172 * Sets the mountpoint of @itdb. Always use this function to set the
7173 * mountpoint of an #Itdb_iTunesDB as it will reset the number of
7174 * available /iPod_Control/Music/F.. dirs. It doesn't attempt to parse
7175 * an iPod database that may be present on the iPod at @mp.
7176 *
7177 * <note><para>Calling this function removes the artwork in the
7178 * #Itdb_iTunesDB database using this #Itdb_Device which was read from the
7179 * iPod.</para></note>.
7180 *
7181 * Since: 0.1.3
7182 */
itdb_set_mountpoint(Itdb_iTunesDB * itdb,const gchar * mp)7183 void itdb_set_mountpoint (Itdb_iTunesDB *itdb, const gchar *mp)
7184 {
7185 g_return_if_fail (itdb);
7186 g_return_if_fail (itdb->device);
7187
7188 itdb_device_set_mountpoint (itdb->device, mp);
7189 itdb->device->musicdirs = 0;
7190 }
7191
7192 /**
7193 * itdb_get_mountpoint:
7194 * @itdb: an #Itdb_iTunesDB
7195 *
7196 * Retrieve a reference to the mountpoint of @itdb
7197 *
7198 * Returns: the @itdb mountpoint, this string shouldn't be freed
7199 * nor modified
7200 *
7201 * Since: 0.4.0
7202 */
itdb_get_mountpoint(Itdb_iTunesDB * itdb)7203 const gchar *itdb_get_mountpoint (Itdb_iTunesDB *itdb)
7204 {
7205 g_return_val_if_fail (itdb, NULL);
7206 g_return_val_if_fail (itdb->device, NULL);
7207 return itdb->device->mountpoint;
7208 }
7209
7210 /* Retrieve a reference to the mountpoint */
itdb_photodb_get_mountpoint(Itdb_PhotoDB * photodb)7211 const gchar *itdb_photodb_get_mountpoint (Itdb_PhotoDB *photodb)
7212 {
7213 g_return_val_if_fail (photodb, NULL);
7214 g_return_val_if_fail (photodb->device, NULL);
7215 return photodb->device->mountpoint;
7216 }
7217
7218 /**
7219 * itdb_musicdirs_number:
7220 * @itdb: an #Itdb_iTunesDB
7221 *
7222 * Determine the number of F.. directories in iPod_Control/Music.
7223 *
7224 * If @itdb->musicdirs is already set, simply return the previously
7225 * determined number. Otherwise count the directories first and set
7226 * @itdb->musicdirs.
7227 *
7228 * Returns: max number of directories in iPod_Control/Music
7229 *
7230 * Since: 0.1.3
7231 */
itdb_musicdirs_number(Itdb_iTunesDB * itdb)7232 gint itdb_musicdirs_number (Itdb_iTunesDB *itdb)
7233 {
7234 g_return_val_if_fail (itdb, 0);
7235 g_return_val_if_fail (itdb->device, 0);
7236
7237 return itdb_device_musicdirs_number (itdb->device);
7238 }
7239
7240 /**
7241 * itdb_cp_get_dest_filename:
7242 * @track: track to transfer or NULL
7243 * @mountpoint: mountpoint of your iPod or NULL
7244 * @filename: the source file
7245 * @error: return location for a #GError or NULL
7246 *
7247 * Creates a valid filename on the iPod where @filename can be copied.
7248 *
7249 * You must provide either @track or @mountpoint. Providing @track is
7250 * not thread-safe (accesses track->itdb->device and may even write to
7251 * track->itdb->device). Providing @mountpoint is thread-safe but
7252 * slightly slower because the number of music directories is counted
7253 * each time the function is called.
7254 *
7255 * You can use itdb_cp() to copy the track to the iPod or implement
7256 * your own copy function. After the file was copied you have to call
7257 * itdb_cp_finalize() to obtain relevant update information for
7258 * #Itdb_Track.
7259 *
7260 * Returns: a valid filename on the iPod where @filename can be
7261 * copied or NULL in case of an error. In that case @error is set
7262 * accordingly. You must free the filename when it is no longer
7263 * needed.
7264 *
7265 * Since: 0.5.0
7266 */
itdb_cp_get_dest_filename(Itdb_Track * track,const gchar * mountpoint,const gchar * filename,GError ** error)7267 gchar *itdb_cp_get_dest_filename (Itdb_Track *track,
7268 const gchar *mountpoint,
7269 const gchar *filename,
7270 GError **error)
7271 {
7272 gchar *ipod_fullfile = NULL;
7273
7274 /* either supply mountpoint or track */
7275 g_return_val_if_fail (mountpoint || track, NULL);
7276 /* if mountpoint is not set, track->itdb is required */
7277 g_return_val_if_fail (mountpoint || track->itdb, NULL);
7278 g_return_val_if_fail (filename, NULL);
7279
7280 if (!mountpoint)
7281 {
7282 mountpoint = itdb_get_mountpoint (track->itdb);
7283 }
7284
7285 if (!mountpoint)
7286 {
7287 g_set_error (error,
7288 ITDB_FILE_ERROR,
7289 ITDB_FILE_ERROR_NOTFOUND,
7290 _("Mountpoint not set."));
7291 return NULL;
7292 }
7293
7294 /* If track->ipod_path exists, we use that one instead. */
7295 if (track)
7296 {
7297 ipod_fullfile = itdb_filename_on_ipod (track);
7298 }
7299
7300 if (!ipod_fullfile)
7301 {
7302 gint dir_num, musicdirs_number;
7303 gchar *dest_components[] = {NULL, NULL, NULL};
7304 gchar *parent_dir_filename, *music_dir;
7305 gchar *original_suffix;
7306 gchar dir_num_str[6];
7307 gint32 oops = 0;
7308 gint32 rand = g_random_int_range (0, 899999); /* 0 to 900000 */
7309
7310 music_dir = itdb_get_music_dir (mountpoint);
7311 if (!music_dir)
7312 {
7313 error_no_music_dir (mountpoint, error);
7314 return NULL;
7315 }
7316
7317 if (track)
7318 {
7319 musicdirs_number = itdb_musicdirs_number (track->itdb);
7320 }
7321 else
7322 {
7323 musicdirs_number = itdb_musicdirs_number_by_mountpoint (mountpoint);
7324 }
7325 if (musicdirs_number <= 0)
7326 {
7327 g_set_error (error,
7328 ITDB_FILE_ERROR,
7329 ITDB_FILE_ERROR_NOTFOUND,
7330 _("No 'F..' directories found in '%s'."),
7331 music_dir);
7332 g_free (music_dir);
7333 return NULL;
7334 }
7335
7336 dir_num = g_random_int_range (0, musicdirs_number);
7337
7338 g_snprintf (dir_num_str, 6, "F%02d", dir_num);
7339 dest_components[0] = dir_num_str;
7340
7341 parent_dir_filename =
7342 itdb_resolve_path (music_dir, (const gchar **)dest_components);
7343 if(parent_dir_filename == NULL)
7344 {
7345 /* Can't find the F%02d directory */
7346 gchar *str = g_build_filename (music_dir,
7347 dest_components[0], NULL);
7348 g_set_error (error,
7349 ITDB_FILE_ERROR,
7350 ITDB_FILE_ERROR_NOTFOUND,
7351 _("Path not found: '%s'."),
7352 str);
7353 g_free (str);
7354 g_free (music_dir);
7355 return NULL;
7356 }
7357
7358 /* we need the original suffix of pcfile to construct a correct ipod
7359 filename */
7360 original_suffix = strrchr (filename, '.');
7361 /* If there is no ".mp3", ".m4a" etc, set original_suffix to empty
7362 string. Note: the iPod will most certainly ignore this file... */
7363 if (!original_suffix) original_suffix = "";
7364
7365 /* use lower-case version of extension as some iPods seem to
7366 choke on upper-case extension. */
7367 original_suffix = g_ascii_strdown (original_suffix, -1);
7368
7369 do
7370 { /* we need to loop until we find an unused filename */
7371 dest_components[1] =
7372 g_strdup_printf("libgpod%06d%s",
7373 rand + oops, original_suffix);
7374 ipod_fullfile = itdb_resolve_path (
7375 parent_dir_filename,
7376 (const gchar **)&dest_components[1]);
7377 if(ipod_fullfile)
7378 { /* already exists -- try next */
7379 g_free(ipod_fullfile);
7380 ipod_fullfile = NULL;
7381 }
7382 else
7383 { /* found unused file -- build filename */
7384 ipod_fullfile = g_build_filename (parent_dir_filename,
7385 dest_components[1], NULL);
7386 }
7387 g_free (dest_components[1]);
7388 ++oops;
7389 } while (!ipod_fullfile);
7390 g_free(parent_dir_filename);
7391 g_free (music_dir);
7392 g_free (original_suffix);
7393 }
7394
7395 return ipod_fullfile;
7396 }
7397
7398 /**
7399 * itdb_cp_finalize:
7400 * @track: track to update or NULL
7401 * @mountpoint: mountpoint of your iPod or NULL
7402 * @dest_filename: the name of the file on the iPod copied to
7403 * @error: return location for a #GError or NULL
7404 *
7405 * Updates information in @track necessary for the iPod.
7406 *
7407 * You must supply either @track or @mountpoint. If @track == NULL, a
7408 * new track structure is created that must be freed with
7409 * itdb_track_free() when it is no longer needed.
7410 *
7411 * The following @track fields are updated:
7412 *
7413 * <itemizedlist>
7414 * <listitem>
7415 * ipod_path
7416 * </listitem>
7417 * <listitem>
7418 * filetype_marker
7419 * </listitem>
7420 * <listitem>
7421 * transferred
7422 * </listitem>
7423 * <listitem>
7424 * size
7425 * </listitem>
7426 * </itemizedlist>
7427 *
7428 * Returns: on success a pointer to the #Itdb_Track item passed
7429 * or a new #Itdb_Track item if @track was NULL. In the latter case
7430 * you must free the memory using itdb_track_free() when the item is
7431 * no longer used. If an error occurs NULL is returned and @error is
7432 * set accordingly. Errors occur when @dest_filename cannot be
7433 * accessed or the mountpoint is not set.
7434 *
7435 * Since: 0.5.0
7436 */
itdb_cp_finalize(Itdb_Track * track,const gchar * mountpoint,const gchar * dest_filename,GError ** error)7437 Itdb_Track *itdb_cp_finalize (Itdb_Track *track,
7438 const gchar *mountpoint,
7439 const gchar *dest_filename,
7440 GError **error)
7441 {
7442 const gchar *suffix;
7443 Itdb_Track *use_track;
7444 gint i, mplen;
7445 struct stat statbuf;
7446
7447 /* either supply mountpoint or track */
7448 g_return_val_if_fail (mountpoint || track, NULL);
7449 /* if mountpoint is not set, track->itdb is required */
7450 g_return_val_if_fail (mountpoint || track->itdb, NULL);
7451 g_return_val_if_fail (dest_filename, NULL);
7452
7453 if (!mountpoint)
7454 {
7455 mountpoint = itdb_get_mountpoint (track->itdb);
7456 }
7457
7458 if (!mountpoint)
7459 {
7460 g_set_error (error,
7461 ITDB_FILE_ERROR,
7462 ITDB_FILE_ERROR_NOTFOUND,
7463 _("Mountpoint not set."));
7464 return NULL;
7465 }
7466
7467 if (stat (dest_filename, &statbuf) == -1)
7468 {
7469 g_set_error (error,
7470 G_FILE_ERROR,
7471 g_file_error_from_errno (errno),
7472 _("'%s' could not be accessed (%s)."),
7473 dest_filename, g_strerror (errno));
7474 return NULL;
7475 }
7476
7477 if (strlen (mountpoint) >= strlen (dest_filename))
7478 {
7479 g_set_error (error,
7480 ITDB_FILE_ERROR,
7481 ITDB_FILE_ERROR_CORRUPT,
7482 _("Destination file '%s' does not appear to be on the iPod mounted at '%s'."),
7483 dest_filename, mountpoint);
7484 return NULL;
7485 }
7486
7487 if (!track)
7488 {
7489 use_track = itdb_track_new ();
7490 }
7491 else
7492 {
7493 use_track = track;
7494 }
7495
7496 use_track->transferred = TRUE;
7497 use_track->size = statbuf.st_size;
7498
7499 /* we need the original suffix of pcfile to construct a correct ipod
7500 filename */
7501 suffix = strrchr (dest_filename, '.');
7502 /* If there is no ".mp3", ".m4a" etc, set original_suffix to empty
7503 string. Note: the iPod will most certainly ignore this file... */
7504 if (!suffix) suffix = ".";
7505
7506 /* set filetype from the suffix, e.g. '.mp3' -> 'MP3 ' */
7507 use_track->filetype_marker = 0;
7508 for (i=1; i<=4; ++i) /* start with i=1 to skip the '.' */
7509 {
7510 use_track->filetype_marker = use_track->filetype_marker << 8;
7511 if (strlen (suffix) > i)
7512 use_track->filetype_marker |= g_ascii_toupper (suffix[i]);
7513 else
7514 use_track->filetype_marker |= ' ';
7515 }
7516
7517 /* now extract filepath for use_track->ipod_path from ipod_fullfile */
7518 /* ipod_path must begin with a '/' */
7519 g_free (use_track->ipod_path);
7520 mplen = strlen (mountpoint); /* length of mountpoint in bytes */
7521 if (dest_filename[mplen] == G_DIR_SEPARATOR)
7522 {
7523 use_track->ipod_path = g_strdup (&dest_filename[mplen]);
7524 }
7525 else
7526 {
7527 use_track->ipod_path = g_strdup_printf ("%c%s", G_DIR_SEPARATOR,
7528 &dest_filename[mplen]);
7529 }
7530 /* convert to iPod type */
7531 itdb_filename_fs2ipod (use_track->ipod_path);
7532
7533 return use_track;
7534 }
7535
7536 /**
7537 * itdb_cp_track_to_ipod:
7538 * @track: the #Itdb_Track to copy (containing @filename metadata)
7539 * @filename: the source file
7540 * @error: return location for a #GError or NULL
7541 *
7542 * Copy one track to the iPod. The PC filename is @filename
7543 * and is taken literally.
7544 *
7545 * The mountpoint of the iPod (in local encoding) must have been set
7546 * with itdb_set_mountpoint() (done automatically when reading an
7547 * iTunesDB).
7548 *
7549 * If @track->transferred is set to TRUE, nothing is done. Upon
7550 * successful transfer @track->transferred is set to TRUE.
7551 *
7552 * For storage, the directories "F00 ... Fnn" will be used randomly.
7553 *
7554 * The filename is constructed as "libgpod@random_number" and copied
7555 * to @track->ipod_path. If this file already exists, @random_number
7556 * is adjusted until an unused filename is found.
7557 *
7558 * If @track->ipod_path is already set, this one will be used
7559 * instead. If a file with this name already exists, it will be
7560 * overwritten.
7561 *
7562 * @track->filetype_marker is set according to the filename extension
7563 *
7564 * Returns: TRUE on success, FALSE on error, in which case @error is
7565 * set accordingly.
7566 */
itdb_cp_track_to_ipod(Itdb_Track * track,const gchar * filename,GError ** error)7567 gboolean itdb_cp_track_to_ipod (Itdb_Track *track,
7568 const gchar *filename, GError **error)
7569 {
7570 gchar *dest_filename;
7571 gboolean result = FALSE;
7572
7573 g_return_val_if_fail (track, FALSE);
7574 g_return_val_if_fail (track->itdb, FALSE);
7575 g_return_val_if_fail (itdb_get_mountpoint (track->itdb), FALSE);
7576 g_return_val_if_fail (filename, FALSE);
7577
7578 if(track->transferred) return TRUE; /* nothing to do */
7579
7580 dest_filename = itdb_cp_get_dest_filename (track, NULL, filename, error);
7581
7582 if (dest_filename)
7583 {
7584 if (itdb_cp (filename, dest_filename, error))
7585 {
7586 if (itdb_cp_finalize (track, NULL, dest_filename, error))
7587 {
7588 result = TRUE;
7589 }
7590 }
7591 g_free (dest_filename);
7592 }
7593
7594 return result;
7595 }
7596
7597 /**
7598 * itdb_filename_on_ipod:
7599 * @track: an #Itdb_Track
7600 *
7601 * Get the full iPod filename as stored in @track.
7602 *
7603 * <note>
7604 * NULL is returned when the file does not exist.
7605 * </note>
7606 *
7607 * <note>
7608 * This code works around a problem on some systems (see
7609 * itdb_resolve_path()) and might return a filename with different
7610 * case than the original filename. Don't copy it back to @track if
7611 * you can avoid it.
7612 * </note>
7613 *
7614 * Returns: full filename to @track on the iPod or NULL if no
7615 * filename is set in @track. Must be freed with g_free() after use.
7616 */
itdb_filename_on_ipod(Itdb_Track * track)7617 gchar *itdb_filename_on_ipod (Itdb_Track *track)
7618 {
7619 gchar *result = NULL;
7620 gchar *buf;
7621 const gchar *mp;
7622
7623 g_return_val_if_fail (track, NULL);
7624
7625 if (!track->ipod_path || !*track->ipod_path)
7626 { /* No filename set */
7627 return NULL;
7628 }
7629
7630 g_return_val_if_fail (track->itdb, NULL);
7631
7632 if (!itdb_get_mountpoint (track->itdb)) return NULL;
7633
7634 mp = itdb_get_mountpoint (track->itdb);
7635
7636 buf = g_strdup (track->ipod_path);
7637 itdb_filename_ipod2fs (buf);
7638 result = g_build_filename (mp, buf, NULL);
7639 g_free (buf);
7640
7641 if (!g_file_test (result, G_FILE_TEST_EXISTS))
7642 {
7643 gchar **components = g_strsplit (track->ipod_path,":",10);
7644 g_free (result);
7645 result = itdb_resolve_path (mp, (const gchar **)components);
7646 g_strfreev (components);
7647 }
7648
7649 return result;
7650 }
7651
7652 /* Use open instead of fopen. fwrite is really slow on the Mac. */
7653 /**
7654 * itdb_cp:
7655 * @from_file: source file
7656 * @to_file: destination file
7657 * @error: return location for a #GError or NULL
7658 *
7659 * Copy file @from_file to @to_file.
7660 *
7661 * Returns: TRUE on success, FALSE on error, in which case @error is
7662 * set accordingly.
7663 */
itdb_cp(const gchar * from_file,const gchar * to_file,GError ** error)7664 gboolean itdb_cp (const gchar *from_file, const gchar *to_file,
7665 GError **error)
7666 {
7667 #ifndef O_BINARY
7668 #define O_BINARY 0
7669 #endif
7670 gchar *data;
7671 glong bread, bwrite;
7672 int file_in = -1;
7673 int file_out = -1;
7674
7675 #if ITUNESDB_DEBUG
7676 fprintf(stderr, "Entered itunesdb_cp: '%s', '%s'\n", from_file, to_file);
7677 #endif
7678
7679 g_return_val_if_fail (from_file, FALSE);
7680 g_return_val_if_fail (to_file, FALSE);
7681
7682 data = g_malloc (ITUNESDB_COPYBLK);
7683
7684 file_in = g_open (from_file, O_RDONLY | O_BINARY, 0);
7685 if (file_in < 0)
7686 {
7687 g_set_error (error,
7688 G_FILE_ERROR,
7689 g_file_error_from_errno (errno),
7690 _("Error opening '%s' for reading (%s)."),
7691 from_file, g_strerror (errno));
7692 goto err_out;
7693 }
7694
7695 file_out = g_open (to_file, O_CREAT|O_WRONLY|O_TRUNC|O_BINARY, 0777);
7696 if (file_out < 0)
7697 {
7698 g_set_error (error,
7699 G_FILE_ERROR,
7700 g_file_error_from_errno (errno),
7701 _("Error opening '%s' for writing (%s)."),
7702 to_file, g_strerror (errno));
7703 goto err_out;
7704 }
7705
7706 do {
7707 bread = read (file_in, data, ITUNESDB_COPYBLK);
7708 #if ITUNESDB_DEBUG
7709 fprintf(stderr, "itunesdb_cp: read %ld bytes\n", bread);
7710 #endif
7711 if (bread < 0)
7712 {
7713 /* error -- not end of file! */
7714 g_set_error (error,
7715 G_FILE_ERROR,
7716 g_file_error_from_errno (errno),
7717 _("Error while reading from '%s' (%s)."),
7718 from_file, g_strerror (errno));
7719 goto err_out;
7720 }
7721 else
7722 {
7723 bwrite = write (file_out, data, bread);
7724 #if ITUNESDB_DEBUG
7725 fprintf(stderr, "itunesdb_cp: wrote %ld bytes\n", bwrite);
7726 #endif
7727 if (bwrite != bread)
7728 {
7729 g_set_error (error,
7730 G_FILE_ERROR,
7731 g_file_error_from_errno (errno),
7732 _("Error while writing to '%s' (%s)."),
7733 to_file, g_strerror (errno));
7734 goto err_out;
7735 }
7736 }
7737 } while (bread != 0);
7738
7739 if (close (file_in) != 0)
7740 {
7741 file_in = -1;
7742 g_set_error (error,
7743 G_FILE_ERROR,
7744 g_file_error_from_errno (errno),
7745 _("Error when closing '%s' (%s)."),
7746 from_file, g_strerror (errno));
7747 goto err_out;
7748 }
7749 if (close (file_out) != 0)
7750 {
7751 file_out = -1;
7752 g_set_error (error,
7753 G_FILE_ERROR,
7754 g_file_error_from_errno (errno),
7755 _("Error when closing '%s' (%s)."),
7756 to_file, g_strerror (errno));
7757 goto err_out;
7758 }
7759 g_free (data);
7760 return TRUE;
7761
7762 err_out:
7763 if (file_in >= 0) close (file_in);
7764 if (file_out >= 0) close (file_out);
7765 g_unlink (to_file);
7766 g_free (data);
7767 return FALSE;
7768 }
7769
7770 /**
7771 * itdb_get_control_dir:
7772 * @mountpoint: the iPod mountpoint
7773 *
7774 * Get the i*_Control directory. Observed values are 'iPod_Control'
7775 * for standard iPods and 'iTunes/iTunes_Control' for mobile
7776 * applications.
7777 *
7778 * Returns: path to the control dir or NULL if non-existent. Must
7779 * g_free() after use.
7780 *
7781 * Since: 0.4.0
7782 */
itdb_get_control_dir(const gchar * mountpoint)7783 gchar *itdb_get_control_dir (const gchar *mountpoint)
7784 {
7785 gchar *p_iphone[] = {"iTunes_Control", NULL};
7786 gchar *p_ipod[] = {"iPod_Control", NULL};
7787 gchar *p_mobile[] = {"iTunes", "iTunes_Control", NULL};
7788 /* Use an array with all possibilities, so further extension will
7789 be easy. It is important that checking for the iTunes_Control
7790 directory be done before the iPod_Control directory because
7791 some devices actually have both directories. The iTunes_Control
7792 directory is correct in these cases. This happens for devices
7793 that Apple shipped with the iPod_Control as the control directory
7794 and later switched to iTunes_Control instead. iTunes appears to
7795 remove files from the old iPod_Control directory but leave the
7796 directory structure intact. Finding this empty directory structure
7797 first will result in a failure to find files. */
7798 gchar **paths[] = {p_iphone, p_ipod, p_mobile, NULL};
7799 gchar ***ptr;
7800 gchar *result = NULL;
7801
7802 g_return_val_if_fail (mountpoint, NULL);
7803
7804 for (ptr=paths; *ptr && !result; ++ptr)
7805 {
7806 result = itdb_resolve_path (mountpoint, (const gchar **)*ptr);
7807 }
7808 return result;
7809 }
7810
7811 /**
7812 * itdb_get_dir:
7813 * @mountpoint: the iPod mountpoint
7814 * @dir: a directory
7815 *
7816 * Retrieve the directory @dir by first calling itdb_get_control_dir()
7817 * and then adding @dir
7818 *
7819 * Returns: path to @dir or NULL if non-existent. Must g_free()
7820 * after use.
7821 */
itdb_get_dir(const gchar * mountpoint,const gchar * dir)7822 static gchar *itdb_get_dir (const gchar *mountpoint, const gchar *dir)
7823 {
7824 gchar *control_dir;
7825 gchar *result = NULL;
7826
7827 g_return_val_if_fail (mountpoint, NULL);
7828 g_return_val_if_fail (dir, NULL);
7829
7830 control_dir = itdb_get_control_dir (mountpoint);
7831 if (control_dir)
7832 {
7833 const gchar *p_dir[] = {NULL, NULL};
7834 p_dir[0] = dir;
7835 result = itdb_resolve_path (control_dir, p_dir);
7836 g_free (control_dir);
7837 }
7838 return result;
7839 }
7840
7841 /**
7842 * itdb_get_path:
7843 * @dir: a directory
7844 * @file: a file
7845 *
7846 * Retrieve a path to the @file in @dir
7847 *
7848 * Returns: path to the @file or NULL if non-existent. Must g_free()
7849 * after use.
7850 *
7851 * Since: 0.4.0
7852 */
itdb_get_path(const gchar * dir,const gchar * file)7853 gchar *itdb_get_path (const gchar *dir, const gchar *file)
7854 {
7855 const gchar *p_file[] = {NULL, NULL};
7856
7857 g_return_val_if_fail (dir, NULL);
7858
7859 p_file[0] = file;
7860
7861 return itdb_resolve_path (dir, p_file);
7862 }
7863
7864 /**
7865 * itdb_get_itunes_dir:
7866 * @mountpoint: the iPod mountpoint
7867 *
7868 * Retrieve the iTunes directory (containing the iTunesDB) by first
7869 * calling itdb_get_control_dir() and then adding 'iTunes'
7870 *
7871 * Returns: path to the iTunes directory or NULL if non-existent.
7872 * Must g_free() after use.
7873 *
7874 * Since: 0.4.0
7875 */
itdb_get_itunes_dir(const gchar * mountpoint)7876 gchar *itdb_get_itunes_dir (const gchar *mountpoint)
7877 {
7878 g_return_val_if_fail (mountpoint, NULL);
7879
7880 return itdb_get_dir (mountpoint, "iTunes");
7881 }
7882
7883 /**
7884 * itdb_get_music_dir:
7885 * @mountpoint: the iPod mountpoint
7886 *
7887 * Retrieve the Music directory (containing the Fnn dirs) by first
7888 * calling itdb_get_control_dir() and then adding 'Music'
7889 *
7890 * Returns: path to the Music directory or NULL if
7891 * non-existent. Must g_free() after use.
7892 */
itdb_get_music_dir(const gchar * mountpoint)7893 gchar *itdb_get_music_dir (const gchar *mountpoint)
7894 {
7895 g_return_val_if_fail (mountpoint, NULL);
7896
7897 return itdb_get_dir (mountpoint, "Music");
7898 }
7899
7900 /**
7901 * itdb_get_device_dir:
7902 * @mountpoint: the iPod mountpoint
7903 *
7904 * Retrieve the Device directory (containing the SysInfo file) by
7905 * first calling itdb_get_control_dir() and then adding 'Device'
7906 *
7907 * Returns: path to the Device directory or NULL if
7908 * non-existent. Must g_free() after use.
7909 *
7910 * Since: 0.4.0
7911 */
itdb_get_device_dir(const gchar * mountpoint)7912 gchar *itdb_get_device_dir (const gchar *mountpoint)
7913 {
7914 g_return_val_if_fail (mountpoint, NULL);
7915
7916 return itdb_get_dir (mountpoint, "Device");
7917 }
7918
7919 /**
7920 * itdb_get_artwork_dir:
7921 * @mountpoint: the iPod mountpoint
7922 *
7923 * Retrieve the Artwork directory (containing the ArtworDB) by
7924 * first calling itdb_get_control_dir() and then adding 'Artwork'
7925 *
7926 * Returns: path to the Artwork directory or NULL if
7927 * non-existent. Must g_free() after use.
7928 *
7929 * Since: 0.4.0
7930 */
itdb_get_artwork_dir(const gchar * mountpoint)7931 gchar *itdb_get_artwork_dir (const gchar *mountpoint)
7932 {
7933 g_return_val_if_fail (mountpoint, NULL);
7934
7935 return itdb_get_dir (mountpoint, "Artwork");
7936 }
7937
7938 /**
7939 * itdb_get_itunesdb_path:
7940 * @mountpoint: the iPod mountpoint
7941 *
7942 * Retrieve a path to the iTunesDB
7943 *
7944 * Returns: path to the iTunesDB or NULL if non-existent. Must g_free()
7945 * after use.
7946 *
7947 * Since: 0.4.0
7948 */
itdb_get_itunesdb_path(const gchar * mountpoint)7949 gchar *itdb_get_itunesdb_path (const gchar *mountpoint)
7950 {
7951 gchar *itunes_dir, *path=NULL;
7952
7953 g_return_val_if_fail (mountpoint, NULL);
7954
7955 itunes_dir = itdb_get_itunes_dir (mountpoint);
7956
7957 if (itunes_dir)
7958 {
7959 path = itdb_get_path (itunes_dir, "iTunesCDB");
7960 if(!path) {
7961 path = itdb_get_path (itunes_dir, "iTunesDB");
7962 }
7963 g_free (itunes_dir);
7964 }
7965
7966 return path;
7967 }
7968
7969 /**
7970 * itdb_get_itunescdb_path:
7971 * @mountpoint: the iPod mountpoint
7972 *
7973 * Retrieve a path to the iTunesCDB. The iTunesCDB is a compressed version
7974 * of the iTunesDB which can be found on iPhones/iPod Touch with firmware 3.0
7975 *
7976 *
7977 * Returns: path to the iTunesCDB or NULL if non-existent. Must g_free()
7978 * after use.
7979 *
7980 * Since: 0.4.0
7981 */
itdb_get_itunescdb_path(const gchar * mountpoint)7982 gchar *itdb_get_itunescdb_path (const gchar *mountpoint)
7983 {
7984 gchar *itunes_dir, *path=NULL;
7985
7986 g_return_val_if_fail (mountpoint, NULL);
7987
7988 itunes_dir = itdb_get_itunes_dir (mountpoint);
7989
7990 if (itunes_dir)
7991 {
7992 path = itdb_get_path (itunes_dir, "iTunesCDB");
7993 g_free (itunes_dir);
7994 }
7995
7996 return path;
7997 }
7998
7999 /**
8000 * itdb_get_itunessd_path:
8001 * @mountpoint: the iPod mountpoint
8002 *
8003 * Retrieve a path to the iTunesSD
8004 *
8005 * Returns: path to the iTunesSD or NULL if non-existent. Must g_free()
8006 * after use.
8007 *
8008 * Since: 0.4.0
8009 */
itdb_get_itunessd_path(const gchar * mountpoint)8010 gchar *itdb_get_itunessd_path (const gchar *mountpoint)
8011 {
8012 gchar *itunes_dir, *path=NULL;
8013
8014 g_return_val_if_fail (mountpoint, NULL);
8015
8016 itunes_dir = itdb_get_itunes_dir (mountpoint);
8017
8018 if (itunes_dir)
8019 {
8020 path = itdb_get_path (itunes_dir, "iTunesSD");
8021 g_free (itunes_dir);
8022 }
8023
8024 return path;
8025 }
8026
8027 /**
8028 * itdb_get_artworkdb_path:
8029 * @mountpoint: the iPod mountpoint
8030 *
8031 * Retrieve a path to the ArtworkDB
8032 *
8033 * Returns: path to the ArtworkDB or NULL if non-existent. Must g_free()
8034 * after use.
8035 *
8036 * Since: 0.4.0
8037 */
itdb_get_artworkdb_path(const gchar * mountpoint)8038 gchar *itdb_get_artworkdb_path (const gchar *mountpoint)
8039 {
8040 gchar *itunes_dir, *path=NULL;
8041
8042 g_return_val_if_fail (mountpoint, NULL);
8043
8044 itunes_dir = itdb_get_artwork_dir (mountpoint);
8045
8046 if (itunes_dir)
8047 {
8048 path = itdb_get_path (itunes_dir, "ArtworkDB");
8049 g_free (itunes_dir);
8050 }
8051
8052 return path;
8053 }
8054
8055
8056 /*------------------------------------------------------------------*\
8057 * *
8058 * Timestamp stuff *
8059 * *
8060 \*------------------------------------------------------------------*/
8061
8062 /**
8063 * itdb_time_get_mac_time:
8064 *
8065 * Gets the current time in a format appropriate for storing in the libgpod
8066 * data structures
8067 *
8068 * Returns: current time
8069 *
8070 * Deprecated: kept for compatibility with older code, directly use
8071 * g_get_current_time() or time(NULL) instead
8072 */
itdb_time_get_mac_time(void)8073 time_t itdb_time_get_mac_time (void)
8074 {
8075 GTimeVal time;
8076
8077 g_get_current_time (&time);
8078
8079 return time.tv_sec;
8080 }
8081
8082 /**
8083 * itdb_time_mac_to_host:
8084 * @time: time expressed in libgpod format
8085 *
8086 * Converts a timestamp from libgpod format to host system timestamp.
8087 *
8088 * Returns: timestamp for the host system
8089 *
8090 * Deprecated: It's been kept for compatibility with older code, but this
8091 * function is now a no-op
8092 */
itdb_time_mac_to_host(time_t time)8093 time_t itdb_time_mac_to_host (time_t time)
8094 {
8095 return time;
8096 }
8097
8098 /**
8099 * itdb_time_host_to_mac:
8100 * @time: time expressed in host unit
8101 *
8102 * Convert host system timestamp to libgpod format timestamp
8103 *
8104 * Returns: a libgpod timestamp
8105 *
8106 * Deprecated: It's been kept for compatibility with older code, but this
8107 * function is now a no-op
8108 */
itdb_time_host_to_mac(time_t time)8109 time_t itdb_time_host_to_mac (time_t time)
8110 {
8111 return time;
8112 }
8113
8114 /**
8115 * itdb_init_ipod:
8116 * @mountpoint: the iPod mountpoint
8117 * @model_number: the iPod model number, can be NULL
8118 * @ipod_name: the name to give to the iPod. Will be displayed in
8119 * gtkpod or itunes
8120 * @error: return location for a #GError or NULL
8121 *
8122 * Initialise an iPod device from scratch. The function attempts to
8123 * create a blank database, complete with master playlist and device
8124 * information as well as the directory structure required for the
8125 * type of iPod.
8126 * @model_number is used to tell libgpod about the exact iPod
8127 * model, which is needed for proper artwork writing. @model_number can be
8128 * found from the table returned by itdb_device_get_ipod_info_table (for
8129 * example). On recent distros with iPods released
8130 * in the last few years (starting with the iPod Color), it should be fine
8131 * to pass in a NULL @model_number while still getting artwork writing.
8132 *
8133 * Returns: TRUE when successful, FALSE if a failure has occurred.
8134 *
8135 * Since: 0.4.0
8136 */
itdb_init_ipod(const gchar * mountpoint,const gchar * model_number,const gchar * ipod_name,GError ** error)8137 gboolean itdb_init_ipod (const gchar *mountpoint,
8138 const gchar *model_number,
8139 const gchar *ipod_name,
8140 GError **error)
8141 {
8142 gboolean writeok;
8143 Itdb_iTunesDB *itdb = NULL;
8144 Itdb_Playlist *mpl = NULL;
8145 gchar *path;
8146
8147 g_return_val_if_fail (mountpoint, FALSE);
8148
8149 /* Create new blank itdb database for writing to iPod */
8150 itdb = itdb_new();
8151
8152 /* Assign iPod device reference to new database */
8153 itdb_set_mountpoint(itdb, mountpoint);
8154
8155 /* Insert model_number into sysinfo file if present
8156 * The model number can be extracted in a couple of ways:
8157 * - use the read_sysinfo_file function
8158 * - use libipoddevice and hal to get the model
8159 * (as far as I know, libipoddevice will also
8160 * read the sysinfo file, complemented by some
8161 * guessing).
8162 */
8163 if (model_number)
8164 {
8165 itdb_device_set_sysinfo (itdb->device,
8166 "ModelNumStr", model_number);
8167 }
8168
8169 /* Create the remaining directories resident on blank ipod */
8170 writeok = itdb_create_directories(itdb->device, error);
8171 if(! writeok)
8172 {
8173 return FALSE;
8174 }
8175
8176 /* Create a new playlist with the desired name of the ipod
8177 * and set it as the mpl */
8178 if (ipod_name == NULL)
8179 {
8180 mpl = itdb_playlist_new(_("iPod"), FALSE);
8181 }
8182 else
8183 {
8184 mpl = itdb_playlist_new(ipod_name, FALSE);
8185 }
8186 itdb_playlist_set_mpl(mpl);
8187 itdb_playlist_add(itdb, mpl, -1);
8188
8189 /* Write both the iTunesDB and iTunesSD files to the new ipod,
8190 * unless they already exist */
8191 path = itdb_get_itunesdb_path (mountpoint);
8192 if (!path)
8193 {
8194 writeok = itdb_write(itdb, error);
8195 if(! writeok)
8196 {
8197 itdb_free (itdb);
8198 return FALSE;
8199 }
8200 }
8201 g_free (path);
8202
8203 /* If model is a shuffle or the model is undetermined,
8204 * ie. @model_number is NULL, then create the itunesSD database
8205 */
8206 if(!model_number || itdb_device_is_shuffle (itdb->device))
8207 {
8208 path = itdb_get_itunessd_path (mountpoint);
8209 if (!path)
8210 {
8211 writeok = itdb_shuffle_write(itdb, error);
8212 if(! writeok)
8213 {
8214 itdb_free (itdb);
8215 return FALSE;
8216 }
8217 }
8218 g_free (path);
8219 }
8220 itdb_free (itdb);
8221 return TRUE;
8222 }
8223
8224 /**
8225 * itdb_chapterdata_build_chapter_blob:
8226 * @chapterdata: Itdb_Chapterdata pointer of chapter data to be encoded
8227 *
8228 * Creates an iTunesDB binary blob of chapter data from @chapterdata.
8229 * This helper function is used by both mk_mhod() in itdb_itunesdb.c
8230 * and mk_Extras() in itdb_sqlite.c, so take care when updating to
8231 * maintain compatibility with both chapterdata blobs.
8232 *
8233 * NOTE: Caller must call g_byte_array_free(chapter_blob, TRUE) on the
8234 * returned chapter_blob
8235 *
8236 * Returns: a binary itdb blob of chapter data (to be freed by the caller)
8237 *
8238 * Since: 0.7.95
8239 */
itdb_chapterdata_build_chapter_blob(Itdb_Chapterdata * chapterdata,gboolean reversed)8240 G_GNUC_INTERNAL GByteArray *itdb_chapterdata_build_chapter_blob(Itdb_Chapterdata *chapterdata, gboolean reversed)
8241 {
8242 WContents *cts;
8243 GByteArray * chapter_blob = NULL;
8244
8245 cts = wcontents_new("");
8246 cts->reversed = reversed;
8247 cts->pos = 0;
8248
8249 itdb_chapterdata_build_chapter_blob_internal (cts, chapterdata);
8250
8251 chapter_blob = g_byte_array_sized_new(cts->pos);
8252 g_byte_array_append(chapter_blob, (guint8 *)cts->contents, cts->pos);
8253 wcontents_free(cts);
8254 return chapter_blob;
8255 }
8256
8257
8258 /*------------------------------------------------------------------*\
8259 * *
8260 * Some functions to access Itdb_DB safely *
8261 * *
8262 \*------------------------------------------------------------------*/
8263 G_GNUC_INTERNAL
db_get_itunesdb(Itdb_DB * db)8264 Itdb_iTunesDB *db_get_itunesdb (Itdb_DB *db)
8265 {
8266 g_return_val_if_fail (db, NULL);
8267 g_return_val_if_fail (db->db_type == DB_TYPE_ITUNES, NULL);
8268
8269 return db->db.itdb;
8270 }
8271
8272 G_GNUC_INTERNAL
db_get_photodb(Itdb_DB * db)8273 Itdb_PhotoDB *db_get_photodb (Itdb_DB *db)
8274 {
8275 g_return_val_if_fail (db, NULL);
8276 g_return_val_if_fail (db->db_type == DB_TYPE_PHOTO, NULL);
8277
8278 return db->db.photodb;
8279 }
8280
8281 G_GNUC_INTERNAL
db_get_device(Itdb_DB * db)8282 Itdb_Device *db_get_device(Itdb_DB *db)
8283 {
8284 g_return_val_if_fail (db, NULL);
8285
8286 switch (db->db_type) {
8287 case DB_TYPE_ITUNES:
8288 g_return_val_if_fail (db_get_itunesdb(db), NULL);
8289 return db_get_itunesdb(db)->device;
8290 case DB_TYPE_PHOTO:
8291 g_return_val_if_fail (db_get_photodb(db), NULL);
8292 return db_get_photodb(db)->device;
8293 }
8294 g_return_val_if_reached (NULL);
8295 }
8296
8297 G_GNUC_INTERNAL
db_get_mountpoint(Itdb_DB * db)8298 gchar *db_get_mountpoint(Itdb_DB *db)
8299 {
8300 Itdb_Device *device;
8301 g_return_val_if_fail (db, NULL);
8302
8303 device = db_get_device (db);
8304 g_return_val_if_fail (device, NULL);
8305
8306 return device->mountpoint;
8307 }
8308
itdb_device_get_control_dir(const Itdb_Device * device)8309 static gchar *itdb_device_get_control_dir (const Itdb_Device *device)
8310 {
8311 Itdb_IpodInfo const *info = NULL;
8312 char *podpath;
8313
8314 podpath = itdb_get_control_dir (device->mountpoint);
8315 if (podpath != NULL) {
8316 return podpath;
8317 }
8318
8319 /* The device doesn't already have an iPod_Control directory, let's try
8320 * to get which one is appropriate to use
8321 */
8322 info = itdb_device_get_ipod_info (device);
8323
8324 if (itdb_device_is_shuffle (device)) {
8325 podpath = g_build_filename (device->mountpoint, "iPod_Control", NULL);
8326 } else if (itdb_device_is_iphone_family (device)) {
8327 podpath = g_build_filename (device->mountpoint,"iTunes_Control", NULL);
8328 } else if (info->ipod_model == ITDB_IPOD_MODEL_MOBILE_1) {
8329 podpath = g_build_filename (device->mountpoint,
8330 "iTunes", "iTunes_Control",
8331 NULL);
8332 } else {
8333 podpath = g_build_filename (device->mountpoint, "iPod_Control", NULL);
8334 }
8335
8336 return podpath;
8337 }
8338
8339
8340 /*------------------------------------------------------------------*\
8341 * *
8342 * Create iPod directory hierarchy *
8343 * *
8344 \*------------------------------------------------------------------*/
itdb_create_directories(Itdb_Device * device,GError ** error)8345 static gboolean itdb_create_directories (Itdb_Device *device, GError **error)
8346 {
8347 const gchar *mp;
8348 gboolean result;
8349 gchar *pbuf;
8350 gint i, dirnum;
8351 gchar *podpath = NULL;
8352 gchar *model_number;
8353 Itdb_IpodInfo const *info = NULL;
8354
8355 g_return_val_if_fail (device, FALSE);
8356
8357 mp = device->mountpoint;
8358 info = itdb_device_get_ipod_info (device);
8359
8360 g_return_val_if_fail (mp, FALSE);
8361
8362 /* Construct the Control directory */
8363 pbuf = itdb_device_get_control_dir (device);
8364 if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8365 {
8366 if (g_mkdir_with_parents(pbuf, 0777) != 0)
8367 {
8368 goto error_dir;
8369 }
8370 }
8371 g_free (pbuf);
8372 podpath = itdb_get_control_dir (mp);
8373 if (!podpath) {
8374 goto error_dir;
8375 }
8376
8377 /* Construct the Music directory inside the Control directory */
8378 pbuf = g_build_filename (podpath, "Music", NULL);
8379 if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8380 {
8381 if((g_mkdir(pbuf, 0777) != 0))
8382 {
8383 goto error_dir;
8384 }
8385 }
8386 g_free (pbuf);
8387
8388 /* Construct the iTunes directory inside the Control directory */
8389 pbuf = g_build_filename (podpath, "iTunes", NULL);
8390 if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8391 {
8392 if((g_mkdir(pbuf, 0777) != 0))
8393 {
8394 goto error_dir;
8395 }
8396 }
8397 g_free (pbuf);
8398
8399 /* Build Artwork directory only for devices requiring artwork
8400 * (assume that 'unknown models' are new and will support
8401 * artwork) */
8402 if (itdb_device_supports_artwork(device) ||
8403 (info->ipod_model == ITDB_IPOD_MODEL_UNKNOWN))
8404 {
8405 pbuf = g_build_filename (podpath, "Artwork", NULL);
8406 if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8407 {
8408 if((g_mkdir(pbuf, 0777) != 0)) {
8409 goto error_dir;
8410 }
8411 }
8412 g_free (pbuf);
8413 }
8414
8415 /* Build Photo directory only for devices requiring it */
8416 if (itdb_device_supports_photo(device) ||
8417 (info->ipod_model == ITDB_IPOD_MODEL_UNKNOWN))
8418 {
8419 pbuf = g_build_filename (mp, "Photos", "Thumbs", NULL);
8420 if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8421 {
8422 if (g_mkdir_with_parents(pbuf, 0777) != 0)
8423 {
8424 goto error_dir;
8425 }
8426 }
8427 g_free (pbuf);
8428 }
8429
8430 /* Build the directories that hold the music files */
8431 dirnum = info->musicdirs;
8432 if (dirnum == 0)
8433 {
8434 guint64 capacity, free_space;
8435 if (itdb_device_get_storage_info(device, &capacity, &free_space)) {
8436 gdouble size = ((gdouble)capacity) / 1073741824;
8437 if (size < 20) dirnum = 20;
8438 else dirnum = 50;
8439 } else {
8440 dirnum = 20;
8441 }
8442 }
8443
8444 for(i = 0; i < dirnum; i++)
8445 {
8446 gchar *num = g_strdup_printf ("F%02d", i);
8447 pbuf = g_build_filename (podpath, "Music", num, NULL);
8448 g_free (num);
8449 if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8450 {
8451 if((g_mkdir(pbuf, 0777) != 0))
8452 {
8453 goto error_dir;
8454 }
8455 }
8456 g_free (pbuf);
8457 }
8458
8459 /* Build Calendar directory for models requiring it */
8460 if (!itdb_device_is_iphone_family (device)
8461 && !itdb_device_is_shuffle (device))
8462 {
8463 pbuf = g_build_filename (mp, "Calendars", NULL);
8464 if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8465 {
8466 if((g_mkdir(pbuf, 0777) != 0))
8467 {
8468 goto error_dir;
8469 }
8470 }
8471 g_free (pbuf);
8472
8473 /* Build Contacts directory for models requiring it */
8474 pbuf = g_build_filename (mp, "Contacts", NULL);
8475 if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8476 {
8477 if((g_mkdir(pbuf, 0777) != 0))
8478 {
8479 goto error_dir;
8480 }
8481 }
8482 g_free (pbuf);
8483
8484 pbuf = g_build_filename (mp, "Notes", NULL);
8485 if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8486 {
8487 if((g_mkdir(pbuf, 0777) != 0))
8488 {
8489 goto error_dir;
8490 }
8491 }
8492 g_free (pbuf);
8493 }
8494
8495 /* Construct a Device directory file for special models */
8496 pbuf = g_build_filename (podpath, "Device", NULL);
8497 if (!g_file_test (pbuf, G_FILE_TEST_EXISTS))
8498 {
8499 if((g_mkdir(pbuf, 0777) != 0))
8500 {
8501 goto error_dir;
8502 }
8503 }
8504 g_free (pbuf);
8505
8506 model_number = itdb_device_get_sysinfo (device, "ModelNumStr");
8507 /* Construct a SysInfo file */
8508 if (model_number && (strlen (model_number) != 0))
8509 {
8510 pbuf = NULL;
8511 if (!itdb_device_write_sysinfo (device, error))
8512 {
8513 g_free (model_number);
8514 goto error_dir;
8515 }
8516 }
8517 g_free (model_number);
8518 pbuf = NULL;
8519
8520 error_dir:
8521 if (pbuf)
8522 {
8523 g_set_error (error, 0, -1,
8524 _("Problem creating iPod directory or file: '%s'."),
8525 pbuf);
8526 result = FALSE;
8527 } else
8528 {
8529 result = TRUE;
8530 }
8531 g_free (pbuf);
8532 g_free (podpath);
8533 return result;
8534 }
8535