quakeforge/tools/wav/qfwavinfo.c
Bill Currie 7052a6f20b work toward making the riff reader more generally useful by pluggin all the
memory leaks. next step is to get it so the data chunk isn't actually read
but rather its location and size stored.
2003-04-10 05:20:59 +00:00

509 lines
10 KiB
C

#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
void *alloca(size_t size);
#endif
#include "QF/dstring.h"
typedef struct d_chunk_s {
unsigned char name[4];
unsigned len;
} d_chunk_t;
typedef struct d_cue_point_s {
unsigned name;
unsigned position;
char chunk[4];
unsigned chunk_start;
unsigned block_start;
unsigned sample_offset;
} d_cue_point_t;
typedef struct d_cue_s {
unsigned count;
d_cue_point_t cue_points[1];
} d_cue_t;
typedef struct d_format_s {
unsigned short format_tag;
unsigned short channels;
unsigned samples_pre_sec;
unsigned byte_per_sec;
unsigned short align;
} d_format_t;
typedef struct d_ltxt_s {
unsigned name;
unsigned len;
char purpose[4];
unsigned country;
unsigned language;
unsigned dialect;
unsigned codepage;
unsigned char data[0];
} d_ltxt_t;
typedef struct cue_s {
d_chunk_t ck;
d_cue_t *cue;
} cue_t;
typedef struct format_s {
d_chunk_t ck;
d_format_t format;
char fdata[0];
} format_t;
typedef struct ltxt_s {
d_chunk_t ck;
d_ltxt_t ltxt;
} ltxt_t;
typedef struct label_s {
d_chunk_t ck;
unsigned ofs;
char *label;
} label_t;
typedef struct data_s {
d_chunk_t ck;
char *data;
} data_t;
typedef struct list_s {
d_chunk_t ck;
char name[4];
d_chunk_t *chunks[0];
} list_t;
#define SWITCH(name) switch (((name)[0] << 24) | ((name)[1] << 16) \
| ((name)[2] << 8) | (name)[3])
#define CASE(a,b,c,d) (((unsigned char)(a) << 24) \
| ((unsigned char)(b) << 16) \
| ((unsigned char)(c) << 8) \
| (unsigned char)(d))
static char *
read_string (FILE *f, int len)
{
char c[2] = {0, 0};
char *s;
int l = len;
dstring_t *str = dstring_newstr ();
while (l--) {
fread (c, 1, 1, f);
if (!c[0])
break;
dstring_appendstr (str, c);
}
s = str->str;
free (str);
fseek (f, l + (len & 1), SEEK_CUR);
return s;
}
static void *
read_data (FILE *f, int len)
{
void *data = malloc (len);
fread (data, 1, len, f);
fseek (f, len & 1, SEEK_CUR);
return data;
}
static void
read_ltxt (FILE *f, int len, d_ltxt_t *ltxt)
{
fread (ltxt, 1, len, f);
}
static void
read_adtl (dstring_t *list_buf, FILE *f, int len)
{
d_chunk_t ck, *chunk = 0;
ltxt_t *ltxt;
label_t *label;
data_t *data;
list_t *list;
int r;
list = (list_t *) list_buf->str;
while (len) {
r = fread (&ck, 1, sizeof (ck), f);
if (!r) {
len = 0;
break;
}
len -= r;
SWITCH (ck.name) {
case CASE ('l','t','x','t'):
ltxt = malloc (sizeof (ltxt_t));
ltxt->ck = ck;
read_ltxt (f, ck.len, &ltxt->ltxt);
chunk = &ltxt->ck;
break;
case CASE ('l','a','b','l'):
case CASE ('n','o','t','e'):
label = malloc (sizeof (label_t));
label->ck = ck;
fread (&label->ofs, 1, 4, f);
label->label = read_string (f, ck.len - 4);
chunk = &label->ck;
break;
default:
data = malloc (sizeof (data));
data->ck = ck;
data->data = read_data (f, ck.len);
chunk = &data->ck;
break;
}
len -= ck.len + (ck.len & 1);
dstring_append (list_buf, (char *)&chunk, sizeof (chunk));
list = (list_t *) list_buf->str;
chunk = 0;
}
dstring_append (list_buf, (char *)&chunk, sizeof (chunk));
list = (list_t *) list_buf->str;
}
static list_t *
read_list (d_chunk_t *ck, FILE *f, int len)
{
d_chunk_t *chunk = 0;
dstring_t *list_buf;
list_t *list;
int r;
list_buf = dstring_new ();
list_buf->size = sizeof (list_t);
dstring_adjust (list_buf);
list = (list_t *)list_buf->str;
list->ck = *ck;
len -= fread (list->name, 1, sizeof (list->name), f);
while (len) {
SWITCH (list->name) {
case CASE ('I','N','F','O'):
{
data_t *data = malloc (sizeof (data_t));
chunk = &data->ck;
r = fread (&data->ck, 1, sizeof (data->ck), f);
if (!r) {
len = 0;
break;
}
len -= r;
SWITCH (data->ck.name) {
case CASE ('I','C','R','D'):
case CASE ('I','S','F','T'):
data->data = read_string (f, data->ck.len);
break;
default:
data->data = read_data (f, data->ck.len);
break;
}
len -= data->ck.len + (data->ck.len & 1);
}
break;
case CASE ('a','d','t','l'):
read_adtl (list_buf, f, len);
len = 0;
break;
default:
{
data_t *data = malloc (sizeof (data_t));
fread (&data->ck, 1, sizeof (data->ck), f);
data->data = read_data (f, data->ck.len);
len -= data->ck.len + sizeof (data->ck);
chunk = &data->ck;
}
len = 0;
break;
}
if (chunk) {
dstring_append (list_buf, (char *)&chunk, sizeof (chunk));
list = (list_t *) list_buf->str;
}
chunk = 0;
}
dstring_append (list_buf, (char *)&chunk, sizeof (chunk));
list = (list_t *) list_buf->str;
free (list_buf);
return list;
}
static d_cue_t *
read_cue (FILE *f, int len)
{
d_cue_t *cue = malloc (len);
fread (cue, 1, len, f);
return cue;
}
static list_t *
riff_read (const char *filename)
{
dstring_t *riff_buf;
list_t *riff = 0;
d_chunk_t *chunk = 0;
FILE *f = fopen (filename, "rb");
d_chunk_t ck;
int file_len, len;
if (f) {
riff_buf = dstring_new ();
riff_buf->size = sizeof (list_t);
dstring_adjust (riff_buf);
riff = (list_t *)riff_buf->str;
fseek (f, 0, SEEK_END);
file_len = ftell (f);
fseek (f, 0, SEEK_SET);
fread (&riff->ck, 1, sizeof (riff->ck), f);
fread (riff->name, 1, sizeof (riff->name), f);
while (fread (&ck, 1, sizeof (ck), f)) {
if (ck.len < 0x80000000)
len = ck.len;
else
len = file_len - ftell (f);
SWITCH (ck.name) {
case CASE ('c','u','e',' '):
{
cue_t *cue = malloc (sizeof (cue_t));
cue->ck = ck;
cue->cue = read_cue (f, len);
chunk = &cue->ck;
}
break;
case CASE ('L','I','S','T'):
{
list_t *list;
list = read_list (&ck, f, len);
chunk = &list->ck;
}
break;
default:
{
data_t *data = malloc (sizeof (data_t));
data->ck = ck;
data->data = read_data (f, len);
chunk = &data->ck;
}
break;
}
dstring_append (riff_buf, (char *)&chunk, sizeof (chunk));
riff = (list_t *) riff_buf->str;
chunk = 0;
}
dstring_append (riff_buf, (char *)&chunk, sizeof (chunk));
riff = (list_t *) riff_buf->str;
fclose (f);
free (riff_buf);
}
return riff;
}
static void
free_adtl (d_chunk_t *adtl)
{
ltxt_t *ltxt;
label_t *label;
data_t *data;
//printf (" %.4s\n", adtl->name);
SWITCH (adtl->name) {
case CASE ('l','t','x','t'):
ltxt = (ltxt_t *) adtl;
/*printf (" %d %d %4s %d %d %d %d\n",
ltxt->ltxt.name,
ltxt->ltxt.len,
ltxt->ltxt.purpose,
ltxt->ltxt.country,
ltxt->ltxt.language,
ltxt->ltxt.dialect,
ltxt->ltxt.codepage);*/
free (ltxt);
break;
case CASE ('l','a','b','l'):
case CASE ('n','o','t','e'):
label = (label_t *) adtl;
//printf (" %-8d %s\n", label->ofs, label->label);
free (label->label);
free (label);
break;
default:
data = (data_t *) adtl;
free (data->data);
free (data);
break;
}
}
static void
free_list (list_t *list)
{
d_chunk_t **ck;
data_t *data;
//printf (" %.4s\n", list->name);
for (ck = list->chunks; *ck; ck++) {
SWITCH (list->name) {
case CASE ('I','N','F','O'):
data = (data_t *) *ck;
//printf (" %.4s\n", data->ck.name);
SWITCH (data->ck.name) {
case CASE ('I','C','R','D'):
case CASE ('I','S','F','T'):
//printf (" %s\n", data->data);
default:
free (data->data);
free (data);
break;
}
break;
case CASE ('a','d','t','l'):
free_adtl (*ck);
break;
default:
data = (data_t *) *ck;
free (data->data);
free (data);
break;
}
}
free (list);
}
static void
riff_free (list_t *riff)
{
d_chunk_t **ck;
cue_t *cue;
list_t *list;
data_t *data;
for (ck = riff->chunks; *ck; ck++) {
//printf ("%.4s\n", (*ck)->name);
SWITCH ((*ck)->name) {
case CASE ('c','u','e',' '):
cue = (cue_t *) *ck;
/*{
int i;
for (i = 0; i < cue->cue->count; i++) {
printf (" %08x %d %.4s %d %d %d\n",
cue->cue->cue_points[i].name,
cue->cue->cue_points[i].position,
cue->cue->cue_points[i].chunk,
cue->cue->cue_points[i].chunk_start,
cue->cue->cue_points[i].block_start,
cue->cue->cue_points[i].sample_offset);
}
}*/
free (cue->cue);
free (cue);
break;
case CASE ('L','I','S','T'):
list = (list_t *) *ck;
free_list (list);
break;
default:
data = (data_t *) *ck;
free (data->data);
free (data);
break;
}
}
free (riff);
}
int
main (int argc, char **argv)
{
while (*++argv) {
list_t *riff = riff_read (*argv);
d_chunk_t **ck;
int sample_start, sample_count;
if (!riff) {
fprintf (stderr, "couldn't read %s\n", *argv);
continue;
}
sample_start = -1;
sample_count = -1;
for (ck = riff->chunks; *ck; ck++) {
SWITCH ((*ck)->name) {
case CASE ('c', 'u', 'e', ' '):
{
cue_t *cue = (cue_t *)*ck;
d_cue_t *dcue = cue->cue;
d_cue_point_t *cp = dcue->cue_points;
int i;
for (i = 0; i < dcue->count; i++)
sample_start = cp[i].sample_offset;
#if 0
printf ("cuepoint: %d %d %.4s %d %d %d\n",
cp[i].name,
cp[i].position,
cp[i].chunk,
cp[i].chunk_start,
cp[i].block_start,
cp[i].sample_offset);
#endif
}
break;
case CASE ('L','I','S','T'):
{
list_t *list = (list_t *)*ck;
SWITCH (list->name) {
case CASE ('a','d','t','l'):
{
d_chunk_t **ck;
for (ck = list->chunks; *ck; ck++) {
SWITCH ((*ck)->name) {
case CASE ('l', 't', 'x', 't'):
{
ltxt_t *ltxt = (ltxt_t *)*ck;
d_ltxt_t *dltxt = &ltxt->ltxt;
sample_count = dltxt->len;
#if 0
printf ("ltxt: %d %d %4s %d %d %d %d\n",
dltxt->name,
dltxt->len,
dltxt->purpose,
dltxt->country,
dltxt->language,
dltxt->dialect,
dltxt->codepage);
#endif
}
break;
}
}
}
break;
}
}
break;
case CASE ('d','a','t','a'):
#if 0
printf ("data: %d\n", (*ck)->len);
#endif
sample_count = (*ck)->len;
break;
}
}
if (sample_start!= -1)
printf ("CUEPOINT=%d %d\n", sample_start, sample_count);
riff_free (riff);
}
return 0;
}