1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2005 Dave Chapman
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
29 #include "metadata_common.h"
30 #include "metadata_parsers.h"
33 #define APETAG_HEADER_LENGTH 32
34 #define APETAG_HEADER_FORMAT "8llll8"
35 #define APETAG_ITEM_HEADER_FORMAT "ll"
36 #define APETAG_ITEM_TYPE_MASK 3
39 /* The AA header consists of the pseudo filename "Album Cover (Front).ext"
40 * whereas ".ext" is the file extension. For now ".jpg" and ".png" are
41 * supported by this APE metadata parser. Therefore the length is 22. */
42 #define APETAG_AA_HEADER_LENGTH 22
55 struct apetag_item_header
61 /* Read the items in an APEV2 tag. Only looks for a tag at the end of a
62 * file. Returns true if a tag was found and fully read, false otherwise.
64 bool read_ape_tags(int fd
, struct mp3entry
* id3
)
66 struct apetag_header header
;
68 if ((lseek(fd
, -APETAG_HEADER_LENGTH
, SEEK_END
) < 0)
69 || (ecread(fd
, &header
, 1, APETAG_HEADER_FORMAT
, IS_BIG_ENDIAN
)
70 != APETAG_HEADER_LENGTH
)
71 || (memcmp(header
.id
, "APETAGEX", sizeof(header
.id
))))
76 if ((header
.version
== 2000) && (header
.item_count
> 0)
77 && (header
.length
> APETAG_HEADER_LENGTH
))
79 char *buf
= id3
->id3v2buf
;
80 unsigned int buf_remaining
= sizeof(id3
->id3v2buf
)
81 + sizeof(id3
->id3v1buf
);
82 unsigned int tag_remaining
= header
.length
- APETAG_HEADER_LENGTH
;
85 if (lseek(fd
, -header
.length
, SEEK_END
) < 0)
90 for (i
= 0; i
< header
.item_count
; i
++)
92 struct apetag_item_header item
;
93 char name
[TAG_NAME_LENGTH
];
94 char value
[TAG_VALUE_LENGTH
];
97 if (tag_remaining
< sizeof(item
))
102 if (ecread(fd
, &item
, 1, APETAG_ITEM_HEADER_FORMAT
, IS_BIG_ENDIAN
)
103 < (long) sizeof(item
))
108 tag_remaining
-= sizeof(item
);
109 r
= read_string(fd
, name
, sizeof(name
), 0, tag_remaining
);
116 tag_remaining
-= r
+ item
.length
;
118 if ((item
.flags
& APETAG_ITEM_TYPE_MASK
) == 0)
122 if (read_string(fd
, value
, sizeof(value
), -1, item
.length
)
128 len
= parse_tag(name
, value
, id3
, buf
, buf_remaining
,
131 buf_remaining
-= len
;
136 if (strcasecmp(name
, "cover art (front)") == 0)
138 /* Allow to read at least APETAG_AA_HEADER_LENGTH bytes. */
139 r
= read_string(fd
, name
, sizeof(name
), 0, APETAG_AA_HEADER_LENGTH
);
145 /* Gather the album art format from the pseudo file name. */
146 id3
->albumart
.type
= AA_TYPE_UNKNOWN
;
147 if (strcasecmp(name
, "cover art (front).jpg") == 0)
149 id3
->albumart
.type
= AA_TYPE_JPG
;
151 else if (strcasecmp(name
, "cover art (front).png") == 0)
153 id3
->albumart
.type
= AA_TYPE_PNG
;
156 /* Set the album art size and position. */
157 if (id3
->albumart
.type
!= AA_TYPE_UNKNOWN
)
159 id3
->albumart
.pos
= lseek(fd
, 0, SEEK_CUR
);
160 id3
->albumart
.size
= item
.length
- r
;
161 id3
->embed_albumart
= true;
164 /* Seek back to this APE items begin. */
165 if (lseek(fd
, -r
, SEEK_CUR
) < 0)
171 /* Seek to the next APE item. */
172 if (lseek(fd
, item
.length
, SEEK_CUR
) < 0)