ref: 4a7f42e378e27bf70c928b411ac859c870c23f24
dir: /flac.c/
/* https://xiph.org/flac/format.html */ #include "tagspriv.h" #define beu3(d) ((d)[0]<<16 | (d)[1]<<8 | (d)[2]<<0) int tagflac(Tagctx *ctx) { int sz, last, type, seektbloff, seektblsz, off; uint8_t *d; uint64_t g; d = (uint8_t*)ctx->buf; /* 8 bytes for marker, block type, length. 18 bytes for the stream info */ if(ctx->read(ctx, d, 8+18) != 8+18 || memcmp(d, "fLaC\x00", 5) != 0) return -1; sz = beu3(&d[5]); /* size of the stream info */ ctx->samplerate = beu3(&d[18]) >> 4; ctx->channels = ((d[20]>>1) & 7) + 1; if(ctx->samplerate < 1 || ctx->channels < 1) return -1; g = (uint64_t)(d[21] & 0xf)<<32 | beu3(&d[22])<<8 | d[25]; ctx->duration = g * 1000 / ctx->samplerate; /* skip the rest of the stream info */ if(ctx->seek(ctx, sz-18, 1) != ctx->restart+8+sz) return -1; for(last = 0; !last;){ if(ctx->read(ctx, d, 4) != 4) return -1; if((sz = beu3(&d[1])) < 0) return -1; if((d[0] & 0x80) != 0) last = 1; if((d[0] & 0x7f) == 3 && ctx->toc != nil){ /* 3 = seek table */ seektbloff = ctx->seek(ctx, 0, 1); seektblsz = sz; if(ctx->seek(ctx, sz, 1) <= 0) return -1; }else if((d[0] & 0x7f) == 6){ /* 6 = picture */ int n, offset; char *mime; if(sz < 8+4+20 || ctx->read(ctx, d, 8) != 8) /* type, mime length */ return -1; sz -= 8; type = beuint(d); /* type */ n = beuint(&d[4]); /* mime length */ mime = ctx->buf+20; if(n < 0 || n >= sz-4-20 || n >= ctx->bufsz-20 || ctx->read(ctx, mime, n) != n) return -1; sz -= n; mime[n] = 0; ctx->read(ctx, d, 4); /* description */ sz -= 4; offset = beuint(d) + ctx->seek(ctx, 0, 1) + 20; ctx->read(ctx, d, 20); sz -= 20; if((n = beuint(&d[16])) < 0) return -1; if(n > 0){ tagscallcb(ctx, Timage, &(Tag){.image = { .mime = mime, .offset = offset, .size = n, .type = type, }}); } if(ctx->seek(ctx, sz, 1) <= 0) return -1; }else if((d[0] & 0x7f) == 4){ /* 4 = vorbis comment */ int i, numtags, tagsz, vensz; char *k, *v; if(sz < 12 || ctx->read(ctx, d, 4) != 4) return -1; sz -= 4; vensz = leuint(d); if(vensz < 0 || vensz > sz-4) return -1; /* skip vendor, read the number of tags */ if(ctx->seek(ctx, vensz, 1) < 0 || ctx->read(ctx, d, 4) != 4) return -1; sz -= vensz + 4; numtags = leuint(d); for(i = 0; i < numtags && sz > 4; i++){ if(ctx->read(ctx, d, 4) != 4) return -1; tagsz = leuint(d); sz -= 4; if(tagsz < 0 || tagsz > sz) return -1; /* if it doesn't fit, ignore it */ if(tagsz == 0 || tagsz >= ctx->bufsz){ if(ctx->seek(ctx, tagsz, 1) < 0) return -1; continue; } k = ctx->buf; if(ctx->read(ctx, k, tagsz) != tagsz) return -1; /* some tags have a stupid '\r'; ignore */ if(k[tagsz-1] == '\r') k[tagsz-1] = 0; k[tagsz] = 0; if((v = strchr(k, '=')) != nil){ *v++ = 0; cbvorbiscomment(ctx, k, v); } } }else if(ctx->seek(ctx, sz, 1) <= 0) return -1; } if(ctx->toc == nil) return 0; off = ctx->seek(ctx, 0, 1); if(seektbloff <= 0 || off <= seektbloff || ctx->seek(ctx, seektbloff, 0) != seektbloff) return 0; for(; seektblsz >= 18; seektblsz -= 18){ if(ctx->read(ctx, d, 18) != 18) break; /* sample offset */ g = (uint64_t)beuint(d+0)<<32 | beuint(d+4); if(g == ~0ULL) /* placeholder */ break; g = g * 1000 / ctx->samplerate; if(g > INT_MAX) break; int ms = g; /* frame offset */ g = off + ((uint64_t)beuint(d+8)<<32 | beuint(d+12)); if(g > INT_MAX) break; ctx->toc(ctx, ms, g); } return 0; }