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;