shithub: dumb

Download patch

ref: 5bee3e5ba3d57d1b16dda6d82c18fb417781625b
parent: 0e95459562669335f7de543d063cfa57d25a8b77
author: Chris Moeller <kode54@gmail.com>
date: Tue Jul 3 18:24:17 EDT 2012

- Fixed XM reader for files with smaller than expected instrument or sample header sizes
- Version is now 0.9.9.54

--- a/dumb/src/it/readxm.c
+++ b/dumb/src/it/readxm.c
@@ -108,6 +108,7 @@
 	int vibrato_sweep; /* 0-0xFF */
 	int vibrato_depth; /* 0-0x0F */
 	int vibrato_speed; /* 0-0x3F */
+	int sample_header_size;
 }
 XM_INSTRUMENT_EXTRA;
 
@@ -347,6 +348,116 @@
 
 
 
+typedef struct LIMITED_XM LIMITED_XM;
+
+struct LIMITED_XM
+{
+	unsigned char *buffered;
+	long ptr, limit, allocated;
+	DUMBFILE *remaining;
+};
+
+/* XXX */
+struct DUMBFILE
+{
+	DUMBFILE_SYSTEM *dfs;
+	void *file;
+	long pos;
+};
+
+static int limit_xm_resize(void *f, long n)
+{
+	DUMBFILE *df = f;
+	LIMITED_XM *lx = df->file;
+	if (lx->buffered || n) {
+		if (n > lx->allocated) {
+			unsigned char *buffered = realloc( lx->buffered, n );
+			if ( !buffered ) return -1;
+			lx->buffered = buffered;
+			memset( buffered + lx->allocated, 0, n - lx->allocated );
+			lx->allocated = n;
+		}
+		if ( dumbfile_getnc( lx->buffered, n, lx->remaining ) < n ) return -1;
+	} else if (!n) {
+		if ( lx->buffered ) free( lx->buffered );
+		lx->buffered = NULL;
+		lx->allocated = 0;
+	}
+	lx->limit = n;
+	lx->ptr = 0;
+	return 0;
+}
+
+static int limit_xm_skip(void *f, long n)
+{
+	LIMITED_XM *lx = f;
+	lx->ptr += n;
+	return 0;
+}
+
+
+
+static int limit_xm_getc(void *f)
+{
+	LIMITED_XM *lx = f;
+	if (lx->ptr >= lx->allocated) {
+		return 0;
+	}
+	return lx->buffered[lx->ptr++];
+}
+
+
+
+static long limit_xm_getnc(char *ptr, long n, void *f)
+{
+	LIMITED_XM *lx = f;
+	int left;
+	left = lx->allocated - lx->ptr;
+	if (n > left) {
+		if (left > 0) {
+			memcpy( ptr, lx->buffered + lx->ptr, left );
+			memset( ptr + left, 0, n - left );
+		} else {
+			memset( ptr, 0, n );
+		}
+	} else {
+		memcpy( ptr, lx->buffered + lx->ptr, n );
+	}
+	lx->ptr += n;
+	return n;
+}
+
+
+
+static void limit_xm_close(void *f)
+{
+	LIMITED_XM *lx = f;
+	if (lx->buffered) free(lx->buffered);
+	/* Do NOT close lx->remaining */
+	free(f);
+}
+
+
+
+DUMBFILE_SYSTEM limit_xm_dfs = {
+	NULL,
+	&limit_xm_skip,
+	&limit_xm_getc,
+	&limit_xm_getnc,
+	&limit_xm_close
+};
+
+static DUMBFILE *dumbfile_limit_xm(DUMBFILE *f)
+{
+	LIMITED_XM * lx = malloc(sizeof(*lx));
+	lx->remaining = f;
+	lx->buffered = NULL;
+	lx->ptr = 0;
+	lx->limit = 0;
+	lx->allocated = 0;
+	return dumbfile_open_ex( lx, &limit_xm_dfs );
+}
+
 static int it_xm_read_instrument(IT_INSTRUMENT *instrument, XM_INSTRUMENT_EXTRA *extra, DUMBFILE *f)
 {
 	unsigned long size, bytes_read;
@@ -358,8 +469,15 @@
 	 * So unread bytes must be skipped before reading the first sample
 	 * header.
 	 */
+
+	if ( limit_xm_resize( f, 4 ) < 0 ) return -1;
+
 	size = dumbfile_igetl(f);
 
+	if ( size == 0 ) size = 4 + 22 + 1 + 2 + 4 + 96 + 48 + 48 + 1 * 14 + 2 + 2;
+
+	if ( limit_xm_resize( f, size - 4 ) < 0 ) return -1;
+
 	dumbfile_getnc(instrument->name, 22, f);
 	instrument->name[22] = 0;
 	instrument->filename[0] = 0;
@@ -373,12 +491,9 @@
 
 	if (extra->n_samples) {
 		/* sample header size */
-		dumbfile_skip(f, 4); // XXX can't be trusted, as there are trackers that write the wrong value here
-		/*i = dumbfile_igetl(f);
-		if (i && i != 0x28) { // XXX some crap with 0 here
-			TRACE("XM error: unexpected sample header size\n");
-			return -1;
-		}*/
+		i = dumbfile_igetl(f);
+		if (!i) i = 0x28;
+		extra->sample_header_size = i;
 
 		/* sample map */
 		for (i = 0; i < 96; i++) {
@@ -464,7 +579,7 @@
 		for (i = 0; i < 96; i++)
 			instrument->map_sample[i] = 0;
 
-	if (dumbfile_skip(f, size - bytes_read))
+	if (size > bytes_read && dumbfile_skip(f, size - bytes_read))
 		return -1;
 
 	instrument->new_note_action = NNA_NOTE_CUT;
@@ -825,16 +940,24 @@
 		for (i = 0; i < sigdata->n_instruments; i++) {
 			XM_INSTRUMENT_EXTRA extra;
 
-			if (it_xm_read_instrument(&sigdata->instrument[i], &extra, f) < 0) {
+			DUMBFILE * lf = dumbfile_limit_xm( f );
+			if ( !lf ) {
+				_dumb_it_unload_sigdata(sigdata);
+				return NULL;
+			}
+
+			if (it_xm_read_instrument(&sigdata->instrument[i], &extra, lf) < 0) {
 				// XXX
 				if ( ! i )
 				{
 					TRACE("XM error: instrument %d\n", i+1);
+					dumbfile_close( lf );
 					_dumb_it_unload_sigdata(sigdata);
 					return NULL;
 				}
 				else
 				{
+					dumbfile_close( lf );
 					sigdata->n_instruments = i;
 					break;
 				}
@@ -849,6 +972,7 @@
 
 				sigdata->sample = safe_realloc(sigdata->sample, sizeof(*sigdata->sample)*(total_samples+extra.n_samples));
 				if (!sigdata->sample) {
+					dumbfile_close( lf );
 					_dumb_it_unload_sigdata(sigdata);
 					return NULL;
 				}
@@ -855,11 +979,24 @@
 				for (j = total_samples; j < total_samples+extra.n_samples; j++)
 					sigdata->sample[j].data = NULL;
 
+				if ( limit_xm_resize( lf, 0 ) < 0 ) {
+					dumbfile_close( lf );
+					_dumb_it_unload_sigdata( sigdata );
+					return NULL;
+				}
+
 				/* read instrument's samples */
 				for (j = 0; j < extra.n_samples; j++) {
 					IT_SAMPLE *sample = &sigdata->sample[total_samples+j];
-					int b = it_xm_read_sample_header(sample, f);
+					int b;
+					if ( limit_xm_resize( lf, extra.sample_header_size ) < 0 ) {
+						dumbfile_close( lf );
+						_dumb_it_unload_sigdata( sigdata );
+						return NULL;
+					}
+					b = it_xm_read_sample_header(sample, lf);
 					if (b < 0) {
+						dumbfile_close( lf );
 						_dumb_it_unload_sigdata(sigdata);
 						return NULL;
 					}
@@ -876,6 +1013,7 @@
 				}
 				for (j = 0; j < extra.n_samples; j++) {
 					if (it_xm_read_sample_data(&sigdata->sample[total_samples+j], roguebytes[j], f) != 0) {
+						dumbfile_close( lf );
 						_dumb_it_unload_sigdata(sigdata);
 						return NULL;
 					}
@@ -882,6 +1020,8 @@
 				}
 				total_samples += extra.n_samples;
 			}
+
+			dumbfile_close( lf );
 		}
 
 		sigdata->n_samples = total_samples;
@@ -913,8 +1053,16 @@
 		for (i = 0; i < sigdata->n_instruments; i++) {
 			XM_INSTRUMENT_EXTRA extra;
 
-			if (it_xm_read_instrument(&sigdata->instrument[i], &extra, f) < 0) {
+			DUMBFILE * lf = dumbfile_limit_xm( f );
+			if ( !lf ) {
+				free(roguebytes);
+				_dumb_it_unload_sigdata(sigdata);
+				return NULL;
+			}
+
+			if (it_xm_read_instrument(&sigdata->instrument[i], &extra, lf) < 0) {
 				TRACE("XM error: instrument %d\n", i+1);
+				dumbfile_close(lf);
 				free(roguebytes);
 				_dumb_it_unload_sigdata(sigdata);
 				return NULL;
@@ -927,6 +1075,7 @@
 
 				sigdata->sample = safe_realloc(sigdata->sample, sizeof(*sigdata->sample)*(total_samples+extra.n_samples));
 				if (!sigdata->sample) {
+					dumbfile_close( lf );
 					free(roguebytes);
 					_dumb_it_unload_sigdata(sigdata);
 					return NULL;
@@ -934,10 +1083,24 @@
 				for (j = total_samples; j < total_samples+extra.n_samples; j++)
 					sigdata->sample[j].data = NULL;
 
+				if ( limit_xm_resize( lf, 0 ) < 0 ) {
+					dumbfile_close( lf );
+					free( roguebytes );
+					_dumb_it_unload_sigdata( sigdata );
+					return NULL;
+				}
+
 				/* read instrument's samples */
 				for (j = 0; j < extra.n_samples; j++) {
 					IT_SAMPLE *sample = &sigdata->sample[total_samples+j];
-					int b = it_xm_read_sample_header(sample, f);
+					int b;
+					if ( limit_xm_resize( lf, extra.sample_header_size ) < 0 ) {
+							dumbfile_close( lf );
+							free( roguebytes );
+							_dumb_it_unload_sigdata( sigdata );
+							return NULL;
+					}
+					b = it_xm_read_sample_header(sample, lf);
 					if (b < 0) {
 						free(roguebytes);
 						_dumb_it_unload_sigdata(sigdata);
@@ -956,6 +1119,8 @@
 				}
 				total_samples += extra.n_samples;
 			}
+
+			dumbfile_close( lf );
 		}
 
 		sigdata->n_samples = total_samples;