📄 tag.c
字号:
/* * libid3tag - ID3 tag manipulation library * Copyright (C) 2000-2003 Underbit Technologies, Inc. * * 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 * * $Id: tag.c,v 1.18 2003/04/19 00:14:33 rob Exp $ */# ifdef HAVE_CONFIG_H# include "config.h"# endif# include "global.h"# include <string.h># include <stdlib.h># ifdef HAVE_ASSERT_H# include <assert.h># endif# include "id3tag.h"# include "tag.h"# include "frame.h"# include "compat.h"# include "parse.h"# include "render.h"# include "latin1.h"# include "ucs4.h"# include "genre.h"# include "crc.h"# include "field.h"# include "util.h"/* * NAME: tag->new() * DESCRIPTION: allocate and return a new, empty tag */struct id3_tag *id3_tag_new(void){ struct id3_tag *tag; tag = malloc(sizeof(*tag)); if (tag) { tag->refcount = 0; tag->version = ID3_TAG_VERSION; tag->flags = 0; tag->extendedflags = 0; tag->restrictions = 0; tag->options = /* ID3_TAG_OPTION_UNSYNCHRONISATION | */ ID3_TAG_OPTION_COMPRESSION | ID3_TAG_OPTION_CRC; tag->nframes = 0; tag->frames = 0; tag->paddedsize = 0; } return tag;}/* * NAME: tag->delete() * DESCRIPTION: destroy a tag and deallocate all associated memory */void id3_tag_delete(struct id3_tag *tag){ assert(tag); if (tag->refcount == 0) { id3_tag_clearframes(tag); if (tag->frames) free(tag->frames); free(tag); }}/* * NAME: tag->addref() * DESCRIPTION: add an external reference to a tag */void id3_tag_addref(struct id3_tag *tag){ assert(tag); ++tag->refcount;}/* * NAME: tag->delref() * DESCRIPTION: remove an external reference to a tag */void id3_tag_delref(struct id3_tag *tag){ assert(tag && tag->refcount > 0); --tag->refcount;}/* * NAME: tag->version() * DESCRIPTION: return the tag's original ID3 version number */unsigned int id3_tag_version(struct id3_tag const *tag){ assert(tag); return tag->version;}/* * NAME: tag->options() * DESCRIPTION: get or set tag options */int id3_tag_options(struct id3_tag *tag, int mask, int values){ assert(tag); if (mask) tag->options = (tag->options & ~mask) | (values & mask); return tag->options;}/* * NAME: tag->setlength() * DESCRIPTION: set the minimum rendered tag size */void id3_tag_setlength(struct id3_tag *tag, id3_length_t length){ assert(tag); tag->paddedsize = length;}/* * NAME: tag->clearframes() * DESCRIPTION: detach and delete all frames associated with a tag */void id3_tag_clearframes(struct id3_tag *tag){ unsigned int i; assert(tag); for (i = 0; i < tag->nframes; ++i) { id3_frame_delref(tag->frames[i]); id3_frame_delete(tag->frames[i]); } tag->nframes = 0;}/* * NAME: tag->attachframe() * DESCRIPTION: attach a frame to a tag */int id3_tag_attachframe(struct id3_tag *tag, struct id3_frame *frame){ struct id3_frame **frames; assert(tag && frame); frames = realloc(tag->frames, (tag->nframes + 1) * sizeof(*frames)); if (frames == 0) return -1; tag->frames = frames; tag->frames[tag->nframes++] = frame; id3_frame_addref(frame); return 0;}/* * NAME: tag->detachframe() * DESCRIPTION: detach (but don't delete) a frame from a tag */int id3_tag_detachframe(struct id3_tag *tag, struct id3_frame *frame){ unsigned int i; assert(tag && frame); for (i = 0; i < tag->nframes; ++i) { if (tag->frames[i] == frame) break; } if (i == tag->nframes) return -1; --tag->nframes; while (i++ < tag->nframes) tag->frames[i - 1] = tag->frames[i]; id3_frame_delref(frame); return 0;}/* * NAME: tag->findframe() * DESCRIPTION: find in a tag the nth (0-based) frame with the given frame ID */struct id3_frame *id3_tag_findframe(struct id3_tag const *tag, char const *id, unsigned int index){ unsigned int len, i; assert(tag); if (id == 0 || *id == 0) return (index < tag->nframes) ? tag->frames[index] : 0; len = strlen(id); if (len == 4) { struct id3_compat const *compat; compat = id3_compat_lookup(id, len); if (compat && compat->equiv && !compat->translate) { id = compat->equiv; len = strlen(id); } } for (i = 0; i < tag->nframes; ++i) { if (strncmp(tag->frames[i]->id, id, len) == 0 && index-- == 0) return tag->frames[i]; } return 0;}enum tagtype { TAGTYPE_NONE = 0, TAGTYPE_ID3V1, TAGTYPE_ID3V2, TAGTYPE_ID3V2_FOOTER};staticenum tagtype tagtype(id3_byte_t const *data, id3_length_t length){ if (length >= 3 && data[0] == 'T' && data[1] == 'A' && data[2] == 'G') return TAGTYPE_ID3V1; if (length >= 10 && ((data[0] == 'I' && data[1] == 'D' && data[2] == '3') || (data[0] == '3' && data[1] == 'D' && data[2] == 'I')) && data[3] < 0xff && data[4] < 0xff && data[6] < 0x80 && data[7] < 0x80 && data[8] < 0x80 && data[9] < 0x80) return data[0] == 'I' ? TAGTYPE_ID3V2 : TAGTYPE_ID3V2_FOOTER; return TAGTYPE_NONE;}staticvoid parse_header(id3_byte_t const **ptr, unsigned int *version, int *flags, id3_length_t *size){ *ptr += 3; *version = id3_parse_uint(ptr, 2); *flags = id3_parse_uint(ptr, 1); *size = id3_parse_syncsafe(ptr, 4);}/* * NAME: tag->query() * DESCRIPTION: if a tag begins at the given location, return its size */signed long id3_tag_query(id3_byte_t const *data, id3_length_t length){ unsigned int version; int flags; id3_length_t size; assert(data); switch (tagtype(data, length)) { case TAGTYPE_ID3V1: return 128; case TAGTYPE_ID3V2: parse_header(&data, &version, &flags, &size); if (flags & ID3_TAG_FLAG_FOOTERPRESENT) size += 10; return 10 + size; case TAGTYPE_ID3V2_FOOTER: parse_header(&data, &version, &flags, &size); return -size - 10; case TAGTYPE_NONE: break; } return 0;}staticvoid trim(char *str){ char *ptr; ptr = str + strlen(str); while (ptr > str && ptr[-1] == ' ') --ptr; *ptr = 0;}staticint v1_attachstr(struct id3_tag *tag, char const *id, char *text, unsigned long number){ struct id3_frame *frame; id3_ucs4_t ucs4[31]; if (text) { trim(text); if (*text == 0) return 0; } frame = id3_frame_new(id); if (frame == 0) return -1; if (id3_field_settextencoding(&frame->fields[0], ID3_FIELD_TEXTENCODING_ISO_8859_1) == -1) goto fail; if (text) id3_latin1_decode(text, ucs4); else id3_ucs4_putnumber(ucs4, number); if (strcmp(id, ID3_FRAME_COMMENT) == 0) { if (id3_field_setlanguage(&frame->fields[1], "XXX") == -1 || id3_field_setstring(&frame->fields[2], id3_ucs4_empty) == -1 || id3_field_setfullstring(&frame->fields[3], ucs4) == -1) goto fail; } else { id3_ucs4_t *ptr = ucs4; if (id3_field_setstrings(&frame->fields[1], 1, &ptr) == -1) goto fail; } if (id3_tag_attachframe(tag, frame) == -1) goto fail; return 0; fail: id3_frame_delete(frame); return -1;}staticstruct id3_tag *v1_parse(id3_byte_t const *data){ struct id3_tag *tag; tag = id3_tag_new(); if (tag) { char title[31], artist[31], album[31], year[5], comment[31]; unsigned int genre, track; tag->version = 0x0100; tag->options |= ID3_TAG_OPTION_ID3V1; tag->options &= ~ID3_TAG_OPTION_COMPRESSION; tag->restrictions = ID3_TAG_RESTRICTION_TEXTENCODING_LATIN1_UTF8 | ID3_TAG_RESTRICTION_TEXTSIZE_30_CHARS; title[30] = artist[30] = album[30] = year[4] = comment[30] = 0; memcpy(title, &data[3], 30); memcpy(artist, &data[33], 30); memcpy(album, &data[63], 30); memcpy(year, &data[93], 4); memcpy(comment, &data[97], 30); genre = data[127]; track = 0; if (comment[28] == 0 && comment[29] != 0) { track = comment[29]; tag->version = 0x0101; } /* populate tag frames */ if (v1_attachstr(tag, ID3_FRAME_TITLE, title, 0) == -1 || v1_attachstr(tag, ID3_FRAME_ARTIST, artist, 0) == -1 || v1_attachstr(tag, ID3_FRAME_ALBUM, album, 0) == -1 || v1_attachstr(tag, ID3_FRAME_YEAR, year, 0) == -1 || (track && v1_attachstr(tag, ID3_FRAME_TRACK, 0, track) == -1) || (genre < 0xff && v1_attachstr(tag, ID3_FRAME_GENRE, 0, genre) == -1) || v1_attachstr(tag, ID3_FRAME_COMMENT, comment, 0) == -1) { id3_tag_delete(tag); tag = 0; } } return tag;}staticstruct id3_tag *v2_parse(id3_byte_t const *ptr){ struct id3_tag *tag; id3_byte_t *mem = 0; tag = id3_tag_new(); if (tag) { id3_byte_t const *end; id3_length_t size; parse_header(&ptr, &tag->version, &tag->flags, &size); tag->paddedsize = 10 + size; if ((tag->flags & ID3_TAG_FLAG_UNSYNCHRONISATION) && ID3_TAG_VERSION_MAJOR(tag->version) < 4) { mem = malloc(size); if (mem == 0) goto fail; memcpy(mem, ptr, size);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -