shithub: dumb

Download patch

ref: dae50fa2a1a66a8658827483bdc8a7aae48e019b
parent: 8b2fadaf5a88171d9d94782c9a28e45f4ed10c92
author: Chris Moeller <[email protected]>
date: Mon Jan 11 03:59:05 EST 2010

{11/6/2005 9:04:04 PM}Added generic RIFF module handler and AM/AMFF format readers

git-tfs-id: [http://localhost:8080/tfs/DefaultCollection/]$/foobar2000/files/plugins.root;C46

--- a/dumb/include/dumb.h
+++ b/dumb/include/dumb.h
@@ -356,6 +356,7 @@
 DUH *dumb_load_psm(const char *filename, int subsong);
 DUH *dumb_load_old_psm(const char * filename);
 DUH *dumb_load_mtm(const char *filename);
+DUH *dumb_load_riff(const char *filename);
 
 DUH *dumb_read_it(DUMBFILE *f);
 DUH *dumb_read_xm(DUMBFILE *f);
@@ -366,6 +367,7 @@
 DUH *dumb_read_psm(DUMBFILE *f, int subsong);
 DUH *dumb_read_old_psm(DUMBFILE *f);
 DUH *dumb_read_mtm(DUMBFILE *f);
+DUH *dumb_read_riff(DUMBFILE *f);
 
 int dumb_get_psm_subsong_count(DUMBFILE *f);
 
--- /dev/null
+++ b/dumb/include/internal/barray.h
@@ -1,0 +1,20 @@
+#ifndef _B_ARRAY_H_
+#define _B_ARRAY_H_
+
+#include <stdlib.h>
+
+void * bit_array_create(size_t size);
+void bit_array_destroy(void * array);
+void * bit_array_dup(void * array);
+
+void bit_array_reset(void * array);
+
+void bit_array_set(void * array, size_t bit);
+int bit_array_test(void * array, size_t bit);
+int bit_array_test_range(void * array, size_t bit, size_t count);
+void bit_array_clear(void * array, size_t bit);
+
+void bit_array_merge(void * array, void * source, size_t offset);
+void bit_array_mask(void * array, void * source, size_t offset);
+
+#endif
--- a/dumb/include/internal/it.h
+++ b/dumb/include/internal/it.h
@@ -389,7 +389,7 @@
 
 struct DUMB_IT_SIGDATA
 {
-	unsigned char name[60];
+	unsigned char name[65];
 
 	unsigned char *song_message;
 
--- /dev/null
+++ b/dumb/include/internal/riff.h
@@ -1,0 +1,21 @@
+#ifndef RIFF_H
+#define RIFF_H
+
+struct riff_chunk
+{
+	unsigned type;
+	void * data;
+	unsigned size;
+};
+
+struct riff
+{
+	unsigned type;
+	unsigned chunk_count;
+	struct riff_chunk * chunks;
+};
+
+struct riff * riff_parse( unsigned char *, unsigned size, unsigned proper );
+void riff_free( struct riff * );
+
+#endif
--- /dev/null
+++ b/dumb/src/helpers/riff.c
@@ -1,0 +1,87 @@
+#include "internal/riff.h"
+
+#include <stdlib.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 );
+	}
+}
--- a/dumb/src/it/loadpsm.c
+++ b/dumb/src/it/loadpsm.c
@@ -8,7 +8,7 @@
  * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
  *                                                      /  \
  *                                                     / .  \
- * loads3m.c - Code to read a ProTracker Studio       / / \  \
+ * loadpsm.c - Code to read a ProTracker Studio       / / \  \
  *             file, opening and closing it for      | <  /   \_
  *             you.                                  |  \/ /\   /
  *                                                    \_  /  > /
