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_s.c | 1177 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1177 insertions(+) create mode 100644 libmpio/mplib/mplib_s.c (limited to 'libmpio/mplib/mplib_s.c') 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); +} -- cgit v1.2.3