2
* arch-tag: implementation of reading id3 tags over GnomeVFS
3
* Copyright (C) 2002 Jorn Baayen <jorn@nl.linux.org>
4
* Marco Pesenti Gritti <marco@it.gnome.org>
5
* Bastien Nocera <hadess@hadess.net>
6
* based upon file.c in libid3tag by Robert Leslie
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation; either version 2 of the License, or
11
* (at your option) any later version.
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with this program; if not, write to the Free Software
20
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
46
# include "mp3bitrate.h"
50
unsigned long location;
55
GnomeVFSHandle *iofile;
56
enum id3_vfs_mode mode;
59
struct id3_tag *primary;
65
ID3_FILE_FLAG_ID3V1 = 0x0001
70
* DESCRIPTION: check for a tag at a file's current position
73
signed long query_tag(GnomeVFSHandle *iofile)
75
GnomeVFSFileSize save_position, bytes_read = -1;
76
id3_byte_t query[ID3_TAG_QUERYSIZE];
79
if (gnome_vfs_tell(iofile, &save_position) != GNOME_VFS_OK)
82
gnome_vfs_read(iofile, query, sizeof(query), &bytes_read);
83
size = id3_tag_query(query, bytes_read);
85
if (gnome_vfs_seek(iofile, GNOME_VFS_SEEK_START, save_position)
94
* DESCRIPTION: read and parse a tag at a file's current position
97
struct id3_tag *read_tag(GnomeVFSHandle *iofile, id3_length_t size)
99
GnomeVFSFileSize bytes_read = -1;
102
struct id3_tag *tag = 0;
106
res = gnome_vfs_read (iofile, data, size, &bytes_read);
107
if (res == GNOME_VFS_OK)
109
tag = id3_tag_parse(data, bytes_read);
119
* NAME: update_primary()
120
* DESCRIPTION: update the primary tag with data from a new tag
123
int update_primary(struct id3_tag *tag, struct id3_tag const *new)
127
if (!(new->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGISANUPDATE))
128
id3_tag_clearframes(tag);
130
for (i = 0; i < new->nframes; ++i) {
131
if (id3_tag_attachframe(tag, new->frames[i]) == -1)
140
* DESCRIPTION: read, parse, and add a tag to a file structure
143
int add_tag(struct id3_vfs_file *file, id3_length_t length)
145
GnomeVFSFileSize location = -1;
147
struct vfstag filetag, *tags;
150
if (gnome_vfs_tell(file->iofile, &location) != GNOME_VFS_OK)
153
/* check for duplication/overlap */
155
unsigned long begin1, end1, begin2, end2;
158
end1 = begin1 + length;
160
for (i = 0; i < file->ntags; ++i) {
161
begin2 = file->tags[i].location;
162
end2 = begin2 + file->tags[i].length;
164
if (begin1 == begin2 && end1 == end2)
165
return 0; /* duplicate */
167
if (begin1 < end2 && end1 > begin2)
168
return -1; /* overlap */
172
tags = realloc(file->tags, (file->ntags + 1) * sizeof(*tags));
178
tag = read_tag(file->iofile, length);
182
if (update_primary(file->primary, tag) == -1) {
188
filetag.location = location;
189
filetag.length = length;
191
file->tags[file->ntags++] = filetag;
199
* NAME: search_tags()
200
* DESCRIPTION: search for tags in a file
203
int search_tags(struct id3_vfs_file *file)
205
GnomeVFSFileSize save_position = -1;
209
if (gnome_vfs_tell(file->iofile, &save_position) != GNOME_VFS_OK)
212
/* look for an ID3v1 tag */
214
if (gnome_vfs_seek(file->iofile, GNOME_VFS_SEEK_END,
215
(GnomeVFSFileOffset)-128) == GNOME_VFS_OK) {
216
size = query_tag(file->iofile);
218
if (add_tag(file, size) == -1)
221
file->flags |= ID3_FILE_FLAG_ID3V1;
222
file->options |= ID3_FILE_OPTION_ID3V1;
226
/* look for a tag at the beginning of the file */
228
gnome_vfs_seek(file->iofile, GNOME_VFS_SEEK_START, 0);
230
size = query_tag(file->iofile);
232
struct id3_frame const *frame;
234
if (add_tag(file, size) == -1)
237
/* locate tags indicated by SEEK frames */
240
id3_tag_findframe(file->tags[file->ntags - 1].tag, "SEEK", 0))) {
243
seek = id3_field_getint(&frame->fields[0]);
244
if (seek < 0 || gnome_vfs_seek
245
(file->iofile, GNOME_VFS_SEEK_CURRENT,
246
(GnomeVFSFileOffset)seek) != GNOME_VFS_OK)
249
size = query_tag(file->iofile);
252
else if (add_tag(file, size) == -1)
257
/* look for a tag at the end of the file (before any ID3v1 tag) */
259
if (gnome_vfs_seek(file->iofile, GNOME_VFS_SEEK_END,
260
((file->flags & ID3_FILE_FLAG_ID3V1) ? -128 : 0) + -10) == GNOME_VFS_OK) {
261
size = query_tag(file->iofile);
262
if (size < 0 && gnome_vfs_seek
263
(file->iofile, GNOME_VFS_SEEK_CURRENT, (GnomeVFSFileOffset)size)
265
size = query_tag(file->iofile);
266
if (size > 0 && add_tag(file, size) == -1)
273
if (file->flags & ID3_FILE_FLAG_ID3V1)
279
if (gnome_vfs_tell(file->iofile, &save_position) != GNOME_VFS_OK)
286
* NAME: finish_file()
287
* DESCRIPTION: release memory associated with a file
290
void finish_file(struct id3_vfs_file *file)
295
id3_tag_delref(file->primary);
296
id3_tag_delete(file->primary);
299
for (i = 0; i < file->ntags; ++i) {
300
id3_tag_delref(file->tags[i].tag);
301
id3_tag_delete(file->tags[i].tag);
312
* DESCRIPTION: create a new file structure and load tags
315
struct id3_vfs_file *new_file(GnomeVFSHandle *iofile, enum id3_vfs_mode mode)
317
struct id3_vfs_file *file;
319
file = malloc(sizeof(*file));
323
file->iofile = iofile;
331
file->primary = id3_tag_new();
332
if (file->primary == 0)
335
id3_tag_addref(file->primary);
337
/* load tags from the file */
339
if (search_tags(file) == -1)
355
* DESCRIPTION: open a file given its pathname
357
struct id3_vfs_file *id3_vfs_open(char const *path, enum id3_vfs_mode mode)
359
GnomeVFSHandle *iofile = NULL;
360
struct id3_vfs_file *file;
362
/* Try opening the file */
363
if (gnome_vfs_open(&iofile, path,
364
(mode == ID3_VFS_MODE_READWRITE) ?
365
GNOME_VFS_OPEN_READ&GNOME_VFS_OPEN_WRITE : GNOME_VFS_OPEN_READ)
369
/* Try the different SEEK types that might fail */
370
if ((gnome_vfs_seek(iofile, GNOME_VFS_SEEK_END, 0) != GNOME_VFS_OK)
371
|| gnome_vfs_seek(iofile, GNOME_VFS_SEEK_START, 0) != GNOME_VFS_OK)
373
gnome_vfs_close (iofile);
377
file = new_file(iofile, mode);
379
gnome_vfs_close(iofile);
385
* NAME: file->fdopen()
386
* DESCRIPTION: open a file using an existing file descriptor
388
* Unsupported with the vfs interface
392
* NAME: file->close()
393
* DESCRIPTION: close a file and delete its associated tags
395
void id3_vfs_close(struct id3_vfs_file *file)
397
gnome_vfs_close(file->iofile);
404
* DESCRIPTION: return the primary tag structure for a file
406
struct id3_tag *id3_vfs_tag(struct id3_vfs_file const *file)
408
return file->primary;
412
* NAME: file->update()
413
* DESCRIPTION: rewrite tag(s) to a file
415
int id3_vfs_update(struct id3_vfs_file *file)
418
unsigned char id3v1_data[128], *id3v1 = 0, *id3v2 = 0;
420
if (file->mode != ID3_VFS_MODE_READWRITE)
423
if (file->options & ID3_FILE_OPTION_ID3V1) {
424
file->primary->options |= ID3_TAG_OPTION_ID3V1;
426
size = id3_tag_render(file->primary, 0);
428
assert(size == sizeof(id3v1_data));
430
size = id3_tag_render(file->primary, id3v1_data);
432
assert(size == sizeof(id3v1_data));
438
file->primary->options &= ~ID3_TAG_OPTION_ID3V1;
440
size = id3_tag_render(file->primary, 0);
442
id3v2 = malloc(size);
446
size = id3_tag_render(file->primary, id3v2);
462
id3_vfs_is_wave (guchar *buffer)
464
if (buffer[8] != 'W')
466
if (buffer[9] != 'A')
468
if (buffer[10] != 'V')
470
if (buffer[11] != 'E' && buffer[11] != ' ')
477
id3_vfs_bitrate (struct id3_vfs_file *file, int *bitrate, int *samplerate,
478
int *time, int *version, int *vbr, int *channels)
480
GnomeVFSFileSize save_position, length_read;
481
GnomeVFSHandle *iofile = file->iofile;
483
guchar buffer[16384];
484
int is_wave, found, i;
494
if (gnome_vfs_tell(iofile, &save_position) != GNOME_VFS_OK)
497
gnome_vfs_seek (iofile, GNOME_VFS_SEEK_START, 0);
499
res = gnome_vfs_read (iofile, buffer, sizeof (buffer), &length_read);
500
if( res != GNOME_VFS_OK || length_read < 512 )
503
/* Reduce false positive by castrating the search if we have a WAVE file */
504
is_wave = id3_vfs_is_wave (buffer);
508
for (i = 0; i + 4 < length_read; i++)
510
if (mp3_bitrate_parse_header (buffer+i, length_read - i, bitrate, samplerate, time, version, vbr, channels))
517
/* If we haven't found anything, try again with 8 more kB */
518
if (is_wave == 0 && found == 0)
520
res = gnome_vfs_read (iofile, buffer, sizeof (buffer), &length_read);
522
if( res != GNOME_VFS_OK || length_read < 512 )
525
for (i = 0; i + 4 < length_read; i++)
527
if (mp3_bitrate_parse_header (buffer+i, length_read - i, bitrate, samplerate, time, version, vbr, channels))
536
if (gnome_vfs_seek(iofile, GNOME_VFS_SEEK_START, save_position) != GNOME_VFS_OK)