Mercurial > audlegacy
comparison Plugins/Input/mpg123/id3.c @ 61:fa848bd484d8 trunk
[svn] Move plugins to Plugins/
author | nenolod |
---|---|
date | Fri, 28 Oct 2005 22:58:11 -0700 |
parents | |
children | 539a0fa7f030 |
comparison
equal
deleted
inserted
replaced
60:1771f253e1b2 | 61:fa848bd484d8 |
---|---|
1 /********************************************************************* | |
2 * | |
3 * Copyright (C) 1999, 2001, Espen Skoglund | |
4 * Department of Computer Science, University of Tromsų | |
5 * | |
6 * Filename: id3.c | |
7 * Description: Code for accessing ID3 tags. | |
8 * Author: Espen Skoglund <espensk@stud.cs.uit.no> | |
9 * Created at: Fri Feb 5 23:55:13 1999 | |
10 * | |
11 * $Id: id3.c,v 1.6 2004/07/20 21:47:22 descender Exp $ | |
12 * | |
13 * This program is free software; you can redistribute it and/or | |
14 * modify it under the terms of the GNU General Public License | |
15 * as published by the Free Software Foundation; either version 2 | |
16 * of the License, or (at your option) any later version. | |
17 * | |
18 * This program is distributed in the hope that it will be useful, | |
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 * GNU General Public License for more details. | |
22 * | |
23 * You should have received a copy of the GNU General Public License | |
24 * along with this program; if not, write to the Free Software | |
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
26 * | |
27 ********************************************************************/ | |
28 #include "config.h" | |
29 | |
30 #include <sys/types.h> | |
31 #include <sys/uio.h> | |
32 #include <glib.h> | |
33 #include <fcntl.h> | |
34 #include <unistd.h> | |
35 #include <string.h> | |
36 #include <stdlib.h> | |
37 #include <stdarg.h> | |
38 | |
39 #include "xmms-id3.h" | |
40 #include "id3_header.h" | |
41 | |
42 | |
43 /* | |
44 ** | |
45 ** Functions for accessing the ID3 tag using a memory pointer. | |
46 ** | |
47 */ | |
48 | |
49 /* | |
50 * Function id3_seek_mem (id3, offset) | |
51 * | |
52 * Seek `offset' bytes forward in the indicated ID3-tag. Return 0 | |
53 * upon success, or -1 if an error occured. | |
54 * | |
55 */ | |
56 static int | |
57 id3_seek_mem(struct id3_tag *id3, int offset) | |
58 { | |
59 if (id3->id3_pos + offset > id3->id3_tagsize || id3->id3_pos + offset < 0) { | |
60 id3_error(id3, "seeking beyond tag boundary"); | |
61 return -1; | |
62 } | |
63 id3->s.me.id3_ptr = (char *) id3->s.me.id3_ptr + offset; | |
64 id3->id3_pos += offset; | |
65 | |
66 return 0; | |
67 } | |
68 | |
69 | |
70 /* | |
71 * Function id3_read_mem (id3, buf, size) | |
72 * | |
73 * Read `size' bytes from indicated ID3-tag. If `buf' is non-NULL, | |
74 * read into that buffer. Return a pointer to the data which was | |
75 * read, or NULL upon error. | |
76 * | |
77 */ | |
78 static void * | |
79 id3_read_mem(struct id3_tag *id3, void *buf, int size) | |
80 { | |
81 void *ret = id3->s.me.id3_ptr; | |
82 | |
83 /* | |
84 * Check boundary. | |
85 */ | |
86 if (id3->id3_pos + size > id3->id3_tagsize) { | |
87 return NULL; | |
88 } | |
89 | |
90 /* | |
91 * If buffer is non-NULL, we have to copy the data. | |
92 */ | |
93 if (buf != NULL) { | |
94 if (size > ID3_FD_BUFSIZE) | |
95 return NULL; | |
96 memcpy(buf, id3->s.me.id3_ptr, size); | |
97 } | |
98 | |
99 /* | |
100 * Update memory pointer. | |
101 */ | |
102 id3->s.me.id3_ptr = (char *) id3->s.me.id3_ptr + size; | |
103 id3->id3_pos += size; | |
104 | |
105 return ret; | |
106 } | |
107 | |
108 | |
109 /* | |
110 ** | |
111 ** Functions for accessing the ID3 tag using a file descriptor. | |
112 ** | |
113 */ | |
114 | |
115 /* | |
116 * Function id3_seek_fd (id3, offset) | |
117 * | |
118 * Seek `offset' bytes forward in the indicated ID3-tag. Return 0 | |
119 * upon success, or -1 if an error occured. | |
120 * | |
121 */ | |
122 static int | |
123 id3_seek_fd(struct id3_tag *id3, int offset) | |
124 { | |
125 /* | |
126 * Check boundary. | |
127 */ | |
128 if (id3->id3_pos + offset > id3->id3_tagsize || id3->id3_pos + offset < 0) | |
129 return -1; | |
130 | |
131 if (lseek(id3->s.fd.id3_fd, offset, SEEK_CUR) == -1) { | |
132 id3_error(id3, "seeking beyond tag boundary"); | |
133 return -1; | |
134 } | |
135 id3->id3_pos += offset; | |
136 | |
137 return 0; | |
138 } | |
139 | |
140 | |
141 /* | |
142 * Function id3_read_fd (id3, buf, size) | |
143 * | |
144 * Read `size' bytes from indicated ID3-tag. If `buf' is non-NULL, | |
145 * read into that buffer. Return a pointer to the data which was | |
146 * read, or NULL upon error. | |
147 * | |
148 */ | |
149 static void * | |
150 id3_read_fd(struct id3_tag *id3, void *buf, int size) | |
151 { | |
152 int done = 0; | |
153 | |
154 /* | |
155 * Check boundary. | |
156 */ | |
157 if (id3->id3_pos + size > id3->id3_tagsize) { | |
158 return NULL; | |
159 } | |
160 | |
161 /* | |
162 * If buffer is NULL, we use the default buffer. | |
163 */ | |
164 if (buf == NULL) { | |
165 if (size > ID3_FD_BUFSIZE) | |
166 return NULL; | |
167 buf = id3->s.fd.id3_buf; | |
168 } | |
169 | |
170 /* | |
171 * Read until we have slurped as much data as we wanted. | |
172 */ | |
173 while (done < size) { | |
174 char *buffer = (char *) buf + done; | |
175 int ret; | |
176 | |
177 /* | |
178 * Try reading from file. | |
179 */ | |
180 ret = read(id3->s.fd.id3_fd, buffer, size); | |
181 if (ret <= 0) { | |
182 id3_error(id3, "read(2) failed"); | |
183 return NULL; | |
184 } | |
185 | |
186 id3->id3_pos += ret; | |
187 done += ret; | |
188 } | |
189 | |
190 return buf; | |
191 } | |
192 | |
193 | |
194 /* | |
195 ** | |
196 ** Functions for accessing the ID3 tag using a file pointer. | |
197 ** | |
198 */ | |
199 | |
200 /* | |
201 * Function id3_seek_fp (id3, offset) | |
202 * | |
203 * Seek `offset' bytes forward in the indicated ID3-tag. Return 0 | |
204 * upon success, or -1 if an error occured. | |
205 * | |
206 */ | |
207 static int | |
208 id3_seek_fp(struct id3_tag *id3, int offset) | |
209 { | |
210 /* | |
211 * Check boundary. | |
212 */ | |
213 if (id3->id3_pos + offset > id3->id3_tagsize || id3->id3_pos + offset < 0) | |
214 return -1; | |
215 | |
216 if (offset > 0) { | |
217 /* | |
218 * If offset is positive, we use vfs_fread() instead of vfs_fseek(). This | |
219 * is more robust with respect to streams. | |
220 */ | |
221 char buf[64]; | |
222 int r, remain = offset; | |
223 | |
224 while (remain > 0) { | |
225 int size = MIN(64, remain); | |
226 r = vfs_fread(buf, 1, size, id3->s.fp.id3_fp); | |
227 if (r == 0) { | |
228 id3_error(id3, "vfs_fread() failed"); | |
229 return -1; | |
230 } | |
231 remain -= r; | |
232 } | |
233 } | |
234 else { | |
235 /* | |
236 * If offset is negative, we ahve to use vfs_fseek(). Let us hope | |
237 * that it works. | |
238 */ | |
239 if (vfs_fseek(id3->s.fp.id3_fp, offset, SEEK_CUR) == -1) { | |
240 id3_error(id3, "seeking beyond tag boundary"); | |
241 return -1; | |
242 } | |
243 } | |
244 id3->id3_pos += offset; | |
245 | |
246 return 0; | |
247 } | |
248 | |
249 | |
250 /* | |
251 * Function id3_read_fp (id3, buf, size) | |
252 * | |
253 * Read `size' bytes from indicated ID3-tag. If `buf' is non-NULL, | |
254 * read into that buffer. Return a pointer to the data which was | |
255 * read, or NULL upon error. | |
256 * | |
257 */ | |
258 static void * | |
259 id3_read_fp(struct id3_tag *id3, void *buf, int size) | |
260 { | |
261 int ret; | |
262 | |
263 /* | |
264 * Check boundary. | |
265 */ | |
266 if (id3->id3_pos + size > id3->id3_tagsize) { | |
267 size = id3->id3_tagsize - id3->id3_pos; | |
268 } | |
269 | |
270 /* | |
271 * If buffer is NULL, we use the default buffer. | |
272 */ | |
273 if (buf == NULL) { | |
274 if (size > ID3_FD_BUFSIZE) | |
275 return NULL; | |
276 buf = id3->s.fd.id3_buf; | |
277 } | |
278 | |
279 /* | |
280 * Try reading from file. | |
281 */ | |
282 ret = vfs_fread(buf, 1, size, id3->s.fp.id3_fp); | |
283 if (ret != size) { | |
284 id3_error(id3, "vfs_fread() failed"); | |
285 return NULL; | |
286 } | |
287 | |
288 id3->id3_pos += ret; | |
289 | |
290 return buf; | |
291 } | |
292 | |
293 | |
294 | |
295 | |
296 /* | |
297 * Function id3_open_mem (ptr, flags) | |
298 * | |
299 * Open an ID3 tag using a memory pointer. Return a pointer to a | |
300 * structure describing the ID3 tag, or NULL if an error occured. | |
301 * | |
302 */ | |
303 struct id3_tag * | |
304 id3_open_mem(void *ptr, int flags) | |
305 { | |
306 struct id3_tag *id3; | |
307 | |
308 /* | |
309 * Allocate ID3 structure. | |
310 */ | |
311 id3 = g_malloc0(sizeof(struct id3_tag)); | |
312 | |
313 /* | |
314 * Initialize access pointers. | |
315 */ | |
316 id3->id3_seek = id3_seek_mem; | |
317 id3->id3_read = id3_read_mem; | |
318 | |
319 id3->id3_oflags = flags; | |
320 id3->id3_type = ID3_TYPE_MEM; | |
321 id3->id3_pos = 0; | |
322 id3->s.me.id3_ptr = ptr; | |
323 | |
324 /* | |
325 * Try reading ID3 tag. | |
326 */ | |
327 if (id3_read_tag(id3) == -1) { | |
328 if (~flags & ID3_OPENF_CREATE) | |
329 goto Return_NULL; | |
330 id3_init_tag(id3); | |
331 } | |
332 | |
333 return id3; | |
334 | |
335 Return_NULL: | |
336 g_free(id3); | |
337 return NULL; | |
338 } | |
339 | |
340 | |
341 /* | |
342 * Function id3_open_fd (fd, flags) | |
343 * | |
344 * Open an ID3 tag using a file descriptor. Return a pointer to a | |
345 * structure describing the ID3 tag, or NULL if an error occured. | |
346 * | |
347 */ | |
348 struct id3_tag * | |
349 id3_open_fd(int fd, int flags) | |
350 { | |
351 struct id3_tag *id3; | |
352 | |
353 /* | |
354 * Allocate ID3 structure. | |
355 */ | |
356 id3 = g_malloc0(sizeof(struct id3_tag)); | |
357 | |
358 /* | |
359 * Initialize access pointers. | |
360 */ | |
361 id3->id3_seek = id3_seek_fd; | |
362 id3->id3_read = id3_read_fd; | |
363 | |
364 id3->id3_oflags = flags; | |
365 id3->id3_type = ID3_TYPE_FD; | |
366 id3->id3_pos = 0; | |
367 id3->s.fd.id3_fd = fd; | |
368 | |
369 /* | |
370 * Allocate buffer to hold read data. | |
371 */ | |
372 id3->s.fd.id3_buf = g_malloc(ID3_FD_BUFSIZE); | |
373 | |
374 /* | |
375 * Try reading ID3 tag. | |
376 */ | |
377 if (id3_read_tag(id3) == -1) { | |
378 if (~flags & ID3_OPENF_CREATE) | |
379 goto Return_NULL; | |
380 id3_init_tag(id3); | |
381 } | |
382 | |
383 return id3; | |
384 | |
385 /* | |
386 * Cleanup code. | |
387 */ | |
388 Return_NULL: | |
389 g_free(id3->s.fd.id3_buf); | |
390 g_free(id3); | |
391 return NULL; | |
392 } | |
393 | |
394 | |
395 /* | |
396 * Function id3_open_fp (fp, flags) | |
397 * | |
398 * Open an ID3 tag using a file pointer. Return a pointer to a | |
399 * structure describing the ID3 tag, or NULL if an error occured. | |
400 * | |
401 */ | |
402 struct id3_tag * | |
403 id3_open_fp(VFSFile * fp, int flags) | |
404 { | |
405 struct id3_tag *id3; | |
406 | |
407 /* | |
408 * Allocate ID3 structure. | |
409 */ | |
410 id3 = g_malloc0(sizeof(struct id3_tag)); | |
411 | |
412 /* | |
413 * Initialize access pointers. | |
414 */ | |
415 id3->id3_seek = id3_seek_fp; | |
416 id3->id3_read = id3_read_fp; | |
417 | |
418 id3->id3_oflags = flags; | |
419 id3->id3_type = ID3_TYPE_FP; | |
420 id3->id3_pos = 0; | |
421 id3->s.fp.id3_fp = fp; | |
422 | |
423 /* | |
424 * Allocate buffer to hold read data. | |
425 */ | |
426 id3->s.fp.id3_buf = g_malloc(ID3_FD_BUFSIZE); | |
427 | |
428 /* | |
429 * Try reading ID3 tag. | |
430 */ | |
431 if (id3_read_tag(id3) == -1) { | |
432 if (~flags & ID3_OPENF_CREATE) | |
433 goto Return_NULL; | |
434 id3_init_tag(id3); | |
435 } | |
436 | |
437 | |
438 return id3; | |
439 | |
440 /* | |
441 * Cleanup code. | |
442 */ | |
443 Return_NULL: | |
444 g_free(id3->s.fp.id3_buf); | |
445 g_free(id3); | |
446 return NULL; | |
447 } | |
448 | |
449 | |
450 /* | |
451 * Function id3_close (id3) | |
452 * | |
453 * Free all resources assoicated with the ID3 tag. | |
454 * | |
455 */ | |
456 int | |
457 id3_close(struct id3_tag *id3) | |
458 { | |
459 int ret = 0; | |
460 | |
461 switch (id3->id3_type) { | |
462 case ID3_TYPE_MEM: | |
463 break; | |
464 case ID3_TYPE_FD: | |
465 g_free(id3->s.fd.id3_buf); | |
466 break; | |
467 case ID3_TYPE_FP: | |
468 g_free(id3->s.fp.id3_buf); | |
469 break; | |
470 case ID3_TYPE_NONE: | |
471 id3_error(id3, "unknown ID3 type"); | |
472 ret = -1; | |
473 } | |
474 | |
475 id3_destroy_frames(id3); | |
476 | |
477 g_free(id3); | |
478 | |
479 return ret; | |
480 } | |
481 | |
482 | |
483 /* | |
484 * Function id3_tell (id3) | |
485 * | |
486 * Return the current position in ID3 tag. This will always be | |
487 * directly after the tag. | |
488 * | |
489 */ | |
490 #if 0 | |
491 int | |
492 id3_tell(struct id3_tag *id3) | |
493 { | |
494 if (id3->id3_newtag) { | |
495 return 0; | |
496 } | |
497 else { | |
498 return id3->id3_tagsize + 3 + sizeof(id3_taghdr_t); | |
499 } | |
500 } | |
501 #endif | |
502 | |
503 | |
504 /* | |
505 * Function id3_alter_file (id3) | |
506 * | |
507 * When altering a file, some ID3 tags should be discarded. As the ID3 | |
508 * library has no means of knowing when a file has been altered | |
509 * outside of the library, this function must be called manually | |
510 * whenever the file is altered. | |
511 * | |
512 */ | |
513 int | |
514 id3_alter_file(struct id3_tag *id3) | |
515 { | |
516 /* | |
517 * List of frame classes that should be discarded whenever the | |
518 * file is altered. | |
519 */ | |
520 static guint32 discard_list[] = { | |
521 ID3_ETCO, ID3_EQUA, ID3_MLLT, ID3_POSS, ID3_SYLT, | |
522 ID3_SYTC, ID3_RVAD, ID3_TENC, ID3_TLEN, ID3_TSIZ, | |
523 0 | |
524 }; | |
525 struct id3_frame *fr; | |
526 guint32 id, i = 0; | |
527 | |
528 /* | |
529 * Go through list of frame types that should be discarded. | |
530 */ | |
531 while ((id = discard_list[i++]) != 0) { | |
532 /* | |
533 * Discard all frames of that type. | |
534 */ | |
535 while ((fr = id3_get_frame(id3, id, 1))) { | |
536 id3_delete_frame(fr); | |
537 } | |
538 } | |
539 | |
540 return 0; | |
541 } | |
542 | |
543 | |
544 /* | |
545 * Function safe_write (fd, buf, size) | |
546 * | |
547 * Like write(2), except that the whole buffer will be written. | |
548 * | |
549 */ | |
550 static int | |
551 safe_write(int fd, void *buf, int size) | |
552 { | |
553 int remaining = size; | |
554 char *ptr = buf; | |
555 int r; | |
556 | |
557 while (remaining > 0) { | |
558 if ((r = write(fd, ptr, remaining)) == -1) | |
559 return -1; | |
560 remaining -= r; | |
561 ptr += r; | |
562 } | |
563 | |
564 return 0; | |
565 } | |
566 | |
567 | |
568 /* | |
569 * Function id3_write_tag (id3, fd) | |
570 * | |
571 * Wrtite the ID3 tag to the indicated file descriptor. Return 0 | |
572 * upon success, or -1 if an error occured. | |
573 * | |
574 */ | |
575 int | |
576 id3_write_tag(struct id3_tag *id3, int fd) | |
577 { | |
578 struct id3_frame *fr; | |
579 GList *node; | |
580 int size = 0; | |
581 char buf[ID3_TAGHDR_SIZE]; | |
582 | |
583 /* | |
584 * Calculate size of ID3 tag. | |
585 */ | |
586 for (node = id3->id3_frame; node != NULL; node = node->next) { | |
587 fr = node->data; | |
588 size += fr->fr_size + ID3_FRAMEHDR_SIZE; | |
589 } | |
590 | |
591 /* | |
592 * Write tag header. | |
593 */ | |
594 buf[0] = id3->id3_version; | |
595 buf[1] = id3->id3_revision; | |
596 buf[2] = id3->id3_flags; | |
597 ID3_SET_SIZE28(size, buf[3], buf[4], buf[5], buf[6]); | |
598 | |
599 if (safe_write(fd, "ID3", 3) == -1) | |
600 return -1; | |
601 if (safe_write(fd, buf, ID3_TAGHDR_SIZE) == -1) | |
602 return -1; | |
603 | |
604 /* | |
605 * TODO: Write extended header. | |
606 */ | |
607 #if 0 | |
608 if (id3->id3_flags & ID3_THFLAG_EXT) { | |
609 id3_exthdr_t exthdr; | |
610 } | |
611 #endif | |
612 | |
613 for (node = id3->id3_frame; node != NULL; node = node->next) { | |
614 char fhdr[ID3_FRAMEHDR_SIZE]; | |
615 | |
616 fr = node->data; | |
617 | |
618 /* | |
619 * TODO: Support compressed headers, encoded | |
620 * headers, and grouping info. | |
621 */ | |
622 /* fhdr.fh_id = fr->fr_desc ? g_htonl(fr->fr_desc->fd_id) : 0; */ | |
623 fhdr[3] = (fr->fr_size >> 24) & 0xff; | |
624 fhdr[4] = (fr->fr_size >> 16) & 0xff; | |
625 fhdr[5] = (fr->fr_size >> 8) & 0xff; | |
626 fhdr[6] = fr->fr_size & 0xff; | |
627 fhdr[7] = (fr->fr_flags >> 8) & 0xff; | |
628 fhdr[8] = fr->fr_flags & 0xff; | |
629 | |
630 if (safe_write(fd, fhdr, sizeof(fhdr)) == -1) | |
631 return -1; | |
632 | |
633 if (safe_write(fd, fr->fr_data, fr->fr_size) == -1) | |
634 return -1; | |
635 } | |
636 return 0; | |
637 } |