Mercurial > audlegacy
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/mpg123/id3.c Fri Oct 28 22:58:11 2005 -0700 @@ -0,0 +1,637 @@ +/********************************************************************* + * + * Copyright (C) 1999, 2001, Espen Skoglund + * Department of Computer Science, University of Tromsų + * + * Filename: id3.c + * Description: Code for accessing ID3 tags. + * Author: Espen Skoglund <espensk@stud.cs.uit.no> + * Created at: Fri Feb 5 23:55:13 1999 + * + * $Id: id3.c,v 1.6 2004/07/20 21:47:22 descender Exp $ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + ********************************************************************/ +#include "config.h" + +#include <sys/types.h> +#include <sys/uio.h> +#include <glib.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> + +#include "xmms-id3.h" +#include "id3_header.h" + + +/* +** +** Functions for accessing the ID3 tag using a memory pointer. +** +*/ + +/* + * Function id3_seek_mem (id3, offset) + * + * Seek `offset' bytes forward in the indicated ID3-tag. Return 0 + * upon success, or -1 if an error occured. + * + */ +static int +id3_seek_mem(struct id3_tag *id3, int offset) +{ + if (id3->id3_pos + offset > id3->id3_tagsize || id3->id3_pos + offset < 0) { + id3_error(id3, "seeking beyond tag boundary"); + return -1; + } + id3->s.me.id3_ptr = (char *) id3->s.me.id3_ptr + offset; + id3->id3_pos += offset; + + return 0; +} + + +/* + * Function id3_read_mem (id3, buf, size) + * + * Read `size' bytes from indicated ID3-tag. If `buf' is non-NULL, + * read into that buffer. Return a pointer to the data which was + * read, or NULL upon error. + * + */ +static void * +id3_read_mem(struct id3_tag *id3, void *buf, int size) +{ + void *ret = id3->s.me.id3_ptr; + + /* + * Check boundary. + */ + if (id3->id3_pos + size > id3->id3_tagsize) { + return NULL; + } + + /* + * If buffer is non-NULL, we have to copy the data. + */ + if (buf != NULL) { + if (size > ID3_FD_BUFSIZE) + return NULL; + memcpy(buf, id3->s.me.id3_ptr, size); + } + + /* + * Update memory pointer. + */ + id3->s.me.id3_ptr = (char *) id3->s.me.id3_ptr + size; + id3->id3_pos += size; + + return ret; +} + + +/* +** +** Functions for accessing the ID3 tag using a file descriptor. +** +*/ + +/* + * Function id3_seek_fd (id3, offset) + * + * Seek `offset' bytes forward in the indicated ID3-tag. Return 0 + * upon success, or -1 if an error occured. + * + */ +static int +id3_seek_fd(struct id3_tag *id3, int offset) +{ + /* + * Check boundary. + */ + if (id3->id3_pos + offset > id3->id3_tagsize || id3->id3_pos + offset < 0) + return -1; + + if (lseek(id3->s.fd.id3_fd, offset, SEEK_CUR) == -1) { + id3_error(id3, "seeking beyond tag boundary"); + return -1; + } + id3->id3_pos += offset; + + return 0; +} + + +/* + * Function id3_read_fd (id3, buf, size) + * + * Read `size' bytes from indicated ID3-tag. If `buf' is non-NULL, + * read into that buffer. Return a pointer to the data which was + * read, or NULL upon error. + * + */ +static void * +id3_read_fd(struct id3_tag *id3, void *buf, int size) +{ + int done = 0; + + /* + * Check boundary. + */ + if (id3->id3_pos + size > id3->id3_tagsize) { + return NULL; + } + + /* + * If buffer is NULL, we use the default buffer. + */ + if (buf == NULL) { + if (size > ID3_FD_BUFSIZE) + return NULL; + buf = id3->s.fd.id3_buf; + } + + /* + * Read until we have slurped as much data as we wanted. + */ + while (done < size) { + char *buffer = (char *) buf + done; + int ret; + + /* + * Try reading from file. + */ + ret = read(id3->s.fd.id3_fd, buffer, size); + if (ret <= 0) { + id3_error(id3, "read(2) failed"); + return NULL; + } + + id3->id3_pos += ret; + done += ret; + } + + return buf; +} + + +/* +** +** Functions for accessing the ID3 tag using a file pointer. +** +*/ + +/* + * Function id3_seek_fp (id3, offset) + * + * Seek `offset' bytes forward in the indicated ID3-tag. Return 0 + * upon success, or -1 if an error occured. + * + */ +static int +id3_seek_fp(struct id3_tag *id3, int offset) +{ + /* + * Check boundary. + */ + if (id3->id3_pos + offset > id3->id3_tagsize || id3->id3_pos + offset < 0) + return -1; + + if (offset > 0) { + /* + * If offset is positive, we use vfs_fread() instead of vfs_fseek(). This + * is more robust with respect to streams. + */ + char buf[64]; + int r, remain = offset; + + while (remain > 0) { + int size = MIN(64, remain); + r = vfs_fread(buf, 1, size, id3->s.fp.id3_fp); + if (r == 0) { + id3_error(id3, "vfs_fread() failed"); + return -1; + } + remain -= r; + } + } + else { + /* + * If offset is negative, we ahve to use vfs_fseek(). Let us hope + * that it works. + */ + if (vfs_fseek(id3->s.fp.id3_fp, offset, SEEK_CUR) == -1) { + id3_error(id3, "seeking beyond tag boundary"); + return -1; + } + } + id3->id3_pos += offset; + + return 0; +} + + +/* + * Function id3_read_fp (id3, buf, size) + * + * Read `size' bytes from indicated ID3-tag. If `buf' is non-NULL, + * read into that buffer. Return a pointer to the data which was + * read, or NULL upon error. + * + */ +static void * +id3_read_fp(struct id3_tag *id3, void *buf, int size) +{ + int ret; + + /* + * Check boundary. + */ + if (id3->id3_pos + size > id3->id3_tagsize) { + size = id3->id3_tagsize - id3->id3_pos; + } + + /* + * If buffer is NULL, we use the default buffer. + */ + if (buf == NULL) { + if (size > ID3_FD_BUFSIZE) + return NULL; + buf = id3->s.fd.id3_buf; + } + + /* + * Try reading from file. + */ + ret = vfs_fread(buf, 1, size, id3->s.fp.id3_fp); + if (ret != size) { + id3_error(id3, "vfs_fread() failed"); + return NULL; + } + + id3->id3_pos += ret; + + return buf; +} + + + + +/* + * Function id3_open_mem (ptr, flags) + * + * Open an ID3 tag using a memory pointer. Return a pointer to a + * structure describing the ID3 tag, or NULL if an error occured. + * + */ +struct id3_tag * +id3_open_mem(void *ptr, int flags) +{ + struct id3_tag *id3; + + /* + * Allocate ID3 structure. + */ + id3 = g_malloc0(sizeof(struct id3_tag)); + + /* + * Initialize access pointers. + */ + id3->id3_seek = id3_seek_mem; + id3->id3_read = id3_read_mem; + + id3->id3_oflags = flags; + id3->id3_type = ID3_TYPE_MEM; + id3->id3_pos = 0; + id3->s.me.id3_ptr = ptr; + + /* + * Try reading ID3 tag. + */ + if (id3_read_tag(id3) == -1) { + if (~flags & ID3_OPENF_CREATE) + goto Return_NULL; + id3_init_tag(id3); + } + + return id3; + + Return_NULL: + g_free(id3); + return NULL; +} + + +/* + * Function id3_open_fd (fd, flags) + * + * Open an ID3 tag using a file descriptor. Return a pointer to a + * structure describing the ID3 tag, or NULL if an error occured. + * + */ +struct id3_tag * +id3_open_fd(int fd, int flags) +{ + struct id3_tag *id3; + + /* + * Allocate ID3 structure. + */ + id3 = g_malloc0(sizeof(struct id3_tag)); + + /* + * Initialize access pointers. + */ + id3->id3_seek = id3_seek_fd; + id3->id3_read = id3_read_fd; + + id3->id3_oflags = flags; + id3->id3_type = ID3_TYPE_FD; + id3->id3_pos = 0; + id3->s.fd.id3_fd = fd; + + /* + * Allocate buffer to hold read data. + */ + id3->s.fd.id3_buf = g_malloc(ID3_FD_BUFSIZE); + + /* + * Try reading ID3 tag. + */ + if (id3_read_tag(id3) == -1) { + if (~flags & ID3_OPENF_CREATE) + goto Return_NULL; + id3_init_tag(id3); + } + + return id3; + + /* + * Cleanup code. + */ + Return_NULL: + g_free(id3->s.fd.id3_buf); + g_free(id3); + return NULL; +} + + +/* + * Function id3_open_fp (fp, flags) + * + * Open an ID3 tag using a file pointer. Return a pointer to a + * structure describing the ID3 tag, or NULL if an error occured. + * + */ +struct id3_tag * +id3_open_fp(VFSFile * fp, int flags) +{ + struct id3_tag *id3; + + /* + * Allocate ID3 structure. + */ + id3 = g_malloc0(sizeof(struct id3_tag)); + + /* + * Initialize access pointers. + */ + id3->id3_seek = id3_seek_fp; + id3->id3_read = id3_read_fp; + + id3->id3_oflags = flags; + id3->id3_type = ID3_TYPE_FP; + id3->id3_pos = 0; + id3->s.fp.id3_fp = fp; + + /* + * Allocate buffer to hold read data. + */ + id3->s.fp.id3_buf = g_malloc(ID3_FD_BUFSIZE); + + /* + * Try reading ID3 tag. + */ + if (id3_read_tag(id3) == -1) { + if (~flags & ID3_OPENF_CREATE) + goto Return_NULL; + id3_init_tag(id3); + } + + + return id3; + + /* + * Cleanup code. + */ + Return_NULL: + g_free(id3->s.fp.id3_buf); + g_free(id3); + return NULL; +} + + +/* + * Function id3_close (id3) + * + * Free all resources assoicated with the ID3 tag. + * + */ +int +id3_close(struct id3_tag *id3) +{ + int ret = 0; + + switch (id3->id3_type) { + case ID3_TYPE_MEM: + break; + case ID3_TYPE_FD: + g_free(id3->s.fd.id3_buf); + break; + case ID3_TYPE_FP: + g_free(id3->s.fp.id3_buf); + break; + case ID3_TYPE_NONE: + id3_error(id3, "unknown ID3 type"); + ret = -1; + } + + id3_destroy_frames(id3); + + g_free(id3); + + return ret; +} + + +/* + * Function id3_tell (id3) + * + * Return the current position in ID3 tag. This will always be + * directly after the tag. + * + */ +#if 0 +int +id3_tell(struct id3_tag *id3) +{ + if (id3->id3_newtag) { + return 0; + } + else { + return id3->id3_tagsize + 3 + sizeof(id3_taghdr_t); + } +} +#endif + + +/* + * Function id3_alter_file (id3) + * + * When altering a file, some ID3 tags should be discarded. As the ID3 + * library has no means of knowing when a file has been altered + * outside of the library, this function must be called manually + * whenever the file is altered. + * + */ +int +id3_alter_file(struct id3_tag *id3) +{ + /* + * List of frame classes that should be discarded whenever the + * file is altered. + */ + static guint32 discard_list[] = { + ID3_ETCO, ID3_EQUA, ID3_MLLT, ID3_POSS, ID3_SYLT, + ID3_SYTC, ID3_RVAD, ID3_TENC, ID3_TLEN, ID3_TSIZ, + 0 + }; + struct id3_frame *fr; + guint32 id, i = 0; + + /* + * Go through list of frame types that should be discarded. + */ + while ((id = discard_list[i++]) != 0) { + /* + * Discard all frames of that type. + */ + while ((fr = id3_get_frame(id3, id, 1))) { + id3_delete_frame(fr); + } + } + + return 0; +} + + +/* + * Function safe_write (fd, buf, size) + * + * Like write(2), except that the whole buffer will be written. + * + */ +static int +safe_write(int fd, void *buf, int size) +{ + int remaining = size; + char *ptr = buf; + int r; + + while (remaining > 0) { + if ((r = write(fd, ptr, remaining)) == -1) + return -1; + remaining -= r; + ptr += r; + } + + return 0; +} + + +/* + * Function id3_write_tag (id3, fd) + * + * Wrtite the ID3 tag to the indicated file descriptor. Return 0 + * upon success, or -1 if an error occured. + * + */ +int +id3_write_tag(struct id3_tag *id3, int fd) +{ + struct id3_frame *fr; + GList *node; + int size = 0; + char buf[ID3_TAGHDR_SIZE]; + + /* + * Calculate size of ID3 tag. + */ + for (node = id3->id3_frame; node != NULL; node = node->next) { + fr = node->data; + size += fr->fr_size + ID3_FRAMEHDR_SIZE; + } + + /* + * Write tag header. + */ + buf[0] = id3->id3_version; + buf[1] = id3->id3_revision; + buf[2] = id3->id3_flags; + ID3_SET_SIZE28(size, buf[3], buf[4], buf[5], buf[6]); + + if (safe_write(fd, "ID3", 3) == -1) + return -1; + if (safe_write(fd, buf, ID3_TAGHDR_SIZE) == -1) + return -1; + + /* + * TODO: Write extended header. + */ +#if 0 + if (id3->id3_flags & ID3_THFLAG_EXT) { + id3_exthdr_t exthdr; + } +#endif + + for (node = id3->id3_frame; node != NULL; node = node->next) { + char fhdr[ID3_FRAMEHDR_SIZE]; + + fr = node->data; + + /* + * TODO: Support compressed headers, encoded + * headers, and grouping info. + */ + /* fhdr.fh_id = fr->fr_desc ? g_htonl(fr->fr_desc->fd_id) : 0; */ + fhdr[3] = (fr->fr_size >> 24) & 0xff; + fhdr[4] = (fr->fr_size >> 16) & 0xff; + fhdr[5] = (fr->fr_size >> 8) & 0xff; + fhdr[6] = fr->fr_size & 0xff; + fhdr[7] = (fr->fr_flags >> 8) & 0xff; + fhdr[8] = fr->fr_flags & 0xff; + + if (safe_write(fd, fhdr, sizeof(fhdr)) == -1) + return -1; + + if (safe_write(fd, fr->fr_data, fr->fr_size) == -1) + return -1; + } + return 0; +}