From 4412ca091e6379def8bc836163c6b580df76619c Mon Sep 17 00:00:00 2001 From: crunchy Date: Wed, 23 Apr 2003 08:34:01 +0000 Subject: start restructuring --- libmpio/mplib/mplib.c | 1011 +++++++++++++++++++++++++++++++++++++ libmpio/mplib/mplib.h | 438 +++++++++++++++++ libmpio/mplib/mplib_paas.c | 256 ++++++++++ libmpio/mplib/mplib_s.c | 1177 ++++++++++++++++++++++++++++++++++++++++++++ libmpio/mplib/mplib_s.h | 153 ++++++ libmpio/mplib/xmalloc.c | 167 +++++++ libmpio/mplib/xmalloc.h | 58 +++ 7 files changed, 3260 insertions(+) create mode 100644 libmpio/mplib/mplib.c create mode 100644 libmpio/mplib/mplib.h create mode 100644 libmpio/mplib/mplib_paas.c create mode 100644 libmpio/mplib/mplib_s.c create mode 100644 libmpio/mplib/mplib_s.h create mode 100644 libmpio/mplib/xmalloc.c create mode 100644 libmpio/mplib/xmalloc.h (limited to 'libmpio/mplib') diff --git a/libmpio/mplib/mplib.c b/libmpio/mplib/mplib.c new file mode 100644 index 0000000..afa7066 --- /dev/null +++ b/libmpio/mplib/mplib.c @@ -0,0 +1,1011 @@ +/* + * mplib - a library that enables you to edit ID3 tags + * Copyright (C) 2001,2002 Stefan Podkowinski + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if STDC_HEADERS +# include +# include +#elif HAVE_STRINGS_H +# include +#endif /*STDC_HEADERS*/ + +#if HAVE_UNISTD_H +# include +# include +#endif + +#include +#include +#include + +#include "xmalloc.h" +#include "mplib.h" +#include "mplib_s.h" +#include "mplib_s.c" + + + +/******************************************************************************************* + * Extern functions + *******************************************************************************************/ + + + +/******************************************************************************************* + * Get + *******************************************************************************************/ + + mpeg_header* + mp_get_mpeg_header_from_file(const char* filename) + { + mpeg_header *ret; + int fd; + + if(!filename) return NULL; + + fd = open(filename, O_RDONLY); + if(fd == -1) return NULL; + + ret = mp_get_mpeg_header_from_fd(fd); + close(fd); + return ret; + } + + +mpeg_header* +mp_get_mpeg_header_from_fd(int fd) +{ + mpeg_header *h; + unsigned char c[5]; + + h = XMALLOCD(mpeg_header, "mp_get_mpeg_header_from_fd:h"); + + if(id3_lseek_syncword(fd)) goto exit_on_error; + + if(read(fd, c, 4) < 4) goto exit_on_error; + + memset(h, 0, sizeof(h)); + h->syncword = (c[1] & 240); + h->syncword <<= 8; + h->syncword |= c[0]; + h->version = (c[1] & 8) >> 3; + h->layer = (c[1] & 6) >> 1; + h->protbit = (c[1] & 1); + h->bitrate = (c[2] & 240) >> 4; + h->samplingfreq = (c[2] & 12) >> 2; + h->padbit = (c[2] & 2) >> 1; + h->privbit = (c[2] & 1); + h->mode = (c[3] & 192) >> 6; + h->mode_ext = (c[3] & 48) >> 4; + h->copyright = (c[3] & 8) >> 3; + h->originalhome = (c[3] & 4) >> 2; + h->emphasis = (c[3] & 3); + + return h; + + exit_on_error: + xfree(h); + return NULL; +} + +char* +mp_get_str_version(const mpeg_header *h) +{ + return h->version == 0 ? "MPEG 2" : "MPEG 1"; +} + +char* +mp_get_str_layer(const mpeg_header *h) +{ + switch(h->layer) + { + case 1: return "Layer III"; + case 2: return "Layer II"; + case 3: return "Layer I"; + default: return "undefined"; + } +} + +char* +mp_get_str_bitrate(const mpeg_header *h) +{ + char *buf = (char *)xmallocd0(11, "mp_get_str_bitrate:buf"); + + if(h->version == 1) /* MPEG 1 */ + { + switch(h->layer) + { + case 1: + snprintf(buf, sizeof buf, "%d kBit/s", br_1_3[h->bitrate]); + return buf; + case 2: + snprintf(buf, sizeof buf, "%d kBit/s", br_1_2[h->bitrate]); + return buf; + case 3: + snprintf(buf, sizeof buf, "%d kBit/s", br_1_1[h->bitrate]); + return buf; + default: + return "undefined"; + } + } + else /* MPEG 2 */ + { + switch(h->layer) + { + case 1: + snprintf(buf, sizeof buf, "%d kBit/s", br_2_3[h->bitrate]); + return buf; + case 2: + snprintf(buf, sizeof buf, "%d kBit/s", br_2_2[h->bitrate]); + return buf; + case 3: + snprintf(buf, sizeof buf, "%d kBit/s", br_2_1[h->bitrate]); + return buf; + default: + return "undefined"; + } + } +} + +char* +mp_get_str_samplingfreq(const mpeg_header *h) +{ + if(h->version == 1) + { + switch(h->samplingfreq) + { + case 0: return "44100 Hz"; + case 1: return "48000 Hz"; + case 2: return "32000 Hz"; + default: return "undefined"; + } + } + else + { + switch(h->samplingfreq) + { + case 0: return "22050 Hz"; + case 1: return "24000 Hz"; + case 2: return "16000 Hz"; + default: return "undefined"; + } + } +} + +char* +mp_get_str_mode(const mpeg_header *h) +{ + switch(h->mode) + { + case 0: return "Stereo"; + case 1: return "Joint-Stereo"; + case 2: return "Dual-Channel"; + case 3: return "Mono"; + default: return "undefined"; + } +} + +id3_tag_list* +mp_get_tag_list_from_file(const char* filename) +{ + id3_tag_list *ret; + int fd; + + if(!filename) return NULL; + + fd = open(filename, O_RDONLY); + if(fd == -1) return NULL; + + ret = mp_get_tag_list_from_fd(fd); + close(fd); + return ret; +} + +id3_tag_list* +mp_get_tag_list_from_fd(int fd) +{ + id3_tag_list *tag_list = NULL; + id3_tag_list *tag_list2 = NULL; + id3v2_tag *v2tag = NULL; + id3v1_tag *v1tag = NULL; + id3_tag *tag = NULL; + + v2tag = id3v2_get_tag(fd); + if(v2tag) + { + tag = XMALLOCD0(id3_tag, "mp_get_tag_list_from_fd:tag"); + if(v2tag->header->version_minor == 3 || v2tag->header->version_minor == 4) + tag->version = 2; + else + tag->version = -1; + tag->tag = v2tag; + + tag_list = XMALLOCD(id3_tag_list, "mp_get_tag_list_from_fd:tag_list"); + tag_list->tag = tag; + tag_list->next = NULL; + tag_list->first = tag_list; + } + + v1tag = id3v1_get_tag(fd); + if(v1tag) + { + tag = XMALLOCD(id3_tag, "mp_get_tag_list_from_fd:tag"); + tag->version = 1; + tag->tag = v1tag; + + if(tag_list) + { + tag_list2 = XMALLOCD(id3_tag_list, "mp_get_tag_list_from_fd:tag_list2"); + tag_list2->tag = tag; + tag_list2->next = NULL; + tag_list2->first = tag_list; + tag_list->next = tag_list2; + } + else + { + tag_list = XMALLOCD(id3_tag_list, "mp_get_tag_list_from_fd:tag_list"); + tag_list->tag = tag; + tag_list->next = NULL; + tag_list->first = tag_list; + } + } + + return tag_list; +} + +id3_content* +mp_get_content(const id3_tag *tag, int field) +{ + return mp_get_content_at_pos(tag, field, 0); +} + +id3_content* +mp_get_content_at_pos(const id3_tag *tag, int field, int pos) +{ + int i; + char *c; + id3_content *ret; + + if(!tag || !tag->tag) + { + errno = MP_EERROR; + return NULL; + } + + if(tag->version == 1) + { + if(pos != 0) + { + errno = MP_EERROR; + return NULL; + } + else return id3v1_get_content(tag->tag, field); + } + else if(tag->version == 2) + { + id3v2_tag *v2 = tag->tag; + char *val; + + switch(field) + { + case MP_ARTIST: + return mp_get_content_custom_at_pos(tag, "TPE1", pos); + case MP_TITLE: + return mp_get_content_custom_at_pos(tag, "TIT2", pos); + case MP_ALBUM: + return mp_get_content_custom_at_pos(tag, "TALB", pos); + case MP_GENRE: + return mp_get_content_custom_at_pos(tag, "TCON", pos); + case MP_COMMENT: + return mp_get_content_custom_at_pos(tag, "COMM", pos); + case MP_YEAR: + return mp_get_content_custom_at_pos(tag, "TYER", pos); + case MP_TRACK: + return mp_get_content_custom_at_pos(tag, "TRCK", pos); + } + errno = MP_EFNF; + return NULL; + } + else + { + errno = MP_EVERSION; + return NULL; + } +} + +id3_content* +mp_get_content_custom(const id3_tag* tag, const char*field) +{ + if(!tag) + { + errno = MP_EERROR; + return NULL; + } + else if(tag->version != 2) + { + errno = MP_EVERSION; + return NULL; + } + + return id3v2_get_content_at_pos(tag->tag, field, 0); + +} + +id3_content* +mp_get_content_custom_at_pos(const id3_tag* tag, const char*field, int pos) +{ + if(!tag) + { + errno = MP_EERROR; + return NULL; + } + else if(tag->version != 2) + { + errno = MP_EVERSION; + return NULL; + } + + + return id3v2_get_content_at_pos(tag->tag, field, pos); +} + + +/******************************************************************************************* + * Set + *******************************************************************************************/ + + int + mp_set_content(id3_tag* tag, const int field, id3_content* new_content) + { + id3v1_tag *v1; + id3v2_tag *v2; + + + if(!tag) return MP_EERROR; + + if(tag->version == 2) + { + return mp_set_content_at_pos(tag, field, new_content, 0); + } + else if(tag->version == 1) + { + unsigned char c; + char *my_val; + int len, j; + + v1 = tag->tag; + + switch(field) + { + +#define FLD(str1, str2, str3, str4) \ + case str1:\ + if(!new_content) v1->str2 = NULL;\ + else\ + {\ + id3_text_content *tc = str4(new_content);\ + if(strlen(tc->text) > str3 || tc->encoding != ISO_8859_1)\ + {\ + mp_convert_to_v2(tag);\ + mp_free_text_content(tc);\ + return mp_set_content(tag, field, new_content);\ + }\ + \ + v1->str2 = tc->text;\ + xfree(tc);\ + }\ + break; + + FLD(MP_ARTIST, artist, 30, mp_parse_artist); + FLD(MP_TITLE, title, 30, mp_parse_title); + FLD(MP_ALBUM, album, 30, mp_parse_album); + FLD(MP_YEAR, year, 4, mp_parse_year); + + case MP_COMMENT: + if(!new_content) v1->comment = NULL; + else + { + id3_comment_content *tc = mp_parse_comment(new_content); + if(strlen(tc->text) > 30 || tc->short_descr || tc->encoding != ISO_8859_1) + { + mp_convert_to_v2(tag); + mp_free_comment_content(tc); + return mp_set_content(tag, field, new_content); + } + v1->comment = xmallocd0(strlen(tc->text)+1, + "mp_set_content:v1->comment"); + memcpy(v1->comment, tc->text, strlen(tc->text)); + mp_free_comment_content(tc); + } + break; + + case MP_TRACK: + if(!new_content) v1->track = 0; + else + { + id3_text_content *tc = mp_parse_track(new_content); +#ifdef HAVE_STRTOL + errno = 0; + j = strtol(tc->text, (char **)NULL, 10); + if(errno != ERANGE) v1->track = j; + else return MP_EERROR; +#else + v1->track = atoi(tc->text); +#endif + mp_free_text_content(tc); + } + break; + + case MP_GENRE: + if(!new_content) v1->genre = 0xFF; + else + { + int b = 0, i; + id3_text_content *tc = mp_parse_genre(new_content); + /* i = strlen(tc->text); */ + for(c = 0; c < GLL; c++) { + if(!strcmp(genre_list[c], tc->text)) + { + v1->genre = c; + b = 1; + } + } + mp_free_text_content(tc); + if(!b) + { + mp_convert_to_v2(tag); + return mp_set_content(tag, field, new_content); + } + break; + } + } + } + else if(tag->version == -1) return MP_EVERSION; + else return MP_EFNF; + + return 0; + } + +int +mp_set_content_at_pos(id3_tag* tag, const int field, id3_content* new_content, int pos) +{ + char* c; + + if(!tag) return MP_EERROR; + if(field < MP_ARTIST || field > MP_TRACK) return MP_EFNF; + + if(tag->version == 1 && pos == 0) return mp_set_content(tag, field, new_content); + + switch(field) + { + case MP_ARTIST: c = "TPE1"; break; + case MP_TITLE: c = "TIT2"; break; + case MP_ALBUM: c = "TALB"; break; + case MP_TRACK: c = "TRCK"; break; + case MP_YEAR: c = "TYER"; break; + case MP_COMMENT: c = "COMM"; break; + case MP_GENRE: c = "TCON"; break; + } + return mp_set_custom_content_at_pos(tag, c, new_content, pos); +} + +int +mp_set_custom_content(id3_tag* tag, char* field, id3_content* new_content) +{ + return mp_set_custom_content_at_pos(tag, field, new_content, 0); +} + +int +mp_set_custom_content_at_pos(id3_tag* tag, char* field, id3_content* new_content, int pos) +{ + id3v2_tag *v2; + + if(!tag || !field || strlen(field) != 4) return MP_EERROR; + + if(tag->version == 1) + { + if(mp_convert_to_v2(tag)) + return MP_EERROR; + } + else if(tag->version == -1) return MP_EVERSION; + + v2 = (id3v2_tag*)tag->tag; + if(!v2->frame_list) + { + v2->frame_list = XMALLOCD0(id3v2_frame_list, + "mp_set_custom_content_at_pos:v2->frame_list"); + id3_add_frame(v2->frame_list, field, new_content->data, new_content->length); + } + else + { + id3v2_frame *frame; + + if((frame = id3_lookup_frame(v2->frame_list, field, pos))) + { + if(new_content) + { + long len, len_sync; + /* make sync safe */ + len = new_content->length; + len_sync = id3_sync(new_content->data, len); + + xfree(frame->data); + frame->data = xmallocd(new_content->length, + "mp_set_custom_content_at_pos:frame->data"); + memcpy(frame->data, new_content->data, new_content->length); + frame->status_flag = 0; + if(len != len_sync) frame->format_flag = 64; + else frame->format_flag = 0; + frame->data_size = len_sync; + } + else id3_remove_frame(v2->frame_list, frame); + } + else if(pos == 0) id3_add_frame(v2->frame_list, field, new_content->data, new_content->length); + else return MP_EFNF; + } + + return 0; +} + +/******************************************************************************************* + * Write & delete + *******************************************************************************************/ + int + mp_write_to_file(const id3_tag_list* tag_list, const char *filename) + { + int ret; + int fd; + + if(!filename) return MP_EERROR; + + fd = open(filename, O_RDWR); + if(fd == -1) return MP_EERROR; + + ret = mp_write_to_fd(tag_list, fd); + close(fd); + return ret; + } + + + int + mp_write_to_fd(const id3_tag_list* tag_list, const int fd) + { + id3_tag *tag; + id3v1_tag *v1; + id3v2_tag *v2; + id3_tag_list *mylist; + int ret = 0; + + if(!tag_list) { + ret |= id3v1_del_tag(fd); + ret |= id3v2_del_tag(fd, NULL); + return ret; + } + + while(tag_list) + { + tag = tag_list->tag; + if(!tag) + { + tag_list = tag_list->next; + continue; + } + + if(tag->version == 1) + { + id3v1_del_tag(fd); + ret |= id3v1_add_tag(fd, tag->tag); + } + else if(tag->version == 2) + { + int pad = 0; + id3v2_frame_list *frame_list; + id3v2_tag *old_v2; + id3v2_tag *v2 = tag->tag; + + /* calculate tag size */ + v2->header->total_tag_size = 10; + if(v2->header->has_footer) v2->header->total_tag_size += 10; + if(v2->header->has_extended_header) v2->header->total_tag_size += v2->header->extended_header->size; + frame_list = v2->frame_list; + while(frame_list) + { + v2->header->total_tag_size += frame_list->data->data_size + 10; + frame_list = frame_list->next; + } + + /* this is where padding handling takes place */ + /* we must get the old tag to see if padding can be used */ + old_v2 = id3v2_get_tag(fd); + if(old_v2) { + if(v2->header->total_tag_size > old_v2->header->total_tag_size) + { + /* padding not sufficent */ + ret |= id3v2_del_tag(fd, old_v2); + ret |= id3v2_add_tag(fd, v2, NULL); + } + else + { + ret |= id3v2_add_tag(fd, v2, old_v2); + } + id3v2_free_tag(old_v2); + } else { + ret |= id3v2_add_tag(fd, v2, NULL); + } + + } + else + { + ret |= MP_EVERSION; + } + + tag_list = tag_list->next; + } /* tag list */ + + return ret; + } + +int +mp_del_tags_from_file(const char* filename) +{ + int ret, fd; + + if(!filename) return 1; + + fd = open(filename, O_RDWR); + if(fd == -1) return 1; + + ret = mp_del_tags_from_fd(fd); + close(fd); + return ret; +} + +int +mp_del_tags_from_fd(const int fd) +{ + int ret = 0; + + ret |= id3v1_del_tag(fd); + ret |= id3v2_del_tag(fd, NULL); + + return ret; +} + +int +mp_del_tags_by_ver_from_file(const char* filename, const int version) +{ + int fd, ret; + + if(!filename) return 1; + + fd = open(filename, O_RDWR); + if(fd == -1) return 1; + + ret = mp_del_tags_by_ver_from_fd(fd, version); + close(fd); + return ret; +} + +int +mp_del_tags_by_ver_from_fd(const int fd, const int version) +{ + if(version == 1) return id3v1_del_tag(fd); + else if(version == 2) return id3v2_del_tag(fd, NULL); + else return MP_EVERSION; +} + + + + +/******************************************************************************************* + * Misc + *******************************************************************************************/ + + int + mp_convert_to_v2(id3_tag *tag) + { + id3v1_tag *v1; + id3_tag *tmp; + id3_content* content; + + if(tag->version == 2) return 0; + else if(tag->version == -1) return MP_EVERSION; + + tmp = mp_alloc_tag_with_version(2); + + v1 = (id3v1_tag*)tag->tag; + + content = mp_assemble_text_content(v1->artist, ISO_8859_1); + if(v1->artist) mp_set_content(tmp, MP_ARTIST, content); + + content = mp_assemble_text_content(v1->title, ISO_8859_1); + if(v1->title) mp_set_content(tmp, MP_TITLE, content); + + content = mp_assemble_text_content(v1->album, ISO_8859_1); + if(v1->album) mp_set_content(tmp, MP_ALBUM, content); + + content = mp_assemble_text_content(v1->year, ISO_8859_1); + if(v1->year) mp_set_content(tmp, MP_YEAR, content); + + content = mp_assemble_comment_content(v1->comment, NULL, ISO_8859_1, NULL); + if(v1->comment) mp_set_content(tmp, MP_COMMENT, content); + + if(v1->genre != 0xFF) + { + char *c = xmallocd(strlen(genre_list[v1->genre]) + 1, + "mp_convert_to_v2:c"); + strcpy(c, genre_list[v1->genre]); + content = mp_assemble_text_content(c, ISO_8859_1); + mp_set_content(tmp, MP_GENRE, content); + } + if(v1->track > 0) + { + char *trk = (char *)xmallocd(4, "mp_convert_to_v2:trk"); + snprintf(trk, 3, "%d", v1->track); + trk[3] = 0; + content = mp_assemble_text_content(trk, ISO_8859_1); + mp_set_content(tmp, MP_TRACK, content); + } + + tag->version = 2; + tag->tag = tmp->tag; + + id3v1_free_tag(v1); + xfree(tmp); + + return 0; + } + +int +mp_convert_to_v1(id3_tag *tag) +{ + id3v1_tag *v1; + id3_tag* tmp; + id3_content* content; + id3_text_content* tc; + id3_comment_content* cc; + char* c; + int j, k = 0; + + if(tag->version == 1) return 0; + else if(tag->version == -1) return MP_EVERSION; + + v1 = XMALLOCD0(id3v1_tag, "mp_convert_to_v1:v1"); + + content = mp_get_content(tag, MP_ARTIST); + tc = mp_parse_artist(content); + v1->artist = tc->text; + xfree(tc); + mp_free_content(content); + + content = mp_get_content(tag, MP_TITLE); + tc = mp_parse_title(content); + v1->title = tc->text; + xfree(tc); + mp_free_content(content); + + content = mp_get_content(tag, MP_ALBUM); + tc = mp_parse_album(content); + v1->album = tc->text; + xfree(tc); + mp_free_content(content); + + content = mp_get_content(tag, MP_YEAR); + tc = mp_parse_year(content); + v1->year = tc->text; + xfree(tc); + mp_free_content(content); + + content = mp_get_content(tag, MP_COMMENT); + cc = mp_parse_comment(content); + v1->comment = cc->text; + xfree(cc->language); + xfree(cc->short_descr); + xfree(cc); + mp_free_content(content); + + content = mp_get_content(tag, MP_TRACK); + tc = mp_parse_track(content); + c = tc->text; + if(c) + { +#ifdef HAVE_STRTOL + errno = 0; + j = strtol(c, (char **)NULL, 10); + if(errno != ERANGE) v1->track = j; + else v1->track = 0; +#else + v1->track = atoi(c); +#endif + } + else v1->track = 0; + xfree(c); + mp_free_text_content(tc); + mp_free_content(content); + + content = mp_get_content(tag, MP_GENRE); + tc = mp_parse_genre(content); + c = tc->text; + for(j = 0; c, j < GLL; j++) { + if(!strcmp(genre_list[j], c)) + { + v1->genre = j; + k = 1; + } + } + if(!c) v1->genre = 0xFF; + xfree(c); + mp_free_text_content(tc); + mp_free_content(content); + + id3v1_truncate_tag(v1); + + id3v2_free_tag(tag->tag); + + tag->version = 1; + tag->tag = v1; + + return 0; +} + +int +mp_is_valid_v1_value(int field, char *value) +{ + int len = 30; + int j; + + switch(field) { + case MP_YEAR: + len = 4; + break; + + case MP_TRACK: +#ifdef HAVE_STRTOL + errno = 0; + j = strtol(value, (char **)NULL, 10); + if(errno != ERANGE) return 1; + else return 0; +#else + return 1; /* poor fellow */ +#endif + + case MP_GENRE: + for(j = 0; j < GLL; j++) { + if(!strcmp(genre_list[j], value)) + { + return 1; + } + return 0; + } + } + + /* Check string length */ + if(strlen(value) > len) return 0; + else return 1; +} + + +void +mp_free_list(id3_tag_list *list) +{ + if(!list) return; + + /* free tag */ + if(list->tag) mp_free_tag(list->tag); + + /* free next element */ + if(list->next) mp_free_list(list->next); + + /* free this element */ + xfree(list); +} + +id3_tag* +mp_alloc_tag(void) +{ + /* The tags initialized version makes a different. Generally spoken, we + like to make id3v1 tags if possible and therefor set the version to 1 + here. This matters in mp_set_content(). */ + return mp_alloc_tag_with_version(1); +} + +id3_tag* +mp_alloc_tag_with_version(int v) +{ + id3_tag* ret; + + if(v != 1 && v != 2) return NULL; + + ret = XMALLOCD(id3_tag, "mp_alloc_tag_with_version:ret"); + ret->version = v; + if(v == 1) + { + ret->tag = XMALLOCD0(id3v1_tag, "mp_alloc_tag_with_version:ret->tag"); + ((id3v1_tag*)ret->tag)->genre = 0xFF; + } + else + { + id3v2_tag *v2; + /* XXX */ + ret->tag = XMALLOCD0(id3v2_tag, "mp_alloc_tag_with_version:ret->tag"); + v2 = (id3v2_tag*)ret->tag; + v2->header = XMALLOCD0(id3v2_header, "mp_alloc_tag_with_version:v2->header"); +//if ID3VERSION == "2.4" + v2->header->version_minor = 4; +//else + v2->header->version_minor = 3; +//endif + v2->header->version_revision = 0; + v2->header->unsyncronization = 1; + v2->header->has_extended_header = 0; + v2->header->is_experimental = 1; + v2->header->has_footer = 0; + v2->header->flags = 0; + v2->header->total_tag_size = 0; + v2->header->extended_header = NULL; + v2->frame_list = NULL; + } + return ret; +} + +void +mp_free_tag(id3_tag *tag) +{ + if(!tag) return; + + if(tag->version == 1) + { + id3v1_free_tag(tag->tag); + } + else if(tag->version == 2) + { + id3v2_free_tag(tag->tag); + } + xfree(tag); +} + +void +mp_free_content(id3_content *content) +{ + if(!content) return; + xfree(content->data); + xfree(content); +} + +void +mp_free_text_content(id3_text_content *content) +{ + if(!content) return; + xfree(content->text); + xfree(content); +} + +void +mp_free_comment_content(id3_comment_content *content) +{ + if(!content) return; + xfree(content->language); + xfree(content->short_descr); + xfree(content->text); + xfree(content); +} + + diff --git a/libmpio/mplib/mplib.h b/libmpio/mplib/mplib.h new file mode 100644 index 0000000..8b3fe64 --- /dev/null +++ b/libmpio/mplib/mplib.h @@ -0,0 +1,438 @@ +/* + * mplib - a library that enables you to edit ID3 tags + * Copyright (C) 2001,2002 Stefan Podkowinski + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __MPLIB_H +#define __MPLIB_H + + +/* __BEGIN_DECLS should be used at the beginning of your declarations, + so that C++ compilers don't mangle their names. Use __END_DECLS at + the end of C declarations. */ +#undef __BEGIN_DECLS +#undef __END_DECLS +#ifdef __cplusplus +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS } +#else +# define __BEGIN_DECLS /* empty */ +# define __END_DECLS /* empty */ +#endif + +/* __P is a macro used to wrap function prototypes, so that compilers + that don't understand ANSI C prototypes still work, and ANSI C + compilers can issue warnings about type mismatches. */ +#undef __P +#if defined (__STDC__) || defined (_AIX) \ + || (defined (__mips) && defined (_SYSTYPE_SVR4)) \ + || defined(WIN32) || defined(__cplusplus) +# define __P(protos) protos +#else +# define __P(protos) () +#endif + + +__BEGIN_DECLS + + + +/*************************************/ +/* Defines */ +/*************************************/ + +#define MP_ARTIST 1 +#define MP_TITLE 2 +#define MP_ALBUM 3 +#define MP_GENRE 4 +#define MP_COMMENT 5 +#define MP_YEAR 6 +#define MP_TRACK 7 + +#define ISO_8859_1 0 +#define UTF16 1 +#define UTF16BE 2 +#define UTF8 3 + + +/*************************************/ +/* errno values */ +/*************************************/ +#define MP_EERROR 1 +#define MP_EFNF 2 +#define MP_EFCOMPR 3 +#define MP_EFENCR 4 +/*define MP_EUNICODE 5*/ +#define MP_EVERSION 6 + + +/*************************************/ +/* Structs and company */ +/*************************************/ + +/* Header structure with 4 segments containing 32 bit header information */ +typedef struct _mpeg_header +{ + unsigned int syncword; /* Sync Word */ + unsigned int version; /* Version number */ + unsigned int layer; /* Layer number */ + unsigned int protbit; /* Protection Bit */ + unsigned int bitrate; /* kbit/sec */ + unsigned int samplingfreq; /* hz */ + unsigned int padbit; /* Padding bit */ + unsigned int privbit; /* Private Bit */ + unsigned int mode; /* Stereo, Joint-Stereo, Dual-Channel, Mono */ + unsigned int mode_ext; /* Mode extension */ + unsigned int copyright; /* Copyright yes/no */ + unsigned int originalhome; /* Original datastream yes/no */ + unsigned int emphasis; /* Emphasis bits */ +} mpeg_header; + + +/* Generic tag structure */ +typedef struct _id3_tag +{ + int version; /* tags version, either 1 or 2 or -1 if not supported */ + void *tag; /* pointer to specific struct */ +} id3_tag; + + +/* list of tags found in file */ +typedef struct _id3_tag_list +{ + id3_tag *tag; + struct _id3_tag_list *next; + struct _id3_tag_list *first; +} id3_tag_list; + + +/* + * The following structures are ment as low-level data holders. I strongly + * suggest you to use the appropriate generic functions below to access them. + */ + +/* V 1 */ + +/* ID3v1 tag structure */ +typedef struct _id3v1_tag +{ + char *title; + char *artist; + char *album; + char *year; + char *comment; + unsigned char track; /* track binary encoded */ + unsigned char genre; /* index on genre list - 0xFF for null */ +} id3v1_tag; + + +/* V 2 */ + +/* ID3v2 Frame structure */ +typedef struct _id3v2_frame +{ + char* frame_id; /* The frame id e.g. TALB */ + unsigned char status_flag; + unsigned char format_flag; + char *data; + unsigned int data_size; /* frame size excluding header, incl. enc.,lang.,etc. + (total frame size - 10) */ +} id3v2_frame; + +/* single linked list referencing a number of frames */ +typedef struct _id3v2_frame_list +{ + struct _id3v2_frame *data; + struct _id3v2_frame_list *next; + struct _id3v2_frame_list *start; +} id3v2_frame_list; + +/* ID3v2 Extended Header structure */ +typedef struct _id3v2_extended_header +{ + unsigned long size; + char *flag_bytes; + unsigned int no_flag_bytes; + unsigned int is_update; + unsigned int crc_data_present; + unsigned char crc_data_length; + unsigned char* crc_data; + unsigned int restrictions; + unsigned char restrictions_data_length; + unsigned char* restrictions_data; +} id3v2_extended_header; + +/* ID3v2 Header structure */ +typedef struct _id3v2_header +{ + /* Version 2.minor.revision */ + unsigned int version_minor; + unsigned int version_revision; + char flags; /* Flags - should only be set by mplib and does only contain + the following infos */ + unsigned int unsyncronization; + unsigned int has_extended_header; + unsigned int is_experimental; + unsigned int has_footer; + unsigned long total_tag_size; /* is size of all tag elements including + header and footer (each 10 bytes) */ + id3v2_extended_header *extended_header; /* Extended header */ +} id3v2_header; + + +/* ID3v2 tag structure */ +typedef struct _id3v2_tag +{ + id3v2_header *header; + id3v2_frame_list *frame_list; +} id3v2_tag; + +/* A fields content unparsed */ +typedef struct _id3_content +{ + unsigned int compressed; + unsigned int encrypted; + char *data; + unsigned int length; +} id3_content; + +typedef enum _id3_encoding +{ + iso_8859_1 = ISO_8859_1, + utf16 = UTF16, + utf16be = UTF16BE, + utf8 = UTF8 +} id3_encoding; + +typedef struct _id3_text_content +{ + id3_encoding encoding; + char *text; /* Null terminated text */ +} id3_text_content; + +typedef struct _id3_comment_content +{ + id3_encoding encoding; + char *language; /* ISO Language code */ + char *short_descr; /* Null term. content short description */ + char *text; /* Null terminated text */ +} id3_comment_content; + +/***************************************/ +/* Functions */ +/***************************************/ + +/* Allocates a MPEG header structure from a file + * Arg 1 - The filename + * Returns - A pointer to a new initialized header structure - NULL on IO Error + */ +extern mpeg_header *mp_get_mpeg_header_from_file __P((const char*)); + + +/* Gets the header structure from a file descriptor + * Arg 1 - The file descriptor + * Returns - A pointer to a new initialized header structure - NULL on IO Error + */ +extern mpeg_header *mp_get_mpeg_header_from_fd __P((int)); + + +/* Frees a mpeg header structure + * Arg 1 - The allocated mpeg header + */ +#define mp_free_mpeg_header(str) xfree(str) + + +/* Allocates a label with the appropriate header field value as a string */ +extern char *mp_get_str_version __P((const mpeg_header*)); +extern char *mp_get_str_layer __P((const mpeg_header*)); +extern char *mp_get_str_bitrate __P((const mpeg_header*)); +extern char *mp_get_str_samplingfreq __P((const mpeg_header*)); +extern char *mp_get_str_mode __P((const mpeg_header*)); + + +/* Allocates and fills a list of tags found in the given file. This list + * will contain at least one and at most two tags or is NULL if no tags + * have been found. + * Arg 1 - The files name/file descriptor to search for tags + * Returns - A pointer to a initialized list struct or null if no tags have + * been found + */ +extern id3_tag_list* mp_get_tag_list_from_file __P((const char*)); +extern id3_tag_list* mp_get_tag_list_from_fd __P((int)); + + +/* Frees a tag list beginning with the given element XXX */ +extern void mp_free_list __P((id3_tag_list*)); + + +/* Gets the first content found of a specified field in the given tag and + * allocates a struct. + * Arg 1 - The tag + * Arg 2 - The fields identifier + * + * Returns The new allocated content for the specified field or NULL + * On NULL: errno set to the following values + * MP_EERROR - General failure: may occure on wrong usage, but should never happen + * MP_EFNF - Field does not exists in tag /invalid identifier + * MP_EVERSION - Tag has a version set that is not supported by the library + */ +extern id3_content* mp_get_content __P((const id3_tag*, int)); + +/* It's posible that a tag has multiple ocurances of a field. + * Use this function to get a specified field by position. The first + * ocurance of the field in the tag is 0. + * e.g.: To get the third comment in an id3v2 tag use + * mp_get_content_at_pos(tag, MP_COMMENT, 2); + * Arg 1 - The tag + * Arg 2 - The fields identifier + * Arg 3 - The content position in the tag + * Returns - see mp_get_content + */ +extern id3_content* mp_get_content_at_pos __P((const id3_tag*, int, int)); + +/* Gets a custom fields content and allocates a struct. This function can + * only be applied to ID3v2 tags. It will lookup a by the given identifier + * and return its content. + * Arg 1 - The tag + * Arg 2 - The field names identifier e.g. ENCR + * Returns - see mp_get_content + */ +extern id3_content* mp_get_content_custom __P((const id3_tag*, const char*)); + +/* See mp_get_content_at_pos() and mp_get_content_custom() + * Arg 1 - The tag + * Arg 2 - The field names identifier e.g. ENCR + * Arg 3 - The content position in the tag + * Returns - see mp_get_content + */ +extern id3_content* mp_get_content_custom_at_pos __P((const id3_tag*, const char*, int)); + +/* Frees a content struct */ +extern void mp_free_content __P((id3_content*)); +extern void mp_free_text_content __P((id3_text_content*)); +extern void mp_free_comment_content __P((id3_comment_content*)); + + +/* Copys the value of a specified field into the given tag. The content + * argument may be freed after using this function. The way a content + * is represented in a tag depends from the tags version and kind of field. + * I.e. it may be nessecary to represent a track number as a binary value in a v1 + * tag or to embeded it into a frame for a v2 tag. The caller just needs to + * give the correct identifier with the value as a id3_content and to take + * care of freeing the id3_content value afterwards. + * Arg 1 - The tag to edit + * Arg 2 - The fields identifier + * Arg 3 - The fields new content + * Returns - 0 success or one of the following errors + * MP_EERROR - General failure: may occure on wrong usage, but should never happen + * MP_EFNF - Field does not exists in tag /invalid identifier + * MP_EVERSION - Function isn't able to handle a tag of this version + */ +extern int mp_set_content __P((id3_tag*, int, id3_content*)); +extern int mp_set_content_at_pos __P((id3_tag*, int, id3_content*, int)); + +/* Sets up a new custom field with the given value + * Arg 1 - The tag to edit + * Arg 2 - The new fields name - A four chars upper case identifier e.g. ENCR + * Arg 3 - The fields new content + * Returns - See mp_set_content + */ +extern int mp_set_custom_content __P((id3_tag*, char*, id3_content*)); +extern int mp_set_custom_content_at_pos __P((id3_tag*, char*, id3_content*, int)); + +/* Writes the tag to the specified file + * Arg 1 - The tag list to be added to file - may be NULL for deleting all tags + * Arg 2 - The files name/file descriptor + * Returns - 0 on success or one of the following errors + * MP_EERROR - General failure: may occure on wrong usage, but should never happen + * MP_EVERSION - Function isn't able to handle a tag of this version + */ +extern int mp_write_to_file __P((const id3_tag_list*, const char*)); +extern int mp_write_to_fd __P((const id3_tag_list*, int)); + +/* Deletes all tags in file + * Arg 1 - The filename of fd + * Return - 0 on success + */ +extern int mp_del_tags_from_file __P((const char*)); +extern int mp_del_tags_from_fd __P((int)); + +/* Deletes all tags in file with the specified version + * Arg 1 - The filename or fd + * Arg 2 - The version + */ +extern int mp_del_tags_by_ver_from_file __P((const char*, int)); +extern int mp_del_tags_by_ver_from_fd __P((int, int)); + +/* Converts a tag to id3v1 or id3v2 tag format + * Arg 1 - The tag to be converted + * Returns - 0 on success or one of the following errors + * MP_EVERSION - Function isn't able to handle a tag of this version + */ +extern int mp_convert_to_v1 __P((id3_tag*)); +extern int mp_convert_to_v2 __P((id3_tag*)); + +/* Checks wether the given value would be a valid v1 field + * Arg 1 - The field + * Arg 2 - The value + * Returns - 0 if test failed + */ +extern int mp_is_valid_v1_value __P((int, char*)); + +/* Parses a content field + * Arg 1 - the content to parse + * Returns - A pointer to a new initialized structure suitable for the content + * or NULL + * On NULL: errno set to the following values + * MP_EERROR - General failure: may occure on wrong usage, but should never happen + * MP_EFENCR - The value for this field has been encrypted and can thus not be retrieved + * MP_EFCOMPR - The value for this field has been compressed and can thus not be retrieved + */ +extern id3_text_content *mp_parse_artist __P((const id3_content*)); +extern id3_text_content *mp_parse_title __P((const id3_content*)); +extern id3_text_content *mp_parse_album __P((const id3_content*)); +extern id3_text_content *mp_parse_year __P((const id3_content*)); +extern id3_text_content *mp_parse_genre __P((const id3_content*)); +extern id3_text_content *mp_parse_track __P((const id3_content*)); +extern id3_text_content *mp_parse_text __P((const id3_content*)); +extern id3_comment_content *mp_parse_comment __P((const id3_content*)); + +/* Assembles content from a comont text content + * Arg 1 - the text + * Arg 2 - the texts encoding (NULL) + * Returns - A pointer to a new initialized content structure + */ +extern id3_content *mp_assemble_text_content __P((const char*, id3_encoding)); + +/* Assembles content from a comment + * Arg 1 - the text + * Arg 2 - a short describtion to the text (NULL) + * Arg 3 - the texts encoding + * Arg 4 - the comments language (NULL) + * Returns - A pointer to a new initialized content structure + */ +extern id3_content *mp_assemble_comment_content __P((const char*, const char*, id3_encoding, const char*)); + +/* Gets a new allocated tag */ +extern id3_tag* mp_alloc_tag __P((void)); +extern id3_tag* mp_alloc_tag_with_version __P((int)); + +/* Frees tag struct */ +extern void mp_free_tag __P((id3_tag *)); + +__END_DECLS + +#endif /* __MPLIB_H */ diff --git a/libmpio/mplib/mplib_paas.c b/libmpio/mplib/mplib_paas.c new file mode 100644 index 0000000..441e474 --- /dev/null +++ b/libmpio/mplib/mplib_paas.c @@ -0,0 +1,256 @@ +/* + * mplib - a library that enables you to edit ID3 tags + * Copyright (C) 2001,2002 Stefan Podkowinski + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if STDC_HEADERS +# include +# include +#elif HAVE_STRINGS_H +# include +#endif /*STDC_HEADERS*/ + +#if HAVE_UNISTD_H +# include +# include +#endif + +#include +#include +#include + +#include "mplib.h" +#include "xmalloc.h" + + + +/******************************************************************************************* + * Parse functions + *******************************************************************************************/ + + id3_text_content* + mp_parse_artist(const id3_content* content) + { + return mp_parse_text(content); + } + + id3_text_content* + mp_parse_title(const id3_content* content) + { + return mp_parse_text(content); + } + + id3_text_content* + mp_parse_album(const id3_content* content) + { + return mp_parse_text(content); + } + + id3_text_content* + mp_parse_year(const id3_content* content) + { + return mp_parse_text(content); + } + + id3_text_content* + mp_parse_genre(const id3_content* content) + { + return mp_parse_text(content); + } + + id3_text_content* + mp_parse_track(const id3_content* content) + { + return mp_parse_text(content); + } + + id3_comment_content* + mp_parse_comment(const id3_content* content) + { + id3_comment_content* cc; + int i, e; + + if(!content || !content->data) + { + errno = MP_EERROR; + return NULL; + } + + if(content->encrypted) + { + errno = MP_EFENCR; + return NULL; + } + if(content->compressed) + { + errno = MP_EFCOMPR; + return NULL; + } + + cc = XMALLOCD0(id3_comment_content, "mp_parse_comment:cc"); + + e = content->data[0]; + if(e >= ISO_8859_1 && e <= UTF8) cc->encoding = e; + else cc->encoding = 0; + + cc->language = xmallocd(4, "mp_parse_comment:cc->language"); + cc->language[0] = content->data[1]; + cc->language[1] = content->data[2]; + cc->language[2] = content->data[3]; + cc->language[3] = 0; + + if(content->data[4]) /* short descr. */ + { + i = strlen(content->data + 4) + 1; + cc->short_descr = xmallocd(i, "mp_parse_comment:cc->short_descr"); + strncpy(cc->short_descr, content->data + 4, i); + } + else + { + cc->short_descr = NULL; + i = 1; + } + + cc->text = xmallocd(content->length - 4 - i + 1, "mp_parse_comment:cc->text"); + memcpy(cc->text, content->data + 4 + i, content->length - 4 - i); + cc->text[content->length - 4 - i] = 0; + + return cc; + } + + id3_text_content* + mp_parse_text(const id3_content* content) + { + id3_text_content* tc; + int e; + + if(!content || !content->data) + { + errno = MP_EERROR; + return NULL; + } + + if(content->encrypted) + { + errno = MP_EFENCR; + return NULL; + } + if(content->compressed) + { + errno = MP_EFCOMPR; + return NULL; + } + + tc = XMALLOCD0(id3_text_content, "mp_parse_text:tc"); + tc->text = xmallocd(content->length, "mp_parse_text:tc->text"); + e = content->data[0]; + if(e >= ISO_8859_1 && e <= UTF8) tc->encoding = e; + else tc->encoding = 0; + + memcpy(tc->text, content->data + 1, content->length - 1); + tc->text[content->length - 1] = 0; + /* XXX multiple entries */ + return tc; + } + + /******************************************************************************************* + * Assemble functions + *******************************************************************************************/ + + id3_content* + mp_assemble_artist_content(const char* text, id3_encoding enc) + { + return mp_assemble_text_content(text, enc); + } + + id3_content* + mp_assemble_title_content(const char* text, id3_encoding enc) + { + return mp_assemble_text_content(text, enc); + } + + id3_content* + mp_assemble_album_content(const char* text, id3_encoding enc) + { + return mp_assemble_text_content(text, enc); + } + + id3_content* + mp_assemble_year_content(const char* text, id3_encoding enc) + { + return mp_assemble_text_content(text, enc); + } + + id3_content* + mp_assemble_genre_content(const char* text, id3_encoding enc) + { + return mp_assemble_text_content(text, enc); + } + + id3_content* + mp_assemble_text_content(const char* text, id3_encoding enc) + { + id3_content *ret; + + if(!text) return NULL; + + ret = XMALLOCD0(id3_content, "mp_assemble_text_content:ret"); + ret->length = strlen(text) + 1; + ret->data = xmallocd(ret->length, "mp_asseble_text_content:ret->data"); + ret->data[0] = enc; + strncpy(ret->data + 1, text, strlen(text)); + + return ret; + } + + id3_content* + mp_assemble_comment_content(const char* text, const char* short_descr, id3_encoding enc, const char* lang) + { + id3_content *ret; + + if(!text) return NULL; + + ret = XMALLOCD0(id3_content, "mp_assemble_comment_content:ret"); + ret->length = strlen(text) + 5; + if(short_descr) ret->length += strlen(short_descr); + + ret->data = xmallocd(ret->length, "mp_assemble_comment_content:ret->data"); + ret->data[0] = enc; + if(lang && strlen(lang) == 3) + { + ret->data[1] = lang[0]; + ret->data[2] = lang[1]; + ret->data[3] = lang[2]; + } + else + { + ret->data[1] = 'X'; + ret->data[2] = 'X'; + ret->data[3] = 'X'; + } + if(short_descr) strcpy(ret->data + 4, short_descr); + else ret->data[4] = 0; + + if(short_descr) strncpy(ret->data + 5 + strlen(short_descr), text, strlen(text)); + else strncpy(ret->data + 5, text, strlen(text)); + + return ret; + + } diff --git a/libmpio/mplib/mplib_s.c b/libmpio/mplib/mplib_s.c new file mode 100644 index 0000000..2c336b4 --- /dev/null +++ b/libmpio/mplib/mplib_s.c @@ -0,0 +1,1177 @@ +/* + * mplib - a library that enables you to edit ID3 tags + * Copyright (C) 2001,2002 Stefan Podkowinski + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if STDC_HEADERS +# include +# include +#elif HAVE_STRINGS_H +# include +#endif /*STDC_HEADERS*/ + +#if HAVE_UNISTD_H +# include +# include +#endif + + +#include +#include +#include +#include +#include + +#include "mplib_s.h" +#include "xmalloc.h" + + + +/******************************/ +/* Static functions */ +/******************************/ + +static id3v1_tag* +id3v1_get_tag(int fd) +{ + id3v1_tag *tag; + char *c; + + tag = XMALLOCD0(id3v1_tag, "id3v1_get_tag:tag"); + + c = (char *)xmallocd(3, "id3v1_get_tag:c"); + + if(lseek(fd, -128L, SEEK_END) == -1) goto exit_on_error; + if(read(fd, c, 3) < 3) goto exit_on_error; + if(strncmp(c, "TAG", 3) != 0) goto exit_on_error; + + tag->title = (char *)xmallocd(31, "id3v1_get_tag:tag->title"); + if(read(fd, tag->title, 30) < 30) goto exit_on_error; + if(tag->title[0] == 0 || id3_is_only_space(tag->title, 30)) { + xfree(tag->title); + tag->title = NULL; + } else tag->title[30] = 0; + + tag->artist = (char*)xmallocd(31, "id3v1_get_tag:tag->artist"); + if(read(fd, tag->artist, 30) < 30) goto exit_on_error; + if(tag->artist[0] == 0 || id3_is_only_space(tag->artist, 30)) { + xfree(tag->artist); + tag->artist = NULL; + } else tag->artist[30] = 0; + + tag->album = (char*)xmallocd(31, "id3v1_get_tag:tag->album"); + if(read(fd, tag->album, 30) < 30) goto exit_on_error; + if(tag->album[0] == 0 || id3_is_only_space(tag->album, 30)) { + xfree(tag->album); + tag->album = NULL; + } else tag->album[30] = 0; + + tag->year = (char*)xmallocd(5, "id3v1_get_tag:tag->year"); + if(read(fd, tag->year, 4) < 4) goto exit_on_error; + if(tag->year[0] == 0 || id3_is_only_space(tag->year, 4)) { + xfree(tag->year); + tag->year = NULL; + } else tag->year[4] = 0; + + tag->comment = (char*)xmallocd(31, "id3v1_get_tag:tag->comment"); + if(read(fd, tag->comment, 30) < 30) goto exit_on_error; + tag->comment[30] = 0; + + if(read(fd, &(tag->genre), 1) < 1) goto exit_on_error; + + /* Looking for v1.1 track info */ + if(tag->comment && tag->comment[28] == 0 && tag->comment[29] != 0) + { + tag->track = tag->comment[29]; + tag->comment[29] = 0; + } + else + { + tag->track = 0; + } + + /* Set comment to NULL if not set - this happens at this point because */ + /* there maybe a track info anyway */ + if(tag->comment[0] == 0 || id3_is_only_space(tag->comment, 28)) { + xfree(tag->comment); + tag->comment = NULL; + } + + xfree(c); + return tag; + + exit_on_error: + + xfree(c); + id3v1_free_tag(tag); + return NULL; +} + +static int +id3v1_add_tag(int fd, id3v1_tag *tag) +{ + int i, j; + void *blank, *set; + char *b_tag, *b_tag_start; + + blank = xmallocd0(30, "id3v1_add_tag:blank"); + set = xmallocd(30, "id3v1_add_tag:set"); + memset(set, 0xFF, 30); + b_tag = b_tag_start = (char *)xmallocd0(128, "id3v1_add_tag:b_tag"); + + strncpy(b_tag, "TAG", 3); b_tag += 3; + + if(tag->title) + { + j = strlen(tag->title); + strncpy(b_tag, tag->title, j); b_tag += j; + i = 30 - j; + if(i > 0) + { + strncpy(b_tag, blank, i); b_tag += i; + } + } + else + { + strncpy(b_tag, blank, 30); b_tag += 30; + } + + if(tag->artist) + { + j = strlen(tag->artist); + strncpy(b_tag, tag->artist, j); b_tag += j; + i = 30 - j; + if(i > 0) + { + strncpy(b_tag, blank, i); b_tag += i; + } + } + else + { + strncpy(b_tag, blank, 30); b_tag += 30; + } + + if(tag->album) + { + j = strlen(tag->album); + strncpy(b_tag, tag->album, j); b_tag += j; + i = 30 - j; + if(i > 0) + { + strncpy(b_tag, blank, i); b_tag += i; + } + } + else + { + strncpy(b_tag, blank, 30); b_tag += 30; + } + + if(tag->year) + { + j = strlen(tag->year); + strncpy(b_tag, tag->year, j); b_tag += j; + i = 4 - j; + if(i > 0) + { + strncpy(b_tag, blank, i); b_tag += i; + } + } + else + { + strncpy(b_tag, blank, 4); b_tag += 4; + } + + if(tag->comment) + { + int hastrack = 0; + j = strlen(tag->comment); + if(tag->track > 0) hastrack = 1; + if(hastrack && j > 28) + { + strncpy(b_tag, tag->comment, 28); b_tag += 28; + } + else + { + strncpy(b_tag, tag->comment, j); b_tag += j; + i = ((tag->track > 0) ? 28 : 30) - j; + } + if(i > 0) + { + strncpy(b_tag, blank, i); b_tag += i; + } + } + else + { + strncpy(b_tag, blank, (tag->track > 0) ? 28 : 30); + b_tag += (tag->track > 0) ? 28 : 30; + } + + if(tag->track > 0) + { + strncpy(b_tag, blank, 1); b_tag += 1; + strncpy(b_tag, &(tag->track), 1); b_tag += 1; + } + if(tag->genre != 0xFF) + { + strncpy(b_tag, &(tag->genre), 1); b_tag += 1; + } + else + { + strncpy(b_tag, set, 1); b_tag += 1; + } + + j = 0; + + if(lseek(fd, 0L, SEEK_END) != -1) + { + if(write(fd, b_tag - 128, 128) < 128) j = 1; + } + else j = 1; + + xfree(b_tag_start); + xfree(blank); + xfree(set); + + return j; +} + +static int +id3v2_add_tag(int fd, id3v2_tag *tag, id3v2_tag *old) +{ + unsigned char *btag, *btag_start; + unsigned char flag = 0; + int i, j; + char *b_tag, *b_tag_start, *d; + id3v2_frame_list *frame_list; + id3v2_frame *frame; + + /* at first we are going to write the tags raw data into + the btag byte array */ + btag = btag_start = xmallocd0(tag->header->total_tag_size, + "id3v2_add_tag:btag"); + strncpy(btag, "ID3", 3); btag += 3; + *btag = (char)tag->header->version_minor; btag += 1; + *btag = (char)tag->header->version_revision; btag += 1; + flag |= ((tag->header->unsyncronization & 1) << 7); + flag |= ((tag->header->has_extended_header & 1) << 6); + flag |= ((tag->header->is_experimental & 1) << 5); + flag |= ((tag->header->has_footer & 1) << 4); + memcpy(btag, &flag, 1); btag += 1; + + if(old) + { + i = old->header->total_tag_size - 10; + if(old->header->has_footer) i -= 10; + } + else + { + i = tag->header->total_tag_size - 10; + if(tag->header->has_footer) i -= 10; + /* add padding to total size we mean to store on disk */ + /* mplib does not use any kind of padding internaly */ +/* i += 1024; */ /* no padding needed for MPIO use */ + } + + d = id3_sync32(i); + btag[0] = d[0]; + btag[1] = d[1]; + btag[2] = d[2]; + btag[3] = d[3]; + xfree(d); + btag += 4; + + if(tag->header->has_extended_header) + { + d = id3_sync32(tag->header->extended_header->size); + btag[0] = d[0]; + btag[1] = d[1]; + btag[2] = d[2]; + btag[3] = d[3]; + xfree(d); + btag += 4; + + *btag = (char)tag->header->extended_header->no_flag_bytes; btag += 1; + flag = ((tag->header->extended_header->is_update & 1) << 6); + flag |= ((tag->header->extended_header->crc_data_present & 1) << 5); + flag |= ((tag->header->extended_header->restrictions & 1) << 4); + memcpy(btag, &flag, 1); btag += 1; + if(tag->header->extended_header->is_update) + { + btag[0] = 0; btag += 1; + } + if(tag->header->extended_header->crc_data_present) + { + int length = tag->header->extended_header->crc_data_length ? tag->header->extended_header->crc_data_length : 5; + *btag = (char)length; btag += 1; + memcpy(btag, tag->header->extended_header->crc_data, length); btag += 1; + } + if(tag->header->extended_header->restrictions) + { + int length = tag->header->extended_header->restrictions_data_length ? tag->header->extended_header->restrictions_data_length : 5; + *btag = (char)length; btag += 1; + memcpy(btag, tag->header->extended_header->restrictions_data, length); btag += 1; + } + } + + frame_list = tag->frame_list; + while(frame_list) { + int j; + frame = frame_list->data; + + strncpy(btag, frame->frame_id, 4); btag += 4; + d = id3_sync32(frame->data_size); + btag[0] = d[0]; + btag[1] = d[1]; + btag[2] = d[2]; + btag[3] = d[3]; + xfree(d); + btag += 4; + memcpy(btag, &frame->status_flag, 1); btag += 1; + memcpy(btag, &frame->format_flag, 1); btag += 1; + + memcpy(btag, frame->data, frame->data_size); btag += frame->data_size; + + frame_list = frame_list->next; + } + + /* XXX footer not supported yet */ + + /* if an old tag was provided it is desired to overwrite it */ + /* else this is a brand new tag */ + if(old) { + FILE *file; + void *ptr = xmallocd0(old->header->total_tag_size - tag->header->total_tag_size, "id3v2_add_tag:ptr"); + if(!(file = fdopen(fd, "r+b"))) + { + xfree(ptr); + goto exit_on_error; + } + + fseek(file, 0, SEEK_SET); + if(fwrite(btag_start, tag->header->total_tag_size, 1, file) < 1) + { + xfree(ptr); + goto exit_on_error; + } + + /* write padding till end of old tag */ + if(fwrite(ptr, old->header->total_tag_size - tag->header->total_tag_size, 1, file) < 1) { + xfree(ptr); + goto exit_on_error; + } + + fflush(file); + xfree(ptr); + + } else { + FILE *file, *tmp; + int read; + void *ptr, *blank; + unsigned char *c; + + ptr = xmallocd(4096, "id3v2_add_tag:ptr"); + blank = xmallocd0(1024, "id3v2_add_tag:blank"); + + file = fdopen(fd, "r+b"); + tmp = tmpfile(); + if(!(file && tmp)) + { + fflush(file); + fclose(tmp); + xfree(ptr); + xfree(blank); + goto exit_on_error; + } + + fseek(file, 0, SEEK_SET); + fseek(tmp, 0, SEEK_SET); + + /* write tag in tmp file */ + fwrite(btag_start, tag->header->total_tag_size, 1, tmp); + + /* Write 1024b padding */ + /* no padding needed for MPIO use */ +/* fwrite(blank, 1024, 1, tmp); */ + + /* write rest of file */ + while(!feof(file)) + { + read = fread(ptr, 1, 4096, file); + if(fwrite(ptr, 1, read, tmp) != read && !feof(file)) + { + fflush(file); + fclose(tmp); + xfree(ptr); + xfree(blank); + goto exit_on_error; + } + } + + fflush(tmp); + + fseek(file, 0, SEEK_SET); + fseek(tmp, 0, SEEK_SET); + while(!feof(tmp)) + { + read = fread(ptr, 1, 4096, tmp); + if(fwrite(ptr, 1, read, file) != read && !feof(tmp)) + { + fflush(file); + fclose(tmp); + xfree(ptr); + xfree(blank); + goto exit_on_error; + } + } + + fflush(file); + fclose(tmp); + + xfree(ptr); + xfree(blank); + } + + xfree(btag_start); + return 0; + + exit_on_error: + xfree(btag_start); + return MP_EERROR; +} + +static int +id3v1_del_tag(int fd) +{ + int nlength; + unsigned char *c; + struct stat fs; + + if(fstat(fd, &fs)) return 1; + + if(fs.st_size < 128) return 1; /* Hardly a valid mpeg file.. */ + + c = (char *)xmallocd(3, "id3v1_del_tag:c"); + if(lseek(fd, -128L, SEEK_END) == -1) goto exit_on_error; + if(read(fd, c, 3) < 3) goto exit_on_error; + if(strncmp(c, "TAG", 3)) goto exit_on_error; + xfree(c); + + nlength = fs.st_size - 128; + + if(ftruncate(fd, nlength)) return 1; + + return 0; + + exit_on_error: + xfree(c); + return 1; +} + + +static int +id3v2_del_tag(int fd, id3v2_tag *t) +{ + unsigned char *c; + long tag_len, file_len; + FILE *file, *tmp; + int read; + void *ptr; + id3v2_tag *tfound = NULL;; + + if(!t) + { + t = id3v2_get_tag(fd); + if(!t) return 0; + else tfound = t; + } + + ptr = xmallocd(4096, "id3v2_del_tag:ptr"); + + tag_len = t->header->total_tag_size; + file_len = lseek(fd, 0, SEEK_END); + if(file_len < 1 || tag_len < 1) goto exit_on_error; + + /* use os system buffering */ + file = fdopen(fd, "r+b"); + tmp = tmpfile(); + if(!(file && tmp)) goto exit_on_error; + + fseek(file, tag_len, SEEK_SET); + fseek(tmp, 0, SEEK_SET); + while(!feof(file)) + { + read = fread(ptr, 1, 4096, file); + if(fwrite(ptr, 1, read, tmp) != read && !feof(file)) + goto exit_on_error; + } + + fflush(tmp); + + fseek(file, 0, SEEK_SET); + fseek(tmp, 0, SEEK_SET); + while(!feof(tmp)) + { + read = fread(ptr, 1, 4096, tmp); + if(fwrite(ptr, 1, read, file) != read && !feof(tmp)) + goto exit_on_error; + } + + fclose(tmp); + xfree(ptr); + if(tfound) id3v2_free_tag(tfound); + return 0; + + exit_on_error: + fclose(tmp); + xfree(ptr); + if(tfound) id3v2_free_tag(tfound); + return 1; +} + + +static int +id3v1_truncate_tag(id3v1_tag *tag) +{ + int notrunc = 0; + int len = 0; + void *ptr; + + if(tag->title && (len = strlen(tag->title)) > 30) + { + realloc(tag->title, 31); + tag->title[30] = 0; + } + + if(tag->artist && (len = strlen(tag->artist)) > 30) + { + realloc(tag->artist, 31); + tag->artist[30] = 0; + } + + if(tag->album && (len = strlen(tag->album)) > 30) + { + realloc(tag->album, 31); + tag->album[30] = 0; + } + + if(tag->year && (len = strlen(tag->year)) > 4) + { + realloc(tag->title, 5); + tag->title[4] = 0; + } + + if(tag->comment) + { + int max = (tag->track > 0) ? 28 : 30; + if((len = strlen(tag->comment)) > max) + { + realloc(tag->comment, max + 1); + tag->comment[max] = 0; + } + } + + return notrunc; +} + +static int +id3_is_only_space(char *str, int strlen) +{ + int i = 0; + + while(i < strlen) + { + if(str[i] != 0x20) return 0; + i++; + } + + return 1; +} + +static id3v2_tag* +id3v2_get_tag(int fd) +{ + unsigned char *c; + id3v2_header *header; + id3v2_frame_list *frame_list; + id3v2_frame *frame; + id3v2_tag *tag = NULL; + int i; + + if(lseek(fd, 0L, SEEK_SET) == -1) return NULL; + + c = (unsigned char*)xmallocd0(1024, "id3v2_get_tag:c"); + + if(read(fd, c, 10) < 10) goto exit_on_error; + + c[10] = 0; + + if(strncmp(c, "ID3", 3)) goto exit_on_error; + + header = XMALLOCD0(id3v2_header, "id3v2_get_tag:header"); + header->version_minor = c[3]; + header->version_revision = c[4]; + header->flags = c[5]; + header->unsyncronization = (c[5] & 128) >> 7; + header->has_extended_header = (c[5] & 64) >> 6; + header->is_experimental = (c[5] & 32) >> 5; + header->has_footer = (c[5] & 16) >> 4; + + header->total_tag_size = id3_unsync32(c, 6) + 10; + if(header->has_footer) header->total_tag_size += 10; + + tag = XMALLOCD0(id3v2_tag, "id3v2_get_tag:tag"); + + /* check if version is supported */ + if(c[3] != 3 && c[3] != 4) + { + xfree(c); + tag->header = header; + tag->frame_list = NULL; + return tag; + } + + frame_list = XMALLOCD0(id3v2_frame_list, "id3v2_get_tag:frame_list"); + frame_list->start = frame_list; + + /* assigning header and frame list to tag */ + tag->header = header; + tag->frame_list = frame_list; + + if(header->has_extended_header) + { + id3v2_extended_header *xt_header = XMALLOCD0(id3v2_extended_header, + "id3v2_get_tag:id3v2_extended_header"); + + header->extended_header = xt_header; + + read(fd, c, 4); /* get length of extended header */ + xt_header->size = id3_unsync32(c, 0); + + read(fd, c, 1); /* get number of flags */ + xt_header->no_flag_bytes = (c[0] > 0) ? c[0] : 1; + + read(fd, c, xt_header->no_flag_bytes); /* get flag bytes */ + xt_header->is_update = (c[0] & 64) >> 6; + xt_header->crc_data_present = (c[0] & 32) >> 5; + xt_header->restrictions = (c[0] & 16) >> 4; + + /* Flag data */ + if(xt_header->is_update) read(fd, c, 1); /* Data length ind. is 0 -skip */ + if(xt_header->crc_data_present) { + read(fd, c, 1); /* data length - shoud be 5 */ + if(*c != 5) goto exit_on_error; /* else things might + break badly */ + xt_header->crc_data_length = *c; + xt_header->crc_data = xmallocd0(*c, "id3v2_get_tag:xt_header->crc_data"); + read(fd, xt_header->crc_data, *c); + } + if(xt_header->restrictions) { + read(fd, c, 1); /* data length - shoud be 1 */ + if(*c != 1) goto exit_on_error; + xt_header->restrictions_data_length = *c; + xt_header->restrictions_data = xmallocd0(*c, + "id3v2_get_tag:xt_header->restrictions_data"); + read(fd, xt_header->restrictions_data, *c); + } + } + + /* Read frames */ + while(lseek(fd, 0L, SEEK_CUR) < header->total_tag_size) + { + int hasEnc = 0, hasLang = 0, d; + + read(fd, c, 10); /* Read header */ + + /* break if padding is reached - this should never happen here.. */ + if(c[0] == 0 && c[1] == 0 && c[2] == 0 && c[3] == 0) break; + + /* Check if possible id is alpha numeric */ + if(!isalnum(c[0]) || !isalnum(c[1]) || !isalnum(c[2]) || !isalnum(c[3])) break; + + frame = XMALLOCD(id3v2_frame, "id3v2_get_tag:frame"); + frame->frame_id = xmallocd(4, "id3v2_get_tag:frame->frame_id"); + strncpy(frame->frame_id, c, 4); + frame->data_size = id3_unsync32(c, 4); + frame->status_flag = c[8]; + frame->format_flag = c[9]; + + /* Getting frame content */ + frame->data = xmallocd(frame->data_size, "id3v2_get_tag:frame->data_size"); + read(fd, frame->data, frame->data_size); + + /* Add frame to list */ + if(frame_list->data) + { + frame_list->next = XMALLOCD(id3v2_frame_list, "id3v2_get_tag:frame_list->next"); + frame_list->next->start = frame_list->start; + frame_list = frame_list->next; + frame_list->next = NULL; + } + frame_list->data = frame; + } + + xfree(c); + return tag; + + exit_on_error: + + xfree(c); + id3v2_free_tag(tag); + return NULL; +} + +static char ** +id3v2_get_names(id3v2_tag *tag) +{ + id3v2_frame *frame; + id3v2_frame_list *frame_list; + char **clist; + int i; + + if(!tag->frame_list) return NULL; + + frame_list = tag->frame_list; + + i = id3_get_no_frames(tag); + clist = xmallocd(sizeof(char*) * i+1, "id3v2_get_names:clist"); + clist[i] = 0; + + for(i = 0; frame_list; i++) + { + if(!frame_list->data) continue; + frame = frame_list->data; + + if(!frame->frame_id) continue; + clist[i] = xmallocd(5, "id3v2_get_names:clist[i]"); + strncpy(clist[i], frame->frame_id, 4); + clist[i][4] = 0; + frame_list = frame_list->next; + } + return clist; +} + + +static id3_content* +id3v1_get_content(id3v1_tag *tag, int field) +{ + int i; + char *c; + id3_content *ret; + + switch(field) + { + case MP_ARTIST: + if(!tag->artist) + { + errno = MP_EFNF; + return NULL; + } + return mp_assemble_text_content(tag->artist, ISO_8859_1); + + case MP_TITLE: + if(!tag->title) + { + errno = MP_EFNF; + return NULL; + } + return mp_assemble_text_content(tag->title, ISO_8859_1); + + case MP_ALBUM: + if(!tag->album) + { + errno = MP_EFNF; + return NULL; + } + return mp_assemble_text_content(tag->album, ISO_8859_1); + + case MP_YEAR: + if(!tag->year) + { + errno = MP_EFNF; + return NULL; + } + return mp_assemble_text_content(tag->year, ISO_8859_1); + + case MP_COMMENT: + if(!tag->comment) + { + errno = MP_EFNF; + return NULL; + } + return mp_assemble_comment_content(tag->comment, NULL, ISO_8859_1, NULL); + + case MP_GENRE: + if(tag->genre < GLL) + { + return mp_assemble_text_content(genre_list[tag->genre], ISO_8859_1); + } + else + { + errno = MP_EFNF; + return NULL; + } + + case MP_TRACK: + if(!tag->track) + { + errno = MP_EFNF; + return NULL; + } + + if(tag->track < 10) i = 2; + else if(tag->track < 100) i = 3; + else i = 4; + c = xmallocd(i, "id3v1_get_content:c"); + snprintf(c, i, "%d", tag->track); + ret = mp_assemble_text_content(c, ISO_8859_1); + xfree(c); + return ret; + + default: + errno = MP_EFNF; + return NULL; + } +} + +static id3_content* +id3v2_get_content_at_pos(id3v2_tag *tag, const char *field, int pos) +{ + id3v2_frame_list *frame_list; + id3v2_frame *frame; + int i, found, j; + + if(!tag->frame_list || !field) + { + errno = MP_EERROR; + return NULL; + } + + frame_list = tag->frame_list; + + for(i = 0, found = 0; frame_list; i++, frame_list = frame_list->next) + { + if(!frame_list->data) continue; + frame = frame_list->data; + + if(!frame->frame_id || !frame->data) continue; + if(strncmp(frame->frame_id, field, 4)) continue; + + if(found == pos) + { + id3_content *ret = XMALLOCD0(id3_content, + "id3v2_get_content_at_pos:ret"); + if(frame_is_compressed(frame)) ret->compressed = 1; + else ret->compressed = 0; + + if(frame_is_encrypted(frame)) ret->encrypted = 1; + else ret->encrypted = 0; + + ret->length = frame->data_size; + ret->data = xmallocd(frame->data_size, "id3v2_get_content_at_pos:ret->data"); + ret->data = memcpy(ret->data, frame->data, frame->data_size); + + return ret; + } + + found++; + } + + errno = MP_EFNF; + return NULL; +} + +static long +id3_sync(unsigned char* data, long length) +{ + int i; + + for(i = 0; i < length - 1; i++) + { + if(((data[i] & 0xFF) == 0xFF && (data[i+1] & 0xE0) == 0xE0) || + (i+2 < length && (data[i] & 0xFF) == 0xFF && data[i+1] == 0 && data[i+2] != 0)) + { + + realloc(data, length + 1); length++; + memmove(data + i+2, data + i+1, length - i - 2); + memset(data + i+1, 0, 1); + } + } + + return length; +} + +static long +id3_unsync(unsigned char* data, long length) +{ + /* TODO */ + /* this function is supposed to make syncsafe values normal again */ + /* we don't need this yet, because there are no fields supported that will */ + /* have the unsynchronization scheme applied */ + + +} + +static unsigned int +id3_unsync32(unsigned char* c, int start) +{ + return c[start+3] + (c[start+2] << 7) + (c[start+1] << 14) + (c[start] << 21); +} + +int +id3_get_no_frames(id3v2_tag *tag) +{ + int i; + id3v2_frame_list *frame_list = tag->frame_list; + + for(i = 0; frame_list; i++) + frame_list = frame_list->next; + + return i; +} + +static unsigned char * +id3_sync32(unsigned int i) +{ + unsigned char *c = (unsigned char *)xmallocd0(4, "id3_sync32:c"); + + c[3] = (i & 0x7f); + c[2] = ((i & 0x80) >> 7) | (((i & 0x7f00) >> 8) << 1); + c[1] = ((i & 0x8000) >> 15) | (((i & 0x7f0000) >> 16) << 1); + c[0] = ((i & 0x800000) >> 23) | (((i & 0x7f000000) >> 24) << 1); + + return c; +} + +id3v2_frame* +id3_lookup_frame(id3v2_frame_list *list, const char *field, const int pos) +{ + int cur = 0; + + if(!list || !field) return NULL; + + do + { + if(!strcmp(list->data->frame_id, field)) + { + if(cur == pos) return list->data; + cur++; + } + } while((list = list->next)); + + return NULL; +} + +int +id3_remove_frame(id3v2_frame_list *list, id3v2_frame *frame) +{ + if(!list || !frame) return MP_EERROR; + + /* Frame is first element in list */ + if(list->data == frame) + { + xfree(list->data); + list->next->start = list->next; + xfree(list); + return 0; + } + + /* Iterate through others */ + do + { + if(list->next->data == frame) + { + list->next = list->next->next; + xfree(frame); + return 0; + } + } while((list = list->next)); + + return 1; +} + + +int +id3_add_frame(id3v2_frame_list *list, char *field, char *new_value, int len) +{ + id3v2_frame *frame; + char *new_valuecp; + long len_sync; + + if(!list || !new_value || !field || strlen(field) != 4) return MP_EERROR; + + // make a copy of the given value to include it in the frame + new_valuecp = xmallocd(len, + "id3_add_frame:new_valuecp"); + memcpy(new_valuecp, new_value, len); + new_value = new_valuecp; + + /* make sync safe */ +/* len_sync = id3_sync(new_value, len); */ + len_sync = len; /* disabled sync for MPIO use */ + + frame = XMALLOCD(id3v2_frame, "id3_add_frame:frame"); + frame->frame_id = xmallocd(4, "id3_add_frame:frame->frame_id"); + strncpy(frame->frame_id, field, 4); + frame->data = new_value; + frame->status_flag = 0; + + if(len != len_sync) frame->format_flag = 64; + else frame->format_flag = 0; + frame->data_size = len_sync; + + /* Empty list */ + if(!list->data) + { + list->data = frame; + return 0; + } + + /* iterate to last element */ + while(list->next) list = list->next; + + list->next = XMALLOCD(id3v2_frame_list, "id3_add_frame:list->next"); + list->next->start = list->start; + list = list->next; + list->next = NULL; + list->data = frame; + + return 0; +} + + +#define BUF_SIZE 4096 + +static int +id3_lseek_syncword(int fd) +{ + unsigned char *data = (unsigned char*) xmallocd(BUF_SIZE, "id3_lseek_syncword:data"); + int ret; + + // Reset the reading offset of the fd + lseek(fd, SEEK_SET, 0); + + if(read(fd, data, BUF_SIZE) < 1) + { + xfree(data); + return 0; /* return false on EOF */ + } + + ret = id3_lseek_syncword_r(fd, data, 0); + xfree(data); + return ret; +} + +static int +id3_lseek_syncword_r(int fd, unsigned char *data, int checked) +{ + unsigned char lastchar; + int i; + + for(i = 0; i + 1 < BUF_SIZE; i++) + { + if(((data[i] & 0xFF)== 0xFF) && ((data[i+1] & 0xE0)== 0xE0)) + { + lseek(fd, checked + i, SEEK_SET); + return 0; + } + } + + lastchar = data[BUF_SIZE - 1]; + + if(read(fd, data, BUF_SIZE) < 1) return 0; /* return false on EOF */ + + if(((lastchar & 0xFF)== 0xFF) && ((data[0] & 0xE0)== 0xE0)) + { + lseek(fd, checked + BUF_SIZE - 1, SEEK_SET); + return 0; + } + + return id3_lseek_syncword_r(fd, data, checked + BUF_SIZE); +} + +static id3_tag* +id3v1_alloc_tag(void) +{ + id3_tag *tag = XMALLOCD(id3_tag, "id3v1_alloc_tag:tag"); + id3v1_tag *v1 = XMALLOCD0(id3v1_tag, "id3v1_alloc_tag:v1"); + + tag->tag = v1; + tag->version = 1; + + v1->genre = 0xFF; + + return tag; +} + +static id3_tag* +id3v2_alloc_tag(void) +{ + id3_tag *tag = XMALLOCD(id3_tag, "id3v2_alloc_tag:tag"); + id3v2_tag *v2 = XMALLOCD0(id3v2_tag, "id3v2_alloc_tag:v2"); + + tag->tag = v2; + tag->version = 2; + + return tag; +} + +static void +id3v1_free_tag(id3v1_tag* v1) +{ + xfree(v1->artist); + xfree(v1->album); + xfree(v1->title); + xfree(v1->year); + xfree(v1->comment); + xfree(v1); +} + +static void +id3v2_free_tag(id3v2_tag* v2) +{ + id3v2_frame_list* doomed; + id3v2_frame *frame; + + if(!v2) return; + + xfree(v2->header->extended_header); + xfree(v2->header); + + if(!v2->frame_list) + { + xfree(v2); + return; + } + + /* Freeing frames */ + do + { + frame = (id3v2_frame*)v2->frame_list->data; + if(frame->frame_id) xfree(frame->frame_id); + if(frame->data) xfree(frame->data); + xfree(v2->frame_list->data); + + doomed = v2->frame_list->next; + xfree(v2->frame_list); + + } while((v2->frame_list = doomed)); + + xfree(v2); +} diff --git a/libmpio/mplib/mplib_s.h b/libmpio/mplib/mplib_s.h new file mode 100644 index 0000000..7803a5e --- /dev/null +++ b/libmpio/mplib/mplib_s.h @@ -0,0 +1,153 @@ +/* + * mplib - a library that enables you to edit ID3 tags + * Copyright (C) 2001,2002 Stefan Podkowinski + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "mplib.h" + +#ifndef __MPLIB_S_H +#define __MPLIB_S_H + +#undef __BEGIN_DECLS +#undef __END_DECLS +#ifdef __cplusplus +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS } +#else +# define __BEGIN_DECLS /* empty */ +# define __END_DECLS /* empty */ +#endif + + +__BEGIN_DECLS + + +/************************************/ +/* Members */ +/************************************/ + +/* Bitrates br_[version]_[layer] ) */ +const static int br_1_1[16] = { -1, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }; +const static int br_1_2[16] = { -1, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }; +const static int br_1_3[16] = { -1, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }; +const static int br_2_1[16] = { -1, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }; +const static int br_2_2[16] = { -1, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }; +const static int br_2_3[16] = { -1, 8, 16, 24, 32, 64, 80, 56, 64, 128, 160, 112, 128, 256, 320, 0 }; + +#define GLL 148 +const static char *genre_list[GLL] = +{ "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", + "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", + "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", + "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", + "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "Alternative Rock", "Bass", "Soul", "Punk", + "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", + "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", + "Cult", "Gangsta Rap", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", + "New Wave", "Psychedelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", + "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", + "National Folk", "Swing", "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", + "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", + "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", "Opera", + "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", + "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rythmic Soul", "Freestyle", + "Duet", "Punk Rock", "Drum Solo", "A Cappella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass", + "Club-House", "Hardcore", "Terror", "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat", + "Christian Gangsta Rap", "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian", + "Christian Rock", "Merengue", "Salsa", "Trash Metal", "Anime", "JPop", "Synthpop" }; + + +/************************************/ +/* Macros */ +/************************************/ + +#define frame_is_read_only(id3v2_frame__) ((id3v2_frame__->status_flag >> 4) & 1) +#define frame_preserve_on_tag_altered(id3v2_frame__) ((id3v2_frame__->status_flag >> 6) & 1) +#define frame_preserve_on_file_altered(id3v2_frame__) ((id3v2_frame__->status_flag >> 5) & 1) + +#define frame_has_group_information(id3v2_frame__) ((id3v2_frame__->format_flag >> 6) & 1) +#define frame_is_compressed(id3v2_frame__) ((id3v2_frame__->format_flag >> 3) & 1) +#define frame_is_encrypted(id3v2_frame__) ((id3v2_frame__->format_flag >> 2) & 1) +#define frame_is_unsynchronised(id3v2_frame__) ((id3v2_frame__->format_flag >> 1) & 1) +#define frame_has_data_length_indicator(id3v2_frame__) (id3v2_frame__->format_flag & 1) + + +/************************************/ +/* Static functions */ +/************************************/ + + +/* Gets the v1/v2 tag from a file */ +static id3v1_tag *id3v1_get_tag(int); +static id3v2_tag *id3v2_get_tag(int); + +/* Adds v1/v2 Tag to file */ +static int id3v1_add_tag(int, id3v1_tag*); +static int id3v2_add_tag(int, id3v2_tag*, id3v2_tag*); + +/* Removes v1/v2 Tag from file */ +static int id3v1_del_tag(int); +static int id3v2_del_tag(int, id3v2_tag*); + +/* Truncates the fields of the tag to the proper lengths */ +static int id3v1_truncate_tag(id3v1_tag*); + +/* Returns 1 or 0 whether arg 1 is just filled up with space (0x20) or not */ +static int id3_is_only_space(char*, int); + +/* Gets all frame names available in the tag */ +static char **id3v2_get_names(id3v2_tag*); + +/* Gets the content of the given field in the tag on the specified position. */ +static id3_content* id3v2_get_content_at_pos(id3v2_tag*, const char*, int); +static id3_content* id3v1_get_content(id3v1_tag*, int); + +/* Gets the number of available frames */ +static int id3_get_no_frames(id3v2_tag*); + +/* Returns string of the frames content */ +/* static char* extract_frame_data(char *, int); */ + +/* Removes a frame from the frame list */ +static int id3_remove_frame(id3v2_frame_list *, id3v2_frame *); + +/* Adds a new frame to the list */ +static int id3_add_frame(id3v2_frame_list *, char *, char *, int); + +/* Lookups a frame by his identifier on the given position */ +static id3v2_frame* id3_lookup_frame(id3v2_frame_list *, const char *, const int); + +/* Sync functions */ +static long id3_unsync(unsigned char*, long); +static long id3_sync(unsigned char*, long); +static unsigned int id3_unsync32(unsigned char*, int); +static unsigned char* id3_sync32(unsigned int); + +static int id3_lseek_syncword(int); +static int id3_lseek_syncword_r(int, unsigned char *, int); + +/* Gets a new allocated v1 or v2 tag */ +static id3_tag* id3v1_alloc_tag(void); +static id3_tag* id3v2_alloc_tag(void); + +/* free functions */ +static void id3v1_free_tag(id3v1_tag*); +static void id3v2_free_tag(id3v2_tag*); + + +__END_DECLS + +#endif /* __MPLIB_S_H */ diff --git a/libmpio/mplib/xmalloc.c b/libmpio/mplib/xmalloc.c new file mode 100644 index 0000000..a66be63 --- /dev/null +++ b/libmpio/mplib/xmalloc.c @@ -0,0 +1,167 @@ +/* + * mplib - a library that enables you to edit ID3 tags + * Copyright (C) 2001,2002 Stefan Podkowinski + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +/* define XMALLOC_CHECK 1 */ +#include "xmalloc.h" +#ifdef USE_GC +# include +# define malloc(str) GC_MALLOC(str) +#endif + +#ifdef XMALLOC_CHECK +typedef struct _xdelem { + void* alloced; + void* freed; + char* descrl; + struct _xdelem* next; +} xdelem; + +xdelem* first = NULL; +#endif + +#define errmsg "mplib: Memory exhausted: Could not allocate %d bytes\n" + +void * +xmalloc(size_t s) +{ + return xmallocd(s, NULL); +} + +void * +xmallocd(size_t s, char* descrl) +{ + void *new = (void*)malloc(s); +#ifdef XMALLOC_CHECK + xdelem* cur = (xdelem*)malloc(sizeof(xdelem)); + cur->next = NULL; + cur->freed = NULL; + cur->descrl = descrl; +#endif + if(!new) fprintf(stderr, errmsg, s); + +#ifdef XMALLOC_CHECK + cur->alloced = new; + exists: + if(!first) first = cur; + else { + xdelem* last = first; + do { + if(last->alloced == cur->alloced) { + last->freed = NULL; + last->descrl = descrl; + free(cur); + goto exists; + } + } while(last->next && (last = last->next)); + last->next = cur; + } +#endif + + return new; +} + +void * +xmallocd0(size_t s, char* descr) +{ +#ifdef XMALLOC_CHECK + void *new = (void*)xmallocd(s, descr); +#else + void *new = (void*)malloc(s); +#endif + if(!new) fprintf(stderr, errmsg, s); + else memset(new, 0, s); + return new; +} + +void * +xmalloc0(size_t s) +{ +#ifdef XMALLOC_CHECK + void *new = (void*)xmalloc(s); +#else + void *new = (void*)malloc(s); +#endif + if(!new) fprintf(stderr, errmsg, s); + else memset(new, 0, s); + return new; +} + +void * +xrealloc(void * ptr, size_t s) +{ + void *new; + + if(!ptr) return xmalloc(s); + + new = (void*)realloc(ptr, s); + if(!new) fprintf(stderr, errmsg, s); + return new; +} + +void +xfree(void* ptr) { + if(!ptr) return; +#ifdef XMALLOC_CHECK + if(first) { + xdelem* el = first; + do { + if(el->freed == ptr) { + if(el->descrl) + printf("XMALLOC: (%s) memory allready freed\n", el->descrl); + else + printf("XMALLOC: memory allready freed at %h\n", ptr); + break; + } + if(el->alloced == ptr) { + el->freed = ptr; + break; + } + } while(el->next && (el = el->next)); + } +#endif + free(ptr); +} + + +#ifdef XMALLOC_CHECK +void +xprint_malloc_stat(void) { + long kb_alloc = 0; + long kb_freed = 0; + long kb_used = 0; + int count_used = 0; + xdelem* el = first; + + if(!first) { + puts("XMALLOC: No statistic available"); + } + puts("xmalloc statistic:"); + do { + if(!el->freed) { + if(el->descrl && !strstr(el->descrl, "ignore")) + printf("%s (not freed)\n", el->descrl); + else if(!el->descrl) printf("%p (not freed)\n", el->alloced); + } else { + //if(el->descrl) printf("%s (freed)\n", el->descrl); + //else printf("%p (freed)\n", el->alloced); + } + } while(el->next && (el = el->next)); +} +#endif diff --git a/libmpio/mplib/xmalloc.h b/libmpio/mplib/xmalloc.h new file mode 100644 index 0000000..ecdf1b6 --- /dev/null +++ b/libmpio/mplib/xmalloc.h @@ -0,0 +1,58 @@ +/* + * mplib - a library that enables you to edit ID3 tags + * Copyright (C) 2001,2002 Stefan Podkowinski + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1. + * + * This library 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 + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __XMALLOC_H +#define __XMALLOC_H + +/* __BEGIN_DECLS should be used at the beginning of your declarations, + so that C++ compilers don't mangle their names. Use __END_DECLS at + the end of C declarations. */ +#undef __BEGIN_DECLS +#undef __END_DECLS +#ifdef __cplusplus +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS } +#else +# define __BEGIN_DECLS /* empty */ +# define __END_DECLS /* empty */ +#endif + + +#define XMALLOC(type) ((type *) xmalloc(sizeof(type))) +#define XMALLOCD(type,descr) ((type *) xmallocd(sizeof(type),descr)) +#define XMALLOC0(type) ((type *) xmalloc0(sizeof(type))) +#define XMALLOCD0(type,descr) ((type *) xmallocd0(sizeof(type),descr)) + +__BEGIN_DECLS + +/* define XMALLOC_CHECK 1 */ + +void *xmalloc(size_t); +void *xmallocd(size_t, char*); +void *xmalloc0(size_t); +void *xmallocd0(size_t, char*); +void *xrealloc(void *, size_t); +void *xcalloc(size_t, size_t); +void xfree(void*); +#ifdef XMALLOC_CHECK +void xprint_malloc_stat(void); +#endif + +__END_DECLS + +#endif /* __XMALLOC_H */ -- cgit v1.2.3