shithub: qk1

ref: 9510b4cae3b95ddea506206756b725a9c5c11148
dir: /wav.c/

View raw version
#include "quakedef.h"

int
wavinfo(byte *in, int len, wavinfo_t *info)
{
	int i, n, sz, fmt, loopsamples;
	byte *p;

	memset(info, 0, sizeof(*info));

	p = in;
	if(len < 36 || memcmp(p, "RIFF", 4) != 0){
		werrstr("not a RIFF");
		return -1;
	}
	p += 4; len -= 4;
	sz = le32(p); len -= 4;
	if(sz < 36 || sz > len){
		werrstr("trucated? sz=%d len=%d", sz, len);
		return -1;
	}
	if(memcmp(p, "WAVE", 4) != 0){
		werrstr("not a WAVE");
		return -1;
	}
	p += 4; sz -= 4;
	loopsamples = 0;
	info->loopofs = -1;

	for(; sz >= 8;){
		p += 4; sz -= 4;
		n = le32(p); sz -= 4;
		if(n > sz || n <= 0)
			break;
		if(n < 4)
			goto skip;
		if(memcmp(p-8, "fmt ", 4) == 0){
			fmt = le16(p); sz -= 2; n -= 2;
			if(fmt != 1){
				werrstr("not Microsoft PCM format: %d", fmt);
				return -1;
			}
			info->channels = le16(p); sz -= 2; n -= 2;
			info->rate = le32(p); sz -= 4; n -= 4;
			p += 4+2; sz -= 4+2; n -= 4+2; /* skip ByteRate + BlockAlign */
			info->width = le16(p); sz -= 2; n -= 2;
			if(info->width < 1 || (info->width % 8) != 0){
				werrstr("invalid width: %d", info->width);
				return -1;
			}
			info->width /= 8;
		}else if(memcmp(p-8, "cue ", 4) == 0){
			i = le32(p); sz -= 4; n -= 4; /* CuePoints - usually two of those */
			if(i >= 1 && n >= i*6*4){
				p += 5*4; sz -= 5*4; n -= 5*4; /* Name+Position+Chunk+ChunkStart+BlockStart */
				info->loopofs = le32(p); sz -= 4; n -= 4;
				/* FIXME(sigrid): check if this is needed and whether it works at all */
				if(i > 1){
					if(info->width < 1){
						werrstr("cue chunk before fmt");
						return -1;
					}
					p += 5*4; sz -= 5*4; n -= 5*4; /* Name+Position+Chunk+ChunkStart+BlockStart */
					loopsamples = info->loopofs + le32(p); sz -= 4; n -= 4;
					if(loopsamples > info->samples) /* eg "nehahra/sounds/vondur/fan1.wav" */
						loopsamples = info->samples;
				}
			}
		}else if(memcmp(p-8, "data", 4) == 0 && info->dataofs == 0){
			info->samples = n / info->width;
			info->dataofs = p - in;
		}else if(memcmp(p-8, "LIST", 4) == 0 && info->loopofs >= 0 && loopsamples == 0){
			p += 4; sz -= 4; n -= 4; /* skip "adtl" */
			if(n >= 20 && memcmp(p, "ltxt", 4) == 0 && memcmp(p+16, "mark", 4) == 0){
				p += 12; sz -= 12; n -= 12;
				loopsamples = info->loopofs + le32(p); sz -= 4; n -= 4;
			}
		}
skip:
		n = (n + 1) & ~1;
		p += n; sz -= n;
	}

	if(info->width < 1 || info->channels < 1 || info->rate < 1){
		werrstr("invalid/missing fmt chunk?");
		return -1;
	}
	if(loopsamples > info->samples || info->loopofs > info->samples){
		werrstr("invalid loop: (%d,%d) > %d", info->loopofs, loopsamples, info->samples);
		return -1;
	}
	if(loopsamples > 0)
		info->samples = loopsamples;

	return 0;
}