diff options
Diffstat (limited to 'apps/metadata/vorbis.c')
-rw-r--r-- | apps/metadata/vorbis.c | 381 |
1 files changed, 0 insertions, 381 deletions
diff --git a/apps/metadata/vorbis.c b/apps/metadata/vorbis.c deleted file mode 100644 index 58bd781873..0000000000 --- a/apps/metadata/vorbis.c +++ /dev/null | |||
@@ -1,381 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * | ||
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. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <inttypes.h> | ||
26 | |||
27 | #include "system.h" | ||
28 | #include "metadata.h" | ||
29 | #include "metadata_common.h" | ||
30 | #include "metadata_parsers.h" | ||
31 | #include "structec.h" | ||
32 | |||
33 | /* Define LOGF_ENABLE to enable logf output in this file */ | ||
34 | /*#define LOGF_ENABLE*/ | ||
35 | #include "logf.h" | ||
36 | |||
37 | struct file | ||
38 | { | ||
39 | int fd; | ||
40 | bool packet_ended; | ||
41 | long packet_remaining; | ||
42 | }; | ||
43 | |||
44 | |||
45 | /* Read an Ogg page header. file->packet_remaining is set to the size of the | ||
46 | * first packet on the page; file->packet_ended is set to true if the packet | ||
47 | * ended on the current page. Returns true if the page header was | ||
48 | * successfully read. | ||
49 | */ | ||
50 | static bool file_read_page_header(struct file* file) | ||
51 | { | ||
52 | unsigned char buffer[64]; | ||
53 | ssize_t table_left; | ||
54 | |||
55 | /* Size of page header without segment table */ | ||
56 | if (read(file->fd, buffer, 27) != 27) | ||
57 | { | ||
58 | return false; | ||
59 | } | ||
60 | |||
61 | if (memcmp("OggS", buffer, 4)) | ||
62 | { | ||
63 | return false; | ||
64 | } | ||
65 | |||
66 | /* Skip pattern (4), version (1), flags (1), granule position (8), | ||
67 | * serial (4), pageno (4), checksum (4) | ||
68 | */ | ||
69 | table_left = buffer[26]; | ||
70 | file->packet_remaining = 0; | ||
71 | |||
72 | /* Read segment table for the first packet */ | ||
73 | do | ||
74 | { | ||
75 | ssize_t count = MIN(sizeof(buffer), (size_t) table_left); | ||
76 | int i; | ||
77 | |||
78 | if (read(file->fd, buffer, count) < count) | ||
79 | { | ||
80 | return false; | ||
81 | } | ||
82 | |||
83 | table_left -= count; | ||
84 | |||
85 | for (i = 0; i < count; i++) | ||
86 | { | ||
87 | file->packet_remaining += buffer[i]; | ||
88 | |||
89 | if (buffer[i] < 255) | ||
90 | { | ||
91 | file->packet_ended = true; | ||
92 | |||
93 | /* Skip remainder of the table */ | ||
94 | if (lseek(file->fd, table_left, SEEK_CUR) < 0) | ||
95 | { | ||
96 | return false; | ||
97 | } | ||
98 | |||
99 | table_left = 0; | ||
100 | break; | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | while (table_left > 0); | ||
105 | |||
106 | return true; | ||
107 | } | ||
108 | |||
109 | |||
110 | /* Read (up to) buffer_size of data from the file. If buffer is NULL, just | ||
111 | * skip ahead buffer_size bytes (like lseek). Returns number of bytes read, | ||
112 | * 0 if there is no more data to read (in the packet or the file), < 0 if a | ||
113 | * read error occurred. | ||
114 | */ | ||
115 | static ssize_t file_read(struct file* file, void* buffer, size_t buffer_size) | ||
116 | { | ||
117 | ssize_t done = 0; | ||
118 | ssize_t count = -1; | ||
119 | |||
120 | do | ||
121 | { | ||
122 | if (file->packet_remaining <= 0) | ||
123 | { | ||
124 | if (file->packet_ended) | ||
125 | { | ||
126 | break; | ||
127 | } | ||
128 | |||
129 | if (!file_read_page_header(file)) | ||
130 | { | ||
131 | count = -1; | ||
132 | break; | ||
133 | } | ||
134 | } | ||
135 | |||
136 | count = MIN(buffer_size, (size_t) file->packet_remaining); | ||
137 | |||
138 | if (buffer) | ||
139 | { | ||
140 | count = read(file->fd, buffer, count); | ||
141 | } | ||
142 | else | ||
143 | { | ||
144 | if (lseek(file->fd, count, SEEK_CUR) < 0) | ||
145 | { | ||
146 | count = -1; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | if (count <= 0) | ||
151 | { | ||
152 | break; | ||
153 | } | ||
154 | |||
155 | if (buffer) | ||
156 | { | ||
157 | buffer += count; | ||
158 | } | ||
159 | |||
160 | buffer_size -= count; | ||
161 | done += count; | ||
162 | file->packet_remaining -= count; | ||
163 | } | ||
164 | while (buffer_size > 0); | ||
165 | |||
166 | return (count < 0 ? count : done); | ||
167 | } | ||
168 | |||
169 | |||
170 | /* Read an int32 from file. Returns false if a read error occurred. | ||
171 | */ | ||
172 | static bool file_read_int32(struct file* file, int32_t* value) | ||
173 | { | ||
174 | char buf[sizeof(int32_t)]; | ||
175 | |||
176 | if (file_read(file, buf, sizeof(buf)) < (ssize_t) sizeof(buf)) | ||
177 | { | ||
178 | return false; | ||
179 | } | ||
180 | |||
181 | *value = get_long_le(buf); | ||
182 | return true; | ||
183 | } | ||
184 | |||
185 | |||
186 | /* Read a string from the file. Read up to buffer_size bytes, or, if eos | ||
187 | * != -1, until the eos character is found (eos is not stored in buf, | ||
188 | * unless it is nil). Writes up to buffer_size chars to buf, always | ||
189 | * terminating with a nil. Returns number of chars read or < 0 if a read | ||
190 | * error occurred. | ||
191 | * | ||
192 | * Unfortunately this is a slightly modified copy of read_string() in | ||
193 | * metadata_common.c... | ||
194 | */ | ||
195 | static long file_read_string(struct file* file, char* buffer, | ||
196 | long buffer_size, int eos, long size) | ||
197 | { | ||
198 | long read_bytes = 0; | ||
199 | |||
200 | while (size > 0) | ||
201 | { | ||
202 | char c; | ||
203 | |||
204 | if (file_read(file, &c, 1) != 1) | ||
205 | { | ||
206 | read_bytes = -1; | ||
207 | break; | ||
208 | } | ||
209 | |||
210 | read_bytes++; | ||
211 | size--; | ||
212 | |||
213 | if ((eos != -1) && (eos == (unsigned char) c)) | ||
214 | { | ||
215 | break; | ||
216 | } | ||
217 | |||
218 | if (buffer_size > 1) | ||
219 | { | ||
220 | *buffer++ = c; | ||
221 | buffer_size--; | ||
222 | } | ||
223 | else if (eos == -1) | ||
224 | { | ||
225 | /* No point in reading any more, skip remaining data */ | ||
226 | if (file_read(file, NULL, size) < 0) | ||
227 | { | ||
228 | read_bytes = -1; | ||
229 | } | ||
230 | else | ||
231 | { | ||
232 | read_bytes += size; | ||
233 | } | ||
234 | |||
235 | break; | ||
236 | } | ||
237 | } | ||
238 | |||
239 | *buffer = 0; | ||
240 | return read_bytes; | ||
241 | } | ||
242 | |||
243 | |||
244 | /* Init struct file for reading from fd. type is the AFMT_* codec type of | ||
245 | * the file, and determines if Ogg pages are to be read. remaining is the | ||
246 | * max amount to read if codec type is FLAC; it is ignored otherwise. | ||
247 | * Returns true if the file was successfully initialized. | ||
248 | */ | ||
249 | static bool file_init(struct file* file, int fd, int type, int remaining) | ||
250 | { | ||
251 | memset(file, 0, sizeof(*file)); | ||
252 | file->fd = fd; | ||
253 | |||
254 | if (type == AFMT_OGG_VORBIS || type == AFMT_SPEEX) | ||
255 | { | ||
256 | if (!file_read_page_header(file)) | ||
257 | { | ||
258 | return false; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | if (type == AFMT_OGG_VORBIS) | ||
263 | { | ||
264 | char buffer[7]; | ||
265 | |||
266 | /* Read packet header (type and id string) */ | ||
267 | if (file_read(file, buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) | ||
268 | { | ||
269 | return false; | ||
270 | } | ||
271 | |||
272 | /* The first byte of a packet is the packet type; comment packets | ||
273 | * are type 3. | ||
274 | */ | ||
275 | if (buffer[0] != 3) | ||
276 | { | ||
277 | return false; | ||
278 | } | ||
279 | } | ||
280 | else if (type == AFMT_FLAC) | ||
281 | { | ||
282 | file->packet_remaining = remaining; | ||
283 | file->packet_ended = true; | ||
284 | } | ||
285 | |||
286 | return true; | ||
287 | } | ||
288 | |||
289 | |||
290 | /* Read the items in a Vorbis comment packet. For Ogg files, the file must | ||
291 | * be located on a page start, for other files, the beginning of the comment | ||
292 | * data (i.e., the vendor string length). Returns total size of the | ||
293 | * comments, or 0 if there was a read error. | ||
294 | */ | ||
295 | long read_vorbis_tags(int fd, struct mp3entry *id3, | ||
296 | long tag_remaining) | ||
297 | { | ||
298 | struct file file; | ||
299 | char *buf = id3->id3v2buf; | ||
300 | int32_t comment_count; | ||
301 | int32_t len; | ||
302 | long comment_size = 0; | ||
303 | int buf_remaining = sizeof(id3->id3v2buf) + sizeof(id3->id3v1buf); | ||
304 | int i; | ||
305 | |||
306 | if (!file_init(&file, fd, id3->codectype, tag_remaining)) | ||
307 | { | ||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | /* Skip vendor string */ | ||
312 | |||
313 | if (!file_read_int32(&file, &len) || (file_read(&file, NULL, len) < 0)) | ||
314 | { | ||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | if (!file_read_int32(&file, &comment_count)) | ||
319 | { | ||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | comment_size += 4 + len + 4; | ||
324 | |||
325 | for (i = 0; i < comment_count && file.packet_remaining > 0; i++) | ||
326 | { | ||
327 | char name[TAG_NAME_LENGTH]; | ||
328 | int32_t read_len; | ||
329 | |||
330 | if (!file_read_int32(&file, &len)) | ||
331 | { | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | comment_size += 4 + len; | ||
336 | read_len = file_read_string(&file, name, sizeof(name), '=', len); | ||
337 | |||
338 | if (read_len < 0) | ||
339 | { | ||
340 | return 0; | ||
341 | } | ||
342 | |||
343 | len -= read_len; | ||
344 | read_len = file_read_string(&file, id3->path, sizeof(id3->path), -1, len); | ||
345 | |||
346 | if (read_len < 0) | ||
347 | { | ||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | logf("Vorbis comment %d: %s=%s", i, name, id3->path); | ||
352 | |||
353 | /* Is it an embedded cuesheet? */ | ||
354 | if (!strcasecmp(name, "CUESHEET")) | ||
355 | { | ||
356 | id3->has_embedded_cuesheet = true; | ||
357 | id3->embedded_cuesheet.pos = lseek(file.fd, 0, SEEK_CUR) - read_len; | ||
358 | id3->embedded_cuesheet.size = len; | ||
359 | id3->embedded_cuesheet.encoding = CHAR_ENC_UTF_8; | ||
360 | } | ||
361 | else | ||
362 | { | ||
363 | len = parse_tag(name, id3->path, id3, buf, buf_remaining, | ||
364 | TAGTYPE_VORBIS); | ||
365 | } | ||
366 | |||
367 | buf += len; | ||
368 | buf_remaining -= len; | ||
369 | } | ||
370 | |||
371 | /* Skip to the end of the block (needed by FLAC) */ | ||
372 | if (file.packet_remaining) | ||
373 | { | ||
374 | if (file_read(&file, NULL, file.packet_remaining) < 0) | ||
375 | { | ||
376 | return 0; | ||
377 | } | ||
378 | } | ||
379 | |||
380 | return comment_size; | ||
381 | } | ||