224 lines · 5.1 KB · C
RawDownload
/* * https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810005 * https://wiki.xiph.org/VorbisComment */#include "tagspriv.h" static const struct {	char *s;	int type;}t[] = {	{"album artist", Talbumartist}, // some legacy leftovers	{"album", Talbum},	{"albumartist", Talbumartist},	{"artist", Tartist},	{"comment", Tcomment},	{"composer", Tcomposer},	{"date", Tdate},	{"genre", Tgenre},	{"r128_album_gain", Talbumgain},	{"r128_track_gain", Ttrackgain},	{"replaygain_album_gain", Talbumgain},	{"replaygain_album_peak", Talbumpeak},	{"replaygain_track_gain", Ttrackgain},	{"replaygain_track_peak", Ttrackpeak},	{"title", Ttitle},	{"tracknumber", Ttrack},}; voidcbvorbiscomment(Tagctx *ctx, char *k, char *v){	int i; 	if(*v == 0)		return;	for(i = 0; i < nelem(t); i++){		if(strcasecmp(k, t[i].s) == 0){			txtcb(ctx, t[i].type, k, v);			break;		}	}	if(i == nelem(t))		txtcb(ctx, Tunknown, k, v);} static intmbpdec(void *buf, int *cnt){	int sz, n;	uint8_t *v; 	v = buf;	if((n = debase64(v, *cnt, v, *cnt)) <= 0)		return -1; 	/* skip id3v2 APIC type */	v += 4; n -= 4;	sz = beuint(v); /* mime size */	v += 4; n -= 4;	if(sz < 0 || sz >= n-4-4-4-4-4-4)		return -1;	v += sz; n -= sz; /* skip MIME */	sz = beuint(v); /* description size */	v += 4; n -= 4;	if(sz < 0 || sz >= n-4-4-4-4-4)		return -1;	v += sz; n -= sz; /* skip description */	v += 4+4+4+4; n -= 4+4+4+4; /* skip width, height, depth, palette info */	sz = beuint(v); /* picture size */	v += 4; n -= 4;	if(sz <= 0 || sz > n)		return -1;	memmove(buf, v, sz);	*cnt = sz; 	return 0;} intcbmbp(Tagctx *ctx, char *v, int ssz, int off, int picsz){	char *mime;	int type, n, sz; 	n = ssz; /* at most this amount is available */	n &= ~3; /* modulo 4 sextets, so debase64 gets complete bytes */	n = debase64((uint8_t*)v, n, (uint8_t*)ctx->buf, ctx->bufsz);	/* https://xiph.org/flac/format.html#metadata_block_picture */	if(n <= 4+4+0+4+0+4+4+4+4+4+0)		return 0;	v = ctx->buf;	type = beuint(v); /* id3v2 APIC type */	v += 4; n -= 4;	sz = beuint(v); /* mime size */	v += 4; n -= 4;	if(sz < 0 || sz >= n-4-4-4-4-4-4)		return -1;	mime = v;	v += sz; n -= sz; /* skip MIME */	sz = beuint(v); /* description size */	v += 4; n -= 4;	if(sz < 0 || sz >= n-4-4-4-4-4)		return -1;	*v = 0; /* null-terminate MIME */	tagscallcb(ctx, Timage, &(Tag){.image = {		.mime = mime,		.offset = off,		.size = picsz,		.type = type,		.decode = mbpdec,	}}); 	return 0;} inttagvorbis(Tagctx *ctx){	char *v;	uint8_t *d, h[4];	int sz, numtags, i, npages, pgend, skip; 	d = (uint8_t*)ctx->buf;	/* need to find vorbis frame with type=3 */	for(npages = pgend = 0; npages < 2; npages++){ /* vorbis comment is the second header */		int nsegs;		if(ctx->read(ctx, d, 27) != 27)			return -1;		if(memcmp(d, "OggS", 4) != 0)			return -1; 		/* calculate the size of the packet */		nsegs = d[26];		if(ctx->read(ctx, d, nsegs+1) != nsegs+1)			return -1;		for(sz = i = 0; i < nsegs; sz += d[i++]); 		if(d[nsegs] == 3){ /* comment */			/* FIXME - embedded pics make tags span multiple packets */			pgend = ctx->seek(ctx, 0, 1) + sz;			break;		}		if(d[nsegs] == 1 && sz >= 28){ /* identification */			if(ctx->read(ctx, d, 28) != 28)				return -1;			sz -= 28;			ctx->channels = d[10];			ctx->samplerate = leuint(&d[11]);			if((ctx->bitrate = leuint(&d[15])) == 0) /* maximum */				ctx->bitrate = leuint(&d[19]); /* nominal */		} 		ctx->seek(ctx, sz-1, 1);	} 	if(npages < 3) {		if(ctx->read(ctx, &d[1], 10) != 10 || memcmp(&d[1], "vorbis", 6) != 0)			return -1;		sz = leuint(&d[7]);		if(ctx->seek(ctx, sz, 1) < 0 || ctx->read(ctx, h, 4) != 4)			return -1;		numtags = leuint(h); 		for(i = 0; i < numtags; i++){			if(ctx->read(ctx, h, 4) != 4)				return -1;			if((sz = leuint(h)) < 0)				return -1;			/* FIXME - embedded pics make tags span multiple packets */			if(pgend < ctx->seek(ctx, 0, 1)+sz)				break; 			skip = 0;			if(sz > ctx->bufsz-1){				skip = sz - (ctx->bufsz-1);				sz -= skip;			} 			if(ctx->read(ctx, ctx->buf, sz) != sz)				return -1;			ctx->buf[sz] = 0; 			if((v = strchr(ctx->buf, '=')) == nil)				return -1;			*v++ = 0;			if(strcasecmp(ctx->buf, "metadata_block_picture") != 0)				cbvorbiscomment(ctx, ctx->buf, v);			else if(cbmbp(ctx, v,					sz - (v - ctx->buf), /* at most this amount is available */					ctx->seek(ctx, 0, 1) - sz + (v - ctx->buf), /* offset */					sz + skip - (v - ctx->buf) /* total pic size (still encoded) */					) != 0)				return -1; 			if(ctx->seek(ctx, skip, 1) < 0)				return -1;		}	} 	/* calculate the duration */	if(ctx->samplerate > 0){		sz = ctx->bufsz <= 4096 ? ctx->bufsz : 4096;		for(i = sz; i < 65536+16; i += sz - 16){			if(ctx->seek(ctx, -i, 2) <= 0)				break;			v = ctx->buf;			if(ctx->read(ctx, v, sz) != sz)				break;			for(; v != nil && v < ctx->buf+sz;){				v = memchr(v, 'O', ctx->buf+sz - v - 14);				if(v != nil && v[1] == 'g' && v[2] == 'g' && v[3] == 'S' && (v[5] & 4) == 4){ /* last page */					uint64_t g = leuint(v+6) | (uint64_t)leuint(v+10)<<32;					ctx->duration = g * 1000 / ctx->samplerate;				}				if(v != nil)					v++;			}			if(ctx->duration != 0)				break;		}	} 	return 0;}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t