diff --git a/dumb/src/it/readxm.c b/dumb/src/it/readxm.c index 7d1b4657b..4afe36568 100644 --- a/dumb/src/it/readxm.c +++ b/dumb/src/it/readxm.c @@ -111,6 +111,7 @@ typedef struct XM_INSTRUMENT_EXTRA int vibrato_sweep; /* 0-0xFF */ int vibrato_depth; /* 0-0x0F */ int vibrato_speed; /* 0-0x3F */ + int sample_header_size; } XM_INSTRUMENT_EXTRA; @@ -359,6 +360,116 @@ static int it_xm_make_envelope(IT_ENVELOPE *envelope, const unsigned short *data +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) { uint32 size, bytes_read; @@ -370,8 +481,15 @@ static int it_xm_read_instrument(IT_INSTRUMENT *instrument, XM_INSTRUMENT_EXTRA * 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; @@ -385,12 +503,9 @@ static int it_xm_read_instrument(IT_INSTRUMENT *instrument, XM_INSTRUMENT_EXTRA 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++) { @@ -476,7 +591,7 @@ static int it_xm_read_instrument(IT_INSTRUMENT *instrument, XM_INSTRUMENT_EXTRA 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; @@ -924,16 +1039,24 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version) 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; } @@ -948,17 +1071,31 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version) 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; } 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; } @@ -975,12 +1112,15 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version) } 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; } } total_samples += extra.n_samples; } + + dumbfile_close( lf ); } sigdata->n_samples = total_samples; @@ -1012,8 +1152,16 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version) 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; @@ -1026,6 +1174,7 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version) 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; @@ -1033,10 +1182,24 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version) 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); @@ -1055,6 +1218,8 @@ static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version) } total_samples += extra.n_samples; } + + dumbfile_close( lf ); } sigdata->n_samples = total_samples;