shithub: dumb

ref: 9a77e91ba8d54b9e2d0cca801d8995a266374112
dir: /src/helpers/riff.c/

View raw version
#include "dumb.h"
#include "internal/riff.h"

#include <stdlib.h>

struct riff * riff_parse( DUMBFILE * f, long offset, long size, unsigned proper )
{
	unsigned stream_size;
	struct riff * stream;

    if ( size < 8 ) return 0;

    if ( dumbfile_seek(f, offset, DFS_SEEK_SET) ) return 0;
    if ( dumbfile_mgetl(f) != DUMB_ID('R','I','F','F') ) return 0;

    stream_size = (int) dumbfile_igetl(f);
    if ( stream_size + 8 > size ) return 0;
	if ( stream_size < 4 ) return 0;

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

    stream->type = (int) dumbfile_mgetl(f);
	stream->chunk_count = 0;
	stream->chunks = 0;

	stream_size -= 4;

    while ( stream_size && !dumbfile_error(f) )
	{
		struct riff_chunk * chunk;
		if ( stream_size < 8 ) break;
        stream->chunks = ( struct riff_chunk * ) realloc( stream->chunks, ( stream->chunk_count + 1 ) * sizeof( struct riff_chunk ) );
		if ( ! stream->chunks ) break;
		chunk = stream->chunks + stream->chunk_count;
        chunk->type = (int) dumbfile_mgetl(f);
        chunk->size = (int) dumbfile_igetl(f);
        chunk->offset = dumbfile_pos(f);
		stream_size -= 8;
		if ( stream_size < chunk->size ) break;
        if ( chunk->type == DUMB_ID('R','I','F','F') )
		{
            chunk->nested = riff_parse( f, chunk->offset - 8, chunk->size + 8, proper );
            if ( ! chunk->nested ) break;
		}
		else
		{
            chunk->nested = 0;
		}
        dumbfile_seek(f, chunk->offset + chunk->size, DFS_SEEK_SET);
		stream_size -= chunk->size;
		if ( proper && ( chunk->size & 1 ) )
		{
            dumbfile_skip(f, 1);
			-- 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->nested ) riff_free( chunk->nested );
			}
			free( stream->chunks );
		}
		free( stream );
	}
}