summaryrefslogtreecommitdiff
path: root/plugins/dumb/dumb-kode54/src/helpers/riff.c
blob: 62a7eccc683a3d079a91110511174d39a2d336db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include "internal/riff.h"

#include <stdlib.h>
#include <string.h>

struct riff * riff_parse( unsigned char * ptr, unsigned size, unsigned proper )
{
	unsigned stream_size;
	struct riff * stream;

	if ( size < 8 ) return 0;

	if ( ptr[0] != 'R' || ptr[1] != 'I' || ptr[2] != 'F' || ptr[3] != 'F' ) return 0;

	stream_size = ptr[4] | ( ptr[5] << 8 ) | ( ptr[6] << 16 ) | ( ptr[7] << 24 );
	if ( stream_size + 8 > size ) return 0;
	if ( stream_size < 4 ) return 0;

	stream = malloc( sizeof( struct riff ) );
	if ( ! stream ) return 0;

	stream->type = ( ptr[8] << 24 ) | ( ptr[9] << 16 ) | ( ptr[10] << 8 ) | ptr[11];
	stream->chunk_count = 0;
	stream->chunks = 0;

	ptr += 12;
	stream_size -= 4;

	while ( stream_size )
	{
		struct riff_chunk * chunk;
		if ( stream_size < 8 ) break;
		stream->chunks = realloc( stream->chunks, ( stream->chunk_count + 1 ) * sizeof( struct riff_chunk ) );
		if ( ! stream->chunks ) break;
		chunk = stream->chunks + stream->chunk_count;
		chunk->type = ( ptr[0] << 24 ) | ( ptr[1] << 16 ) | ( ptr[2] << 8 ) | ptr[3];
		chunk->size = ptr[4] | ( ptr[5] << 8 ) | ( ptr[6] << 16 ) | ( ptr[7] << 24 );
		ptr += 8;
		stream_size -= 8;
		if ( stream_size < chunk->size ) break;
		if ( chunk->type == 'RIFF' )
		{
			chunk->data = riff_parse( ptr - 8, chunk->size + 8, proper );
			if ( ! chunk->data ) break;
		}
		else
		{
			chunk->data = malloc( chunk->size );
			if ( ! chunk->data ) break;
			memcpy( chunk->data, ptr, chunk->size );
		}
		ptr += chunk->size;
		stream_size -= chunk->size;
		if ( proper && ( chunk->size & 1 ) )
		{
			++ ptr;
			-- stream_size;
		}
		++stream->chunk_count;
	}
	
	if ( stream_size )
	{
		riff_free( stream );
		stream = 0;
	}

	return stream;
}

void riff_free( struct riff * stream )
{
	if ( stream )
	{
		if ( stream->chunks )
		{
			unsigned i;
			for ( i = 0; i < stream->chunk_count; ++i )
			{
				struct riff_chunk * chunk = stream->chunks + i;
				if ( chunk->type == 'RIFF' ) riff_free( ( struct riff * ) chunk->data );
				else free( chunk->data );
			}
			free( stream->chunks );
		}
		free( stream );
	}
}