--- /dev/null
+++ b/dumb/src/it/loadriff.c
@@ -1,0 +1,42 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * loadriff.c - Code to read a RIFF module file       / / \  \
+ *              opening and closing it for you.      | <  /   \_
+ *                                                   |  \/ /\   /
+ *                                                    \_  /  > /
+ * By Chris Moeller.                                    | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+
+
+
+/* dumb_load_riff(): loads a RIFF file into a DUH struct, returning a pointer
+ * to the DUH struct. When you have finished with it, you must pass the
+ * pointer to unload_duh() so that the memory can be freed.
+ */
+DUH * dumb_load_riff( const char *filename )
+{
+	DUH * duh;
+	DUMBFILE * f = dumbfile_open( filename );
+
+	if ( ! f )
+		return NULL;
+
+	duh = dumb_read_riff( f );
+
+	dumbfile_close( f );
+
+	return duh;
+}
--- /dev/null
+++ b/dumb/src/it/readam.c
@@ -1,0 +1,753 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * readam.c - Code to read a RIFF AM module           / / \  \
+ *             from an open file.                    | <  /   \_
+ *                                                   |  \/ /\   /
+ * By Chris Moeller.                                  \_  /  > /
+ *                                                      | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+#include "internal/it.h"
+#include "internal/riff.h"
+
+static int it_riff_am_process_sample( IT_SAMPLE * sample, const unsigned char * data, int len, int ver )
+{
+	int header_length;
+	int default_pan;
+	int default_volume;
+	int flags;
+	int length;
+	int length_bytes;
+	int loop_start;
+	int loop_end;
+	int sample_rate;
+
+	if ( ver == 0 )
+	{
+		if ( len < 0x38 )
+			return -1;
+
+		header_length = 0x38;
+
+		memcpy( sample->name, data, 28 );
+		sample->name[ 28 ] = 0;
+
+		default_pan = data[ 0x1C ];
+		default_volume = data[ 0x1D ];
+		flags = data[ 0x1E ] | ( data[ 0x1F ] << 8 );
+		length = data[ 0x20 ] | ( data[ 0x21 ] << 8 ) | ( data[ 0x22 ] << 16 ) | ( data[ 0x23 ] << 24 );
+		loop_start = data[ 0x24 ] | ( data[ 0x25 ] << 8 ) | ( data[ 0x26 ] << 16 ) | ( data[ 0x27 ] << 24 );
+		loop_end = data[ 0x28 ] | ( data[ 0x29 ] << 8 ) | ( data[ 0x2A ] << 16 ) | ( data[ 0x2B ] << 24 );
+		sample_rate = data[ 0x2C ] | ( data[ 0x2D ] << 8 ) | ( data[ 0x2E ] << 16 ) | ( data[ 0x2F ] << 24 );
+	}
+	else
+	{
+		if (len < 4) return -1;
+
+		header_length = data[ 0 ] | ( data[ 1 ] << 8 ) | ( data[ 2 ] << 16 ) | ( data[ 3 ] << 24 );
+		if ( header_length < 0x40 )
+			return -1;
+		if ( header_length + 4 > len )
+			return -1;
+
+		data += 4;
+		len -= 4;
+
+		memcpy( sample->name, data, 32 );
+		sample->name[ 32 ] = 0;
+
+		default_pan = data[ 0x20 ] | ( data[ 0x21 ] << 8 );
+		default_volume = data[ 0x22 ] | ( data[ 0x23 ] << 8 );
+		flags = data[ 0x24 ] | ( data[ 0x25 ] << 8 ); /* | ( data[ 0x26 ] << 16 ) | ( data[ 0x27 ] << 24 );*/
+		length = data[ 0x28 ] | ( data[ 0x29 ] << 8 ) | ( data[ 0x2A ] << 16 ) | ( data[ 0x2B ] << 24 );
+		loop_start = data[ 0x2C ] | ( data[ 0x2D ] << 8 ) | ( data[ 0x2E ] << 16 ) | ( data[ 0x2F ] << 24 );
+		loop_end = data[ 0x30 ] | ( data[ 0x31 ] << 8 ) | ( data[ 0x32 ] << 16 ) | ( data[ 0x33 ] << 24 );
+		sample_rate = data[ 0x34 ] | ( data[ 0x35 ] << 8 ) | ( data[ 0x36 ] << 16 ) | ( data[ 0x37 ] << 24 );
+
+		if ( default_pan > 0x7FFF || default_volume > 0x7FFF )
+			return -1;
+
+		default_pan = default_pan * 64 / 32767;
+		default_volume = default_volume * 64 / 32767;
+	}
+
+	/*if ( data[ 0x38 ] || data[ 0x39 ] || data[ 0x3A ] || data[ 0x3B ] )
+		return -1;*/
+
+	if ( ! length ) {
+		sample->flags &= ~IT_SAMPLE_EXISTS;
+		return 0;
+	}
+
+	if ( flags & ~( 0x80 | 0x20 | 0x10 | 0x08 | 0x04 ) )
+		return -1;
+
+	length_bytes = length << ( ( flags & 0x04 ) >> 2 );
+
+	if ( length_bytes + header_length > len )
+		return -1;
+
+	sample->flags = 0;
+
+	if ( flags & 0x80 ) sample->flags |= IT_SAMPLE_EXISTS;
+	if ( flags & 0x04 ) sample->flags |= IT_SAMPLE_16BIT;
+
+	sample->length = length;
+	sample->loop_start = loop_start;
+	sample->loop_end = loop_end;
+	sample->C5_speed = sample_rate;
+	sample->default_volume = default_volume;
+	sample->default_pan = default_pan | ( ( flags & 0x20 ) << 2 );
+	sample->filename[0] = 0;
+	sample->global_volume = 64;
+	sample->vibrato_speed = 0;
+	sample->vibrato_depth = 0;
+	sample->vibrato_rate = 0;
+	sample->vibrato_waveform = IT_VIBRATO_SINE;
+	sample->max_resampling_quality = -1;
+
+	if ( flags & 0x08 )
+	{
+		if (((unsigned int)sample->loop_end <= (unsigned int)sample->length) &&
+			((unsigned int)sample->loop_start < (unsigned int)sample->loop_end))
+		{
+			sample->length = sample->loop_end;
+			sample->flags |= IT_SAMPLE_LOOP;
+			if ( flags & 0x10 ) sample->flags |= IT_SAMPLE_PINGPONG_LOOP;
+		}
+	}
+
+	sample->left = malloc( length_bytes );
+	if ( ! sample->left )
+		return -1;
+
+	memcpy( sample->left, data + header_length, length_bytes );
+
+	return 0;
+}
+
+static int it_riff_am_process_pattern( IT_PATTERN * pattern, const unsigned char * data, int len, int ver )
+{
+	int nrows, row, pos;
+	unsigned flags;
+	IT_ENTRY * entry;
+
+	nrows = data[0] + 1;
+
+	pattern->n_rows = nrows;
+
+	data += 1;
+	len -= 1;
+
+	pattern->n_entries = 0;
+
+	row = 0;
+	pos = 0;
+
+	while ( (row < nrows) && (pos < len) ) {
+		if ( ! data[ pos ] ) {
+			++ row;
+			++ pos;
+			continue;
+		}
+
+		flags = data[ pos++ ] & 0xE0;
+
+		if (flags) {
+			++ pattern->n_entries;
+			if (flags & 0x80) pos += 2;
+			if (flags & 0x40) pos += 2;
+			if (flags & 0x20) pos ++;
+		}
+	}
+
+	if ( ! pattern->n_entries ) return 0;
+
+	pattern->n_entries += nrows;
+
+	pattern->entry = malloc( pattern->n_entries * sizeof( * pattern->entry ) );
+	if ( ! pattern->entry ) return -1;
+
+	entry = pattern->entry;
+
+	row = 0;
+	pos = 0;
+
+	while ( ( row < nrows ) && ( pos < len ) )
+	{
+		if ( ! data[ pos ] )
+		{
+			IT_SET_END_ROW( entry );
+			++ entry;
+			++ row;
+			++ pos;
+			continue;
+		}
+
+		flags = data[ pos++ ];
+		entry->channel = flags & 0x1F;
+		entry->mask = 0;
+
+		if (flags & 0xE0)
+		{
+			if ( flags & 0x80 )
+			{
+				_dumb_it_xm_convert_effect( data[ pos + 1 ], data[ pos ], entry, 0 );
+				pos += 2;
+			}
+
+			if ( flags & 0x40 )
+			{
+				if ( data[ pos ] )
+				{
+					entry->mask |= IT_ENTRY_INSTRUMENT;
+					entry->instrument = data[ pos ];
+				}
+				if ( data[ pos + 1 ] )
+				{
+					entry->mask |= IT_ENTRY_NOTE;
+					entry->note = data[ pos + 1 ] - 1;
+				}
+				pos += 2;
+			}
+
+			if ( flags & 0x20 )
+			{
+				entry->mask |= IT_ENTRY_VOLPAN;
+				if ( ver == 0 ) entry->volpan = data[ pos ];
+				else entry->volpan = data[ pos ] * 64 / 127;
+				++ pos;
+			}
+
+			if (entry->mask) entry++;
+		}
+	}
+
+	while ( row < nrows )
+	{
+		IT_SET_END_ROW( entry );
+		++ entry;
+		++ row;
+	}
+
+	pattern->n_entries = entry - pattern->entry;
+	if ( ! pattern->n_entries ) return -1;
+
+	return 0;
+}
+
+static DUMB_IT_SIGDATA *it_riff_amff_load_sigdata( struct riff * stream )
+{
+	DUMB_IT_SIGDATA *sigdata;
+
+	int n, o, p, found;
+
+	unsigned char * ptr;
+
+	if ( ! stream ) goto error;
+
+	if ( stream->type != DUMB_ID( 'A', 'M', 'F', 'F' ) ) goto error;
+
+	sigdata = malloc( sizeof( *sigdata ) );
+	if ( ! sigdata ) goto error;
+
+	sigdata->n_patterns = 0;
+	sigdata->n_samples = 0;
+	sigdata->name[0] = 0;
+
+	found = 0;
+
+	for ( n = 0; n < stream->chunk_count; ++n )
+	{
+		struct riff_chunk * c = stream->chunks + n;
+		switch( c->type )
+		{
+		case DUMB_ID( 'M', 'A', 'I', 'N' ):
+			/* initialization data */
+			if ( ( found & 1 ) || ( c->size < 0x48 ) ) goto error_sd;
+			found |= 1;
+			break;
+
+		case DUMB_ID( 'O', 'R', 'D', 'R' ):
+			if ( ( found & 2 ) || ( c->size < 1 ) ) goto error_sd;
+			found |= 2;
+			break;
+
+		case DUMB_ID( 'P', 'A', 'T', 'T' ):
+			ptr = ( unsigned char * ) c->data;
+			if ( ptr[ 0 ] >= sigdata->n_patterns ) sigdata->n_patterns = ptr[ 0 ] + 1;
+			o = ptr[ 1 ] | ( ptr[ 2 ] << 8 ) | ( ptr[ 3 ] << 16 ) | ( ptr[ 4 ] << 24 );
+			if ( o + 5 > c->size ) goto error_sd;
+			break;
+
+		case DUMB_ID( 'I', 'N', 'S', 'T' ):
+			{
+				if ( c->size < 0x121 ) goto error;
+				ptr = ( unsigned char * ) c->data;
+				if ( ptr[ 1 ] >= sigdata->n_samples ) sigdata->n_samples = ptr[ 1 ] + 1;
+				if ( ptr[ 0xE1 ] == 'S' && ptr[ 0xE2 ] == 'A' &&
+					ptr[ 0xE3 ] == 'M' && ptr[ 0xE4 ] == 'P' )
+				{
+					unsigned size = ptr[ 0xE5 ] | ( ptr[ 0xE6 ] << 8 ) | ( ptr[ 0xE7 ] << 16 ) | ( ptr[ 0xE8 ] << 24 );
+					if ( size + 0xE1 + 8 > c->size ) goto error;
+				}
+			}
+			break;
+		}
+	}
+
+	if ( found != 3 || !sigdata->n_samples || !sigdata->n_patterns ) goto error_sd;
+
+	if ( sigdata->n_samples > 255 || sigdata->n_patterns > 255 ) goto error_sd;
+
+	sigdata->song_message = NULL;
+	sigdata->order = NULL;
+	sigdata->instrument = NULL;
+	sigdata->sample = NULL;
+	sigdata->pattern = NULL;
+	sigdata->midi = NULL;
+	sigdata->checkpoint = NULL;
+
+	sigdata->mixing_volume = 48;
+	sigdata->pan_separation = 128;
+
+	sigdata->n_instruments = 0;
+	sigdata->n_orders = 0;
+
+	memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
+
+	for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) {
+		sigdata->channel_pan[n  ] = 16;
+		sigdata->channel_pan[n+1] = 48;
+		sigdata->channel_pan[n+2] = 48;
+		sigdata->channel_pan[n+3] = 16;
+	}
+
+	for ( n = 0; n < stream->chunk_count; ++n )
+	{
+		struct riff_chunk * c = stream->chunks + n;
+		switch ( c->type )
+		{
+		case DUMB_ID( 'M', 'A', 'I', 'N' ):
+			ptr = ( unsigned char * ) c->data;
+			memcpy( sigdata->name, c->data, 64 );
+			sigdata->name[ 64 ] = 0;
+			sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_WAS_AN_S3M;
+			if ( ! ( ptr[ 0x40 ] & 1 ) ) sigdata->flags |= IT_LINEAR_SLIDES;
+			if ( ( ptr[ 0x40 ] & ~3 ) || ! ( ptr[ 0x40 ] & 2 ) ) goto error_usd; // unknown flags
+			sigdata->n_pchannels = ptr[ 0x41 ];
+			sigdata->speed = ptr[ 0x42 ];
+			sigdata->tempo = ptr[ 0x43 ];
+
+			sigdata->global_volume = ptr[ 0x48 ];
+
+			if ( c->size < 0x48 + sigdata->n_pchannels ) goto error_usd;
+
+			for ( o = 0; o < sigdata->n_pchannels; ++o )
+			{
+				sigdata->channel_pan[ o ] = ptr[ 0x49 + o ];
+				if ( ptr[ 0x49 + o ] >= 128 )
+				{
+					sigdata->channel_volume[ o ] = 0;
+				}
+			}
+			break;
+		}
+	}
+
+	sigdata->pattern = malloc( sigdata->n_patterns * sizeof( *sigdata->pattern ) );
+	if ( ! sigdata->pattern ) goto error_usd;
+	for ( n = 0; n < sigdata->n_patterns; ++n )
+		sigdata->pattern[ n ].entry = NULL;
+
+	sigdata->sample = malloc( sigdata->n_samples * sizeof( *sigdata->sample ) );
+	if ( ! sigdata->sample ) goto error_usd;
+	for ( n = 0; n < sigdata->n_samples; ++n )
+	{
+		IT_SAMPLE * sample = sigdata->sample + n;
+		sample->right = sample->left = NULL;
+		sample->flags = 0;
+		sample->name[ 0 ] = 0;
+	}
+
+	for ( n = 0; n < stream->chunk_count; ++n )
+	{
+		struct riff_chunk * c = stream->chunks + n;
+		switch ( c->type )
+		{
+		case DUMB_ID( 'O', 'R', 'D', 'R' ):
+			ptr = ( unsigned char * ) c->data;
+			sigdata->n_orders = ptr[ 0 ] + 1;
+			if ( sigdata->n_orders + 1 > c->size ) goto error_usd;
+			sigdata->order = malloc( sigdata->n_orders );
+			if ( ! sigdata->order ) goto error_usd;
+			memcpy( sigdata->order, ptr + 1, sigdata->n_orders );
+			break;
+
+		case DUMB_ID( 'P', 'A', 'T', 'T' ):
+			ptr = ( unsigned char * ) c->data;
+			o = ptr[ 1 ] | ( ptr[ 2 ] << 8 ) | ( ptr[ 3 ] << 16 ) | ( ptr[ 4 ] << 24 );
+			if ( it_riff_am_process_pattern( sigdata->pattern + ptr[ 0 ], ptr + 5, o, 0 ) ) goto error_usd;
+			break;
+
+		case DUMB_ID( 'I', 'N', 'S', 'T' ):
+			{
+				IT_SAMPLE * sample;
+				if ( c->size < 0x121 ) goto error;
+				ptr = ( unsigned char * ) c->data;
+				sample = sigdata->sample + ptr[ 1 ];
+				if ( ptr[ 0xE1 ] == 'S' && ptr[ 0xE2 ] == 'A' &&
+					ptr[ 0xE3 ] == 'M' && ptr[ 0xE4 ] == 'P' )
+				{
+					unsigned size = ptr[ 0xE5 ] | ( ptr[ 0xE6 ] << 8 ) | ( ptr[ 0xE7 ] << 16 ) | ( ptr[ 0xE8 ] << 24 );
+					if ( it_riff_am_process_sample( sample, ptr + 0xE1 + 8, size, 0 ) ) goto error_usd;
+				}
+				else
+				{
+					memcpy( sample->name, ptr + 2, 28 );
+					sample->name[ 28 ] = 0;
+				}
+			}
+			break;
+		}
+	}
+
+	_dumb_it_fix_invalid_orders( sigdata );
+
+	return sigdata;
+
+error_usd:
+	_dumb_it_unload_sigdata( sigdata );
+	goto error;
+error_sd:
+	free( sigdata );
+error:
+	return NULL;
+}
+
+static DUMB_IT_SIGDATA *it_riff_am_load_sigdata( struct riff * stream )
+{
+	DUMB_IT_SIGDATA *sigdata;
+
+	int n, o, p, found;
+
+	unsigned char * ptr;
+
+	if ( ! stream ) goto error;
+
+	if ( stream->type != DUMB_ID( 'A', 'M', ' ', ' ' ) ) goto error;
+
+	sigdata = malloc(sizeof(*sigdata));
+	if ( ! sigdata ) goto error;
+
+	sigdata->n_patterns = 0;
+	sigdata->n_samples = 0;
+	sigdata->name[0] = 0;
+
+	found = 0;
+
+	for ( n = 0; n < stream->chunk_count; ++n )
+	{
+		struct riff_chunk * c = stream->chunks + n;
+		switch( c->type )
+		{
+		case DUMB_ID( 'I' ,'N' ,'I' ,'T' ):
+			/* initialization data */
+			if ( ( found & 1 ) || ( c->size < 0x48 ) ) goto error_sd;
+			found |= 1;
+			break;
+
+		case DUMB_ID( 'O', 'R', 'D', 'R' ):
+			if ( ( found & 2 ) || ( c->size < 1 ) ) goto error_sd;
+			found |= 2;
+			break;
+
+		case DUMB_ID( 'P', 'A', 'T', 'T' ):
+			ptr = ( unsigned char * ) c->data;
+			if ( ptr[ 0 ] >= sigdata->n_patterns ) sigdata->n_patterns = ptr[ 0 ] + 1;
+			o = ptr[ 1 ] | ( ptr[ 2 ] << 8 ) | ( ptr[ 3 ] << 16 ) | ( ptr[ 4 ] << 24 );
+			if ( o + 5 > c->size ) goto error_sd;
+			break;
+
+		case DUMB_ID( 'R', 'I', 'F', 'F' ):
+			{
+				struct riff * str = ( struct riff * ) c->data;
+				switch ( str->type )
+				{
+				case DUMB_ID( 'A', 'I', ' ', ' ' ):
+					for ( o = 0; o < str->chunk_count; ++o )
+					{
+						struct riff_chunk * chk = str->chunks + o;
+						switch( chk->type )
+						{
+						case DUMB_ID( 'I', 'N', 'S', 'T' ):
+							{
+								struct riff * temp;
+								unsigned size;
+								unsigned sample_found;
+								ptr = ( unsigned char * ) chk->data;
+								size = ptr[ 0 ] | ( ptr[ 1 ] << 8 ) | ( ptr[ 2 ] << 16 ) | ( ptr[ 3 ] << 24 );
+								if ( size < 0x142 ) goto error;
+								sample_found = 0;
+								if ( ptr[ 5 ] >= sigdata->n_samples ) sigdata->n_samples = ptr[ 5 ] + 1;
+								temp = riff_parse( ptr + 4 + size, chk->size - size - 4, 1 );
+								if ( temp )
+								{
+									if ( temp->type == DUMB_ID( 'A', 'S', ' ', ' ' ) )
+									{
+										for ( p = 0; p < temp->chunk_count; ++p )
+										{
+											if ( temp->chunks[ p ].type == DUMB_ID( 'S', 'A', 'M', 'P' ) )
+											{
+												if ( sample_found )
+												{
+													riff_free( temp );
+													goto error;
+												}
+												sample_found = 1;
+											}
+										}
+									}
+									riff_free( temp );
+								}
+							}
+						}
+					}
+				}
+			}
+			break;
+		}
+	}
+
+	if ( found != 3 || !sigdata->n_samples || !sigdata->n_patterns ) goto error_sd;
+
+	if ( sigdata->n_samples > 255 || sigdata->n_patterns > 255 ) goto error_sd;
+
+	sigdata->song_message = NULL;
+	sigdata->order = NULL;
+	sigdata->instrument = NULL;
+	sigdata->sample = NULL;
+	sigdata->pattern = NULL;
+	sigdata->midi = NULL;
+	sigdata->checkpoint = NULL;
+
+	sigdata->mixing_volume = 48;
+	sigdata->pan_separation = 128;
+
+	sigdata->n_instruments = 0;
+	sigdata->n_orders = 0;
+
+	memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
+
+	for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) {
+		sigdata->channel_pan[n  ] = 16;
+		sigdata->channel_pan[n+1] = 48;
+		sigdata->channel_pan[n+2] = 48;
+		sigdata->channel_pan[n+3] = 16;
+	}
+
+	for ( n = 0; n < stream->chunk_count; ++n )
+	{
+		struct riff_chunk * c = stream->chunks + n;
+		switch ( c->type )
+		{
+		case DUMB_ID( 'I', 'N', 'I', 'T' ):
+			ptr = ( unsigned char * ) c->data;
+			memcpy( sigdata->name, c->data, 64 );
+			sigdata->name[ 64 ] = 0;
+			sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_WAS_AN_S3M;
+			if ( ! ( ptr[ 0x40 ] & 1 ) ) sigdata->flags |= IT_LINEAR_SLIDES;
+			if ( ( ptr[ 0x40 ] & ~3 ) || ! ( ptr[ 0x40 ] & 2 ) ) goto error_usd; // unknown flags
+			sigdata->n_pchannels = ptr[ 0x41 ];
+			sigdata->speed = ptr[ 0x42 ];
+			sigdata->tempo = ptr[ 0x43 ];
+
+			sigdata->global_volume = ptr[ 0x48 ];
+
+			if ( c->size < 0x48 + sigdata->n_pchannels ) goto error_usd;
+
+			for ( o = 0; o < sigdata->n_pchannels; ++o )
+			{
+				if ( ptr[ 0x49 + o ] <= 128 )
+				{
+					sigdata->channel_pan[ o ] = ptr[ 0x49 + o ] / 2;
+				}
+				else
+				{
+					sigdata->channel_volume[ o ] = 0;
+				}
+			}
+			break;
+		}
+	}
+
+	sigdata->pattern = malloc( sigdata->n_patterns * sizeof( *sigdata->pattern ) );
+	if ( ! sigdata->pattern ) goto error_usd;
+	for ( n = 0; n < sigdata->n_patterns; ++n )
+		sigdata->pattern[ n ].entry = NULL;
+
+	sigdata->sample = malloc( sigdata->n_samples * sizeof( *sigdata->sample ) );
+	if ( ! sigdata->sample ) goto error_usd;
+	for ( n = 0; n < sigdata->n_samples; ++n )
+	{
+		IT_SAMPLE * sample = sigdata->sample + n;
+		sample->right = sample->left = NULL;
+		sample->flags = 0;
+		sample->name[ 0 ] = 0;
+	}
+
+	for ( n = 0; n < stream->chunk_count; ++n )
+	{
+		struct riff_chunk * c = stream->chunks + n;
+		switch ( c->type )
+		{
+		case DUMB_ID( 'O', 'R', 'D', 'R' ):
+			ptr = ( unsigned char * ) c->data;
+			sigdata->n_orders = ptr[ 0 ] + 1;
+			if ( sigdata->n_orders + 1 > c->size ) goto error_usd;
+			sigdata->order = malloc( sigdata->n_orders );
+			if ( ! sigdata->order ) goto error_usd;
+			memcpy( sigdata->order, ptr + 1, sigdata->n_orders );
+			break;
+
+		case DUMB_ID( 'P', 'A', 'T', 'T' ):
+			ptr = ( unsigned char * ) c->data;
+			o = ptr[ 1 ] | ( ptr[ 2 ] << 8 ) | ( ptr[ 3 ] << 16 ) | ( ptr[ 4 ] << 24 );
+			if ( it_riff_am_process_pattern( sigdata->pattern + ptr[ 0 ], ptr + 5, o, 1 ) ) goto error_usd;
+			break;
+
+		case DUMB_ID( 'R', 'I', 'F', 'F' ):
+			{
+				struct riff * str = ( struct riff * ) c->data;
+				switch ( str->type )
+				{
+				case DUMB_ID('A', 'I', ' ', ' '):
+					for ( o = 0; o < str->chunk_count; ++o )
+					{
+						struct riff_chunk * chk = str->chunks + o;
+						switch( chk->type )
+						{
+						case DUMB_ID( 'I', 'N', 'S', 'T' ):
+							{
+								struct riff * temp;
+								unsigned size;
+								unsigned sample_found;
+								IT_SAMPLE * sample;
+								ptr = ( unsigned char * ) chk->data;
+								size = ptr[ 0 ] | ( ptr[ 1 ] << 8 ) | ( ptr[ 2 ] << 16 ) | ( ptr[ 3 ] << 24 );
+								temp = riff_parse( ptr + 4 + size, chk->size - size - 4, 1 );
+								sample_found = 0;
+								sample = sigdata->sample + ptr[ 5 ];
+								if ( temp )
+								{
+									if ( temp->type == DUMB_ID( 'A', 'S', ' ', ' ' ) )
+									{
+										for ( p = 0; p < temp->chunk_count; ++p )
+										{
+											struct riff_chunk * c = temp->chunks + p;
+											if ( c->type == DUMB_ID( 'S', 'A', 'M', 'P' ) )
+											{
+												if ( sample_found )
+												{
+													riff_free( temp );
+													goto error_usd;
+												}
+												if ( it_riff_am_process_sample( sigdata->sample + ptr[ 5 ], ( unsigned char * ) c->data, c->size, 1 ) )
+												{
+													riff_free( temp );
+													goto error_usd;
+												}
+												sample_found = 1;
+											}
+										}
+									}
+									riff_free( temp );
+								}
+								if ( ! sample_found )
+								{
+									memcpy( sample->name, ptr + 6, 32 );
+									sample->name[ 32 ] = 0;
+								}
+							}
+						}
+					}
+				}
+			}
+			break;
+		}
+	}
+
+	_dumb_it_fix_invalid_orders( sigdata );
+
+	return sigdata;
+
+error_usd:
+	_dumb_it_unload_sigdata( sigdata );
+	goto error;
+error_sd:
+	free( sigdata );
+error:
+	return NULL;
+}
+
+DUH *dumb_read_riff_amff( struct riff * stream )
+{
+	sigdata_t *sigdata;
+	long length;
+
+	DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+	sigdata = it_riff_amff_load_sigdata( stream );
+
+	if (!sigdata)
+		return NULL;
+
+	length = 0;/*_dumb_it_build_checkpoints(sigdata, 0);*/
+
+	{
+		const char *tag[2][2];
+		tag[0][0] = "TITLE";
+		tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+		tag[1][0] = "FORMAT";
+		tag[1][1] = "RIFF AMFF";
+		return make_duh( length, 2, ( const char * const (*) [ 2 ] ) tag, 1, & descptr, & sigdata );
+	}
+}
+
+DUH *dumb_read_riff_am( struct riff * stream )
+{
+	sigdata_t *sigdata;
+	long length;
+
+	DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
+
+	sigdata = it_riff_am_load_sigdata( stream );
+
+	if (!sigdata)
+		return NULL;
+
+	length = 0;/*_dumb_it_build_checkpoints(sigdata, 0);*/
+
+	{
+		const char *tag[2][2];
+		tag[0][0] = "TITLE";
+		tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
+		tag[1][0] = "FORMAT";
+		tag[1][1] = "RIFF AM";
+		return make_duh( length, 2, ( const char * const (*) [ 2 ] ) tag, 1, & descptr, & sigdata );
+	}
+}
--- /dev/null
+++ b/dumb/src/it/readriff.c
@@ -1,0 +1,72 @@
+/*  _______         ____    __         ___    ___
+ * \    _  \       \    /  \  /       \   \  /   /       '   '  '
+ *  |  | \  \       |  |    ||         |   \/   |         .      .
+ *  |  |  |  |      |  |    ||         ||\  /|  |
+ *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
+ *  |  |  |  |      |  |    ||         ||    |  |         .      .
+ *  |  |_/  /        \  \__//          ||    |  |
+ * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
+ *                                                      /  \
+ *                                                     / .  \
+ * readriff.c - Code to read a RIFF module file       / / \  \
+ *              from memory.                         | <  /   \_
+ *                                                   |  \/ /\   /
+ *                                                    \_  /  > /
+ * By Chris Moeller.                                    | \ / /
+ *                                                      |  ' /
+ *                                                       \__/
+ */
+
+#include "dumb.h"
+#include "internal/it.h"
+#include "internal/riff.h"
+
+
+DUH *dumb_read_riff_amff( struct riff * stream );
+DUH *dumb_read_riff_am( struct riff * stream );
+
+/* dumb_read_riff(): reads a RIFF file into a DUH struct, returning a pointer
+ * to the DUH struct. When you have finished with it, you must pass the
+ * pointer to unload_duh() so that the memory can be freed.
+ */
+DUH *dumb_read_riff( DUMBFILE * f )
+{
+	DUH * duh;
+	struct riff * stream;
+
+	{
+		unsigned char * buffer = 0;
+		unsigned size = 0;
+		unsigned read;
+		do
+		{
+			buffer = realloc( buffer, 32768 + size );
+			if ( ! buffer ) return 0;
+			read = dumbfile_getnc( buffer + size, 32768, f );
+			if ( read < 0 )
+			{
+				free( buffer );
+				return 0;
+			}
+			size += read;
+		}
+		while ( read == 32768 );
+		stream = riff_parse( buffer, size, 1 );
+		if ( ! stream ) stream = riff_parse( buffer, size, 0 );
+		free( buffer );
+	}
+
+	if ( ! stream ) return 0;
+
+	if ( stream->type == DUMB_ID( 'A', 'M', ' ', ' ' ) )
+		duh = dumb_read_riff_am( stream );
+	else if ( stream->type == DUMB_ID( 'A', 'M', 'F', 'F' ) )
+		duh = dumb_read_riff_amff( stream );
+	/* else if ( stream->type == DUMB_ID( 'D', 'S', 'M', 'F' ) )
+		duh = dumb_read_riff_dsmf( stream ); */
+	else duh = 0;
+
+	riff_free( stream );
+
+	return duh;
+}
--- a/dumb/vc6/dumb/dumb.vcproj
+++ b/dumb/vc6/dumb/dumb.vcproj
@@ -841,6 +841,10 @@
 					</FileConfiguration>
 				</File>
 				<File
