/* _______ ____ __ ___ ___ * \ _ \ \ / \ / \ \ / / ' ' ' * | | \ \ | | || | \/ | . . * | | | | | | || ||\ /| | * | | | | | | || || \/ | | ' ' ' * | | | | | | || || | | . . * | |_/ / \ \__// || | | * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque * / \ * / . \ * readxm.c - Code to read a Fast Tracker II / / \ \ * module from an open file. | < / \_ * | \/ /\ / * By Julien Cugniere. Some bits of code taken \_ / > / * from reads3m.c. | \ / / * | ' / * \__/ */ #include <stdlib.h>#include <string.h>#include <math.h> #include "dumb.h"#include "internal/it.h"#include "internal/dumbfile.h" /** TODO: * XM_TREMOLO doesn't sound quite right... * XM_SET_ENVELOPE_POSITION todo. * VIBRATO conversion needs to be checked (sample/effect/volume). Plus check that effect memory is correct when using XM_VOLSLIDE_VIBRATO. - sample vibrato (instrument vibrato) is now handled correctly. - entheh * XM_E_SET_VIBRATO/TREMOLO_CONTROL: effectvalue&4 -> don't retrig wave when a new instrument is played. In retrigger_note()?. Is it worth implementing? * Lossy fadeout approximation. 0..31 converted to 0 --> won't fade at all. * Replace DUMB's sawtooth by ramp_down/ramp_up. Update XM loader. * A lot of things need to be reset when the end of the song is reached. * It seems that IT and XM don't behave the same way when dealing with mixed loops. When IT encounters multiple SBx (x>0) commands on the same row, it decrements the loop count for all, but only execute the loop of the last one (highest channel). FT2 only decrements the loop count of the last one. Not that I know of any modules using so convoluted combinations! * Maybe we could remove patterns that don't appear in the order table ? Or provide a function to "optimize" a DUMB_IT_SIGDATA ? */ #define XM_LINEAR_FREQUENCY 1 /* otherwise, use amiga slides */ #define XM_ENTRY_PACKED 128#define XM_ENTRY_NOTE 1#define XM_ENTRY_INSTRUMENT 2#define XM_ENTRY_VOLUME 4#define XM_ENTRY_EFFECT 8#define XM_ENTRY_EFFECTVALUE 16 #define XM_NOTE_OFF 97 #define XM_ENVELOPE_ON 1#define XM_ENVELOPE_SUSTAIN 2#define XM_ENVELOPE_LOOP 4 #define XM_SAMPLE_NO_LOOP 0#define XM_SAMPLE_FORWARD_LOOP 1#define XM_SAMPLE_PINGPONG_LOOP 2#define XM_SAMPLE_16BIT 16#define XM_SAMPLE_STEREO 32 #define XM_VIBRATO_SINE 0#define XM_VIBRATO_SQUARE 1#define XM_VIBRATO_RAMP_DOWN 2#define XM_VIBRATO_RAMP_UP 3 /* Probably useless :) */const char xm_convert_vibrato[] = {IT_VIBRATO_SINE, IT_VIBRATO_XM_SQUARE, IT_VIBRATO_RAMP_DOWN, IT_VIBRATO_RAMP_UP, IT_VIBRATO_RANDOM}; #define XM_MAX_SAMPLES_PER_INSTRUMENT 16 /* Extra data that doesn't fit inside IT_INSTRUMENT */typedef struct XM_INSTRUMENT_EXTRA { int n_samples; int vibrato_type; int vibrato_sweep; /* 0-0xFF */ int vibrato_depth; /* 0-0x0F */ int vibrato_speed; /* 0-0x3F */ int sample_header_size;} XM_INSTRUMENT_EXTRA; /* Trims off trailing white space, usually added by the tracker on file creation */static void trim_whitespace(char *ptr, size_t size) { char *p = ptr + size - 1; while (p >= ptr && *p <= 0x20) *p-- = '\0';} /* Frees the original block if it can't resize it or if size is 0, and acts * as malloc if ptr is NULL. */static void *safe_realloc(void *ptr, size_t size) { if (ptr == NULL) return malloc(size); if (size == 0) { free(ptr); return NULL; } else { void *new_block = realloc(ptr, size); if (!new_block) free(ptr); return new_block; }} /* The interpretation of the XM volume column is left to the player. Here, we * just filter bad values. */// This function is so tiny now, should we inline it?static void it_xm_convert_volume(int volume, IT_ENTRY *entry) { entry->mask |= IT_ENTRY_VOLPAN; entry->volpan = volume; switch (volume >> 4) { case 0xA: /* set vibrato speed */ case 0xB: /* vibrato */ case 0xF: /* tone porta */ case 0x6: /* vol slide up */ case 0x7: /* vol slide down */ case 0x8: /* fine vol slide up */ case 0x9: /* fine vol slide down */ case 0xC: /* set panning */ case 0xD: /* pan slide left */ case 0xE: /* pan slide right */ case 0x1: /* set volume */ case 0x2: /* set volume */ case 0x3: /* set volume */ case 0x4: /* set volume */ break; case 0x5: if (volume == 0x50) break; /* set volume */ /* else fall through */ default: entry->mask &= ~IT_ENTRY_VOLPAN; break; }} static int it_xm_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int n_channels, unsigned char *buffer, int version) { int size; int pos; int channel; int row; int effect, effectvalue; IT_ENTRY *entry; /* pattern header size */ if (dumbfile_igetl(f) != (version == 0x0102 ? 0x08 : 0x09)) { TRACE("XM error: unexpected pattern header size\n"); return -1; } /* pattern data packing type */ if (dumbfile_getc(f) != 0) { TRACE("XM error: unexpected pattern packing type\n"); return -1; } if (version == 0x0102) pattern->n_rows = dumbfile_getc(f) + 1; else pattern->n_rows = dumbfile_igetw(f); /* 1..256 */ size = dumbfile_igetw(f); pattern->n_entries = 0; if (dumbfile_error(f)) return -1; if (size == 0) return 0; if (size > 1280 * n_channels) { TRACE("XM error: pattern data size > %d bytes\n", 1280 * n_channels); return -1; } if (dumbfile_getnc((char *)buffer, size, f) < size) return -1; /* compute number of entries */ pattern->n_entries = 0; pos = channel = row = 0; while (pos < size) { if (!(buffer[pos] & XM_ENTRY_PACKED) || (buffer[pos] & 31)) pattern->n_entries++; channel++; if (channel >= n_channels) { channel = 0; row++; pattern->n_entries++; } if (buffer[pos] & XM_ENTRY_PACKED) { static const char offset[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5}; pos += 1 + offset[buffer[pos] & 31]; } else { pos += 5; } } if (row > pattern->n_rows) { TRACE("XM error: wrong number of rows in pattern data\n"); return -1; } /* Whoops, looks like some modules may be short, a few channels, maybe even * rows... */ while (row < pattern->n_rows) { pattern->n_entries++; row++; } pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry)); if (!pattern->entry) return -1; /* read the entries */ entry = pattern->entry; pos = channel = row = 0; while (pos < size) { unsigned char mask; if (buffer[pos] & XM_ENTRY_PACKED) mask = buffer[pos++] & 31; else mask = 31; if (mask) { ASSERT(entry < pattern->entry + pattern->n_entries); entry->channel = channel; entry->mask = 0; if (mask & XM_ENTRY_NOTE) { int note = buffer[pos++]; /* 1-96 <=> C0-B7 */ entry->note = (note == XM_NOTE_OFF) ? (IT_NOTE_OFF) : (note - 1); entry->mask |= IT_ENTRY_NOTE; } if (mask & XM_ENTRY_INSTRUMENT) { entry->instrument = buffer[pos++]; /* 1-128 */ entry->mask |= IT_ENTRY_INSTRUMENT; } if (mask & XM_ENTRY_VOLUME) it_xm_convert_volume(buffer[pos++], entry); effect = effectvalue = 0; if (mask & XM_ENTRY_EFFECT) effect = buffer[pos++]; if (mask & XM_ENTRY_EFFECTVALUE) effectvalue = buffer[pos++]; _dumb_it_xm_convert_effect(effect, effectvalue, entry, 0); entry++; } channel++; if (channel >= n_channels) { channel = 0; row++; IT_SET_END_ROW(entry); entry++; } } while (row < pattern->n_rows) { row++; IT_SET_END_ROW(entry); entry++; } return 0;} static int it_xm_make_envelope(IT_ENVELOPE *envelope, const unsigned short *data, int y_offset) { int i, pos, val; if (envelope->n_nodes > 12) { /* XXX TRACE("XM error: wrong number of envelope nodes (%d)\n", envelope->n_nodes); envelope->n_nodes = 0; return -1; */ envelope->n_nodes = 12; } if (envelope->sus_loop_start >= 12) envelope->flags &= ~IT_ENVELOPE_SUSTAIN_LOOP; if (envelope->loop_end >= 12) envelope->loop_end = 0; if (envelope->loop_start >= envelope->loop_end) envelope->flags &= ~IT_ENVELOPE_LOOP_ON; pos = 0; for (i = 0; i < envelope->n_nodes; i++) { envelope->node_t[i] = data[pos++]; val = data[pos++]; if (val > 64) { TRACE("XM error: out-of-range envelope node (node_y[%d]=%d)\n", i, val); /* FT2 seems to simply clip the value */ val = 64; } envelope->node_y[i] = (signed char)(val + y_offset); } return 0;} typedef struct LIMITED_XM LIMITED_XM; struct LIMITED_XM { unsigned char *buffered; long ptr, limit, allocated; DUMBFILE *remaining;}; static int limit_xm_resize(void *f, long n) { DUMBFILE *df = f; LIMITED_XM *lx = df->file; if (n < 0) return -1; 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((char *)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_end(void *f, long n) { DUMBFILE *df = f; LIMITED_XM *lx = df->file; return dumbfile_skip(lx->remaining, n);} static int limit_xm_skip(void *f, dumb_off_t 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 dumb_ssize_t limit_xm_getnc(char *ptr, size_t n, void *f) { LIMITED_XM *lx = f; dumb_ssize_t left; left = lx->allocated - lx->ptr; if ((dumb_ssize_t)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);} /* These two can be stubs since this implementation doesn't use seeking */static int limit_xm_seek(void *f, dumb_off_t n) { USED(f); USED(n); return 1;} static dumb_off_t limit_xm_get_size(void *f) { USED(f); return 0;} DUMBFILE_SYSTEM limit_xm_dfs = {NULL, limit_xm_skip, limit_xm_getc, limit_xm_getnc, limit_xm_close, limit_xm_seek, limit_xm_get_size}; 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; unsigned short vol_points[24]; unsigned short pan_points[24]; int i, type; const unsigned long max_size = 4 + 22 + 1 + 2 + 4 + 96 + 48 + 48 + 1 * 14 + 2 + 2; unsigned long skip_end = 0; /* Header size. Tends to be more than the actual size of the structure. * 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 = max_size; else if (size > max_size) { skip_end = size - max_size; size = max_size; } if (limit_xm_resize(f, size - 4) < 0) return -1; dumbfile_getnc((char *)instrument->name, 22, f); instrument->name[22] = 0; trim_whitespace((char *)instrument->name, 22); instrument->filename[0] = 0; dumbfile_skip(f, 1); /* Instrument type. Should be 0, but seems random. */ extra->n_samples = dumbfile_igetw(f); if (dumbfile_error(f) || (unsigned int)extra->n_samples > XM_MAX_SAMPLES_PER_INSTRUMENT) return -1; bytes_read = 4 + 22 + 1 + 2; if (extra->n_samples) { /* sample header size */ /*i = dumbfile_igetl(f); if (!i || i > 0x28) i = 0x28;*/ dumbfile_skip(f, 4); i = 0x28; extra->sample_header_size = i; /* sample map */Lines 1–500 of 1429. Load more
/* _______ ____ __ ___ ___ * \ _ \ \ / \ / \ \ / / ' ' ' * | | \ \ | | || | \/ | . . * | | | | | | || ||\ /| | * | | | | | | || || \/ | | ' ' ' * | | | | | | || || | | . . * | |_/ / \ \__// || | | * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque * / \ * / . \ * readxm.c - Code to read a Fast Tracker II / / \ \ * module from an open file. | < / \_ * | \/ /\ / * By Julien Cugniere. Some bits of code taken \_ / > / * from reads3m.c. | \ / / * | ' / * \__/ */ #include <stdlib.h> #include <string.h> #include <math.h> #include "dumb.h" #include "internal/it.h" #include "internal/dumbfile.h" /** TODO: * XM_TREMOLO doesn't sound quite right... * XM_SET_ENVELOPE_POSITION todo. * VIBRATO conversion needs to be checked (sample/effect/volume). Plus check that effect memory is correct when using XM_VOLSLIDE_VIBRATO. - sample vibrato (instrument vibrato) is now handled correctly. - entheh * XM_E_SET_VIBRATO/TREMOLO_CONTROL: effectvalue&4 -> don't retrig wave when a new instrument is played. In retrigger_note()?. Is it worth implementing? * Lossy fadeout approximation. 0..31 converted to 0 --> won't fade at all. * Replace DUMB's sawtooth by ramp_down/ramp_up. Update XM loader. * A lot of things need to be reset when the end of the song is reached. * It seems that IT and XM don't behave the same way when dealing with mixed loops. When IT encounters multiple SBx (x>0) commands on the same row, it decrements the loop count for all, but only execute the loop of the last one (highest channel). FT2 only decrements the loop count of the last one. Not that I know of any modules using so convoluted combinations! * Maybe we could remove patterns that don't appear in the order table ? Or provide a function to "optimize" a DUMB_IT_SIGDATA ? */ #define XM_LINEAR_FREQUENCY 1 /* otherwise, use amiga slides */ #define XM_ENTRY_PACKED 128 #define XM_ENTRY_NOTE 1 #define XM_ENTRY_INSTRUMENT 2 #define XM_ENTRY_VOLUME 4 #define XM_ENTRY_EFFECT 8 #define XM_ENTRY_EFFECTVALUE 16 #define XM_NOTE_OFF 97 #define XM_ENVELOPE_ON 1 #define XM_ENVELOPE_SUSTAIN 2 #define XM_ENVELOPE_LOOP 4 #define XM_SAMPLE_NO_LOOP 0 #define XM_SAMPLE_FORWARD_LOOP 1 #define XM_SAMPLE_PINGPONG_LOOP 2 #define XM_SAMPLE_16BIT 16 #define XM_SAMPLE_STEREO 32 #define XM_VIBRATO_SINE 0 #define XM_VIBRATO_SQUARE 1 #define XM_VIBRATO_RAMP_DOWN 2 #define XM_VIBRATO_RAMP_UP 3 /* Probably useless :) */ const char xm_convert_vibrato[] = {IT_VIBRATO_SINE, IT_VIBRATO_XM_SQUARE, IT_VIBRATO_RAMP_DOWN, IT_VIBRATO_RAMP_UP, IT_VIBRATO_RANDOM}; #define XM_MAX_SAMPLES_PER_INSTRUMENT 16 /* Extra data that doesn't fit inside IT_INSTRUMENT */ typedef struct XM_INSTRUMENT_EXTRA { int n_samples; int vibrato_type; int vibrato_sweep; /* 0-0xFF */ int vibrato_depth; /* 0-0x0F */ int vibrato_speed; /* 0-0x3F */ int sample_header_size; } XM_INSTRUMENT_EXTRA; /* Trims off trailing white space, usually added by the tracker on file creation */ static void trim_whitespace(char *ptr, size_t size) { char *p = ptr + size - 1; while (p >= ptr && *p <= 0x20) *p-- = '\0'; } /* Frees the original block if it can't resize it or if size is 0, and acts * as malloc if ptr is NULL. */ static void *safe_realloc(void *ptr, size_t size) { if (ptr == NULL) return malloc(size); if (size == 0) { free(ptr); return NULL; } else { void *new_block = realloc(ptr, size); if (!new_block) free(ptr); return new_block; } } /* The interpretation of the XM volume column is left to the player. Here, we * just filter bad values. */ // This function is so tiny now, should we inline it? static void it_xm_convert_volume(int volume, IT_ENTRY *entry) { entry->mask |= IT_ENTRY_VOLPAN; entry->volpan = volume; switch (volume >> 4) { case 0xA: /* set vibrato speed */ case 0xB: /* vibrato */ case 0xF: /* tone porta */ case 0x6: /* vol slide up */ case 0x7: /* vol slide down */ case 0x8: /* fine vol slide up */ case 0x9: /* fine vol slide down */ case 0xC: /* set panning */ case 0xD: /* pan slide left */ case 0xE: /* pan slide right */ case 0x1: /* set volume */ case 0x2: /* set volume */ case 0x3: /* set volume */ case 0x4: /* set volume */ break; case 0x5: if (volume == 0x50) break; /* set volume */ /* else fall through */ default: entry->mask &= ~IT_ENTRY_VOLPAN; break; } } static int it_xm_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int n_channels, unsigned char *buffer, int version) { int size; int pos; int channel; int row; int effect, effectvalue; IT_ENTRY *entry; /* pattern header size */ if (dumbfile_igetl(f) != (version == 0x0102 ? 0x08 : 0x09)) { TRACE("XM error: unexpected pattern header size\n"); return -1; } /* pattern data packing type */ if (dumbfile_getc(f) != 0) { TRACE("XM error: unexpected pattern packing type\n"); return -1; } if (version == 0x0102) pattern->n_rows = dumbfile_getc(f) + 1; else pattern->n_rows = dumbfile_igetw(f); /* 1..256 */ size = dumbfile_igetw(f); pattern->n_entries = 0; if (dumbfile_error(f)) return -1; if (size == 0) return 0; if (size > 1280 * n_channels) { TRACE("XM error: pattern data size > %d bytes\n", 1280 * n_channels); return -1; } if (dumbfile_getnc((char *)buffer, size, f) < size) return -1; /* compute number of entries */ pattern->n_entries = 0; pos = channel = row = 0; while (pos < size) { if (!(buffer[pos] & XM_ENTRY_PACKED) || (buffer[pos] & 31)) pattern->n_entries++; channel++; if (channel >= n_channels) { channel = 0; row++; pattern->n_entries++; } if (buffer[pos] & XM_ENTRY_PACKED) { static const char offset[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5}; pos += 1 + offset[buffer[pos] & 31]; } else { pos += 5; } } if (row > pattern->n_rows) { TRACE("XM error: wrong number of rows in pattern data\n"); return -1; } /* Whoops, looks like some modules may be short, a few channels, maybe even * rows... */ while (row < pattern->n_rows) { pattern->n_entries++; row++; } pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry)); if (!pattern->entry) return -1; /* read the entries */ entry = pattern->entry; pos = channel = row = 0; while (pos < size) { unsigned char mask; if (buffer[pos] & XM_ENTRY_PACKED) mask = buffer[pos++] & 31; else mask = 31; if (mask) { ASSERT(entry < pattern->entry + pattern->n_entries); entry->channel = channel; entry->mask = 0; if (mask & XM_ENTRY_NOTE) { int note = buffer[pos++]; /* 1-96 <=> C0-B7 */ entry->note = (note == XM_NOTE_OFF) ? (IT_NOTE_OFF) : (note - 1); entry->mask |= IT_ENTRY_NOTE; } if (mask & XM_ENTRY_INSTRUMENT) { entry->instrument = buffer[pos++]; /* 1-128 */ entry->mask |= IT_ENTRY_INSTRUMENT; } if (mask & XM_ENTRY_VOLUME) it_xm_convert_volume(buffer[pos++], entry); effect = effectvalue = 0; if (mask & XM_ENTRY_EFFECT) effect = buffer[pos++]; if (mask & XM_ENTRY_EFFECTVALUE) effectvalue = buffer[pos++]; _dumb_it_xm_convert_effect(effect, effectvalue, entry, 0); entry++; } channel++; if (channel >= n_channels) { channel = 0; row++; IT_SET_END_ROW(entry); entry++; } } while (row < pattern->n_rows) { row++; IT_SET_END_ROW(entry); entry++; } return 0; } static int it_xm_make_envelope(IT_ENVELOPE *envelope, const unsigned short *data, int y_offset) { int i, pos, val; if (envelope->n_nodes > 12) { /* XXX TRACE("XM error: wrong number of envelope nodes (%d)\n", envelope->n_nodes); envelope->n_nodes = 0; return -1; */ envelope->n_nodes = 12; } if (envelope->sus_loop_start >= 12) envelope->flags &= ~IT_ENVELOPE_SUSTAIN_LOOP; if (envelope->loop_end >= 12) envelope->loop_end = 0; if (envelope->loop_start >= envelope->loop_end) envelope->flags &= ~IT_ENVELOPE_LOOP_ON; pos = 0; for (i = 0; i < envelope->n_nodes; i++) { envelope->node_t[i] = data[pos++]; val = data[pos++]; if (val > 64) { TRACE("XM error: out-of-range envelope node (node_y[%d]=%d)\n", i, val); /* FT2 seems to simply clip the value */ val = 64; } envelope->node_y[i] = (signed char)(val + y_offset); } return 0; } typedef struct LIMITED_XM LIMITED_XM; struct LIMITED_XM { unsigned char *buffered; long ptr, limit, allocated; DUMBFILE *remaining; }; static int limit_xm_resize(void *f, long n) { DUMBFILE *df = f; LIMITED_XM *lx = df->file; if (n < 0) return -1; 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((char *)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_end(void *f, long n) { DUMBFILE *df = f; LIMITED_XM *lx = df->file; return dumbfile_skip(lx->remaining, n); } static int limit_xm_skip(void *f, dumb_off_t 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 dumb_ssize_t limit_xm_getnc(char *ptr, size_t n, void *f) { LIMITED_XM *lx = f; dumb_ssize_t left; left = lx->allocated - lx->ptr; if ((dumb_ssize_t)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); } /* These two can be stubs since this implementation doesn't use seeking */ static int limit_xm_seek(void *f, dumb_off_t n) { USED(f); USED(n); return 1; } static dumb_off_t limit_xm_get_size(void *f) { USED(f); return 0; } DUMBFILE_SYSTEM limit_xm_dfs = {NULL, limit_xm_skip, limit_xm_getc, limit_xm_getnc, limit_xm_close, limit_xm_seek, limit_xm_get_size}; 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; unsigned short vol_points[24]; unsigned short pan_points[24]; int i, type; const unsigned long max_size = 4 + 22 + 1 + 2 + 4 + 96 + 48 + 48 + 1 * 14 + 2 + 2; unsigned long skip_end = 0; /* Header size. Tends to be more than the actual size of the structure. * 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 = max_size; else if (size > max_size) { skip_end = size - max_size; size = max_size; } if (limit_xm_resize(f, size - 4) < 0) return -1; dumbfile_getnc((char *)instrument->name, 22, f); instrument->name[22] = 0; trim_whitespace((char *)instrument->name, 22); instrument->filename[0] = 0; dumbfile_skip(f, 1); /* Instrument type. Should be 0, but seems random. */ extra->n_samples = dumbfile_igetw(f); if (dumbfile_error(f) || (unsigned int)extra->n_samples > XM_MAX_SAMPLES_PER_INSTRUMENT) return -1; bytes_read = 4 + 22 + 1 + 2; if (extra->n_samples) { /* sample header size */ /*i = dumbfile_igetl(f); if (!i || i > 0x28) i = 0x28;*/ dumbfile_skip(f, 4); i = 0x28; extra->sample_header_size = i; /* sample map */