Mercurial > audlegacy
view Plugins/Input/mpg123/id3.c @ 145:8794a4781a15 trunk
[svn] looping stuff
author | nenolod |
---|---|
date | Thu, 10 Nov 2005 17:51:04 -0800 |
parents | fa848bd484d8 |
children | 539a0fa7f030 |
line wrap: on
line source
/********************************************************************* * * 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; }