+					RelativePath="..\..\src\helpers\riff.c"
+					>
+				</File>
+				<File
 					RelativePath="..\..\src\helpers\sampbuf.c"
 					>
 					<FileConfiguration
@@ -1335,6 +1339,10 @@
 					</FileConfiguration>
 				</File>
 				<File
+					RelativePath="..\..\src\it\loadriff.c"
+					>
+				</File>
+				<File
 					RelativePath="..\..\src\it\loads3m.c"
 					>
 					<FileConfiguration
@@ -1475,6 +1483,10 @@
 					</FileConfiguration>
 				</File>
 				<File
+					RelativePath="..\..\src\it\readam.c"
+					>
+				</File>
+				<File
 					RelativePath="..\..\src\it\readmod.c"
 					>
 					<FileConfiguration
@@ -1650,6 +1662,10 @@
 					</FileConfiguration>
 				</File>
 				<File
+					RelativePath="..\..\src\it\readriff.c"
+					>
+				</File>
+				<File
 					RelativePath="..\..\src\it\reads3m.c"
 					>
 					<FileConfiguration
@@ -1767,11 +1783,19 @@
 				Name="internal"
 				>
 				<File
+					RelativePath="..\..\include\internal\barray.h"
+					>
+				</File>
+				<File
 					RelativePath="..\..\include\internal\dumb.h"
 					>
 				</File>
 				<File
 					RelativePath="..\..\include\internal\it.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\include\internal\riff.h"
 					>
 				</File>
 			</Filter>