📄 tag.c
字号:
/*
* libid3tag - ID3 tag manipulation library
* Copyright (C) 2000-2004 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.1 2004/05/17 19:25:38 lcanis 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
};
static
enum 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;
}
static
void 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;
}
static
void trim(char *str)
{
char *ptr;
ptr = str + strlen(str);
while (ptr > str && ptr[-1] == ' ')
--ptr;
*ptr = 0;
}
static
int 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;
}
static
struct 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;
}
static
struct 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 + -