ref: ef707958359b91d9cc34a29f4f5328a069a4c1ce
dir: /src/it/readam.c/
/* _______ ____ __ ___ ___ * \ _ \ \ / \ / \ \ / / ' ' ' * | | \ \ | | || | \/ | . . * | | | | | | || ||\ /| | * | | | | | | || || \/ | | ' ' ' * | | | | | | || || | | . . * | |_/ / \ \__// || | | * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque * / \ * / . \ * readam.c - Code to read a RIFF AM module / / \ \ * from a parsed RIFF structure. | < / \_ * | \/ /\ / * By Christopher Snowhill. \_ / > / * | \ / / * | ' / * \__/ */ #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, DUMBFILE *f, size_t len, int ver) { size_t header_length; int default_pan; int default_volume; int flags; size_t length; size_t length_bytes; size_t loop_start; size_t loop_end; size_t sample_rate; long start = dumbfile_pos(f); if (ver == 0) { if (len < 0x38) return -1; header_length = 0x38; dumbfile_getnc((char *)sample->name, 28, f); sample->name[28] = 0; default_pan = dumbfile_getc(f); default_volume = dumbfile_getc(f); flags = dumbfile_igetw(f); length = dumbfile_igetl(f); loop_start = dumbfile_igetl(f); loop_end = dumbfile_igetl(f); sample_rate = dumbfile_igetl(f); } else { if (len < 4) return -1; header_length = dumbfile_igetl(f); if (header_length < 0x40) return -1; if (header_length + 4 > len) return -1; start += 4; len -= 4; dumbfile_getnc((char *)sample->name, 32, f); default_pan = dumbfile_igetw(f); default_volume = dumbfile_igetw(f); flags = dumbfile_igetw(f); dumbfile_skip(f, 2); length = dumbfile_igetl(f); loop_start = dumbfile_igetl(f); loop_end = dumbfile_igetl(f); sample_rate = dumbfile_igetl(f); if (default_pan > 0x7FFF || default_volume > 0x7FFF) return -1; default_pan = default_pan * 64 / 32767; default_volume = default_volume * 64 / 32767; } if (!length) { sample->flags &= ~IT_SAMPLE_EXISTS; return 0; } if (flags & ~(0x8000 | 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->finetune = 0; 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; } } length_bytes = sample->length << ((flags & 0x04) >> 2); sample->data = malloc(length_bytes); if (!sample->data) return -1; if (dumbfile_seek(f, start + header_length, DFS_SEEK_SET)) return -1; dumbfile_getnc(sample->data, length_bytes, f); return 0; } static int it_riff_am_process_pattern(IT_PATTERN *pattern, DUMBFILE *f, size_t len, int ver) { int nrows, row; long start, end; unsigned flags; int p, q, r; IT_ENTRY *entry; nrows = dumbfile_getc(f) + 1; pattern->n_rows = nrows; len -= 1; pattern->n_entries = 0; row = 0; start = dumbfile_pos(f); end = start + len; while ((row < nrows) && !dumbfile_error(f) && (dumbfile_pos(f) < end)) { p = dumbfile_getc(f); if (!p) { ++row; continue; } flags = p & 0xE0; if (flags) { ++pattern->n_entries; if (flags & 0x80) dumbfile_skip(f, 2); if (flags & 0x40) dumbfile_skip(f, 2); if (flags & 0x20) dumbfile_skip(f, 1); } } 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; dumbfile_seek(f, start, DFS_SEEK_SET); while ((row < nrows) && !dumbfile_error(f) && (dumbfile_pos(f) < end)) { p = dumbfile_getc(f); if (!p) { IT_SET_END_ROW(entry); ++entry; ++row; continue; } flags = p; entry->channel = flags & 0x1F; entry->mask = 0; if (flags & 0xE0) { if (flags & 0x80) { q = dumbfile_getc(f); r = dumbfile_getc(f); _dumb_it_xm_convert_effect(r, q, entry, 0); } if (flags & 0x40) { q = dumbfile_getc(f); r = dumbfile_getc(f); if (q) { entry->mask |= IT_ENTRY_INSTRUMENT; entry->instrument = q; } if (r) { entry->mask |= IT_ENTRY_NOTE; entry->note = r - 1; } } if (flags & 0x20) { q = dumbfile_getc(f); entry->mask |= IT_ENTRY_VOLPAN; if (ver == 0) entry->volpan = q; else entry->volpan = q * 64 / 127; } if (entry->mask) entry++; } } while (row < nrows) { IT_SET_END_ROW(entry); ++entry; ++row; } pattern->n_entries = (int)(entry - pattern->entry); if (!pattern->n_entries) return -1; return 0; } static DUMB_IT_SIGDATA *it_riff_amff_load_sigdata(DUMBFILE *f, struct riff *stream) { DUMB_IT_SIGDATA *sigdata; int n, found; int o, p; 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; (unsigned)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'): if (dumbfile_seek(f, c->offset, DFS_SEEK_SET)) goto error_sd; o = dumbfile_getc(f); if (o >= sigdata->n_patterns) sigdata->n_patterns = (o + 1); o = (int)dumbfile_igetl(f); if ((unsigned)o + 5 > c->size) goto error_sd; break; case DUMB_ID('I', 'N', 'S', 'T'): { if (c->size < 0xE1) goto error_sd; if (dumbfile_seek(f, c->offset + 1, DFS_SEEK_SET)) goto error_sd; o = dumbfile_getc(f); if (o >= sigdata->n_samples) sigdata->n_samples = (int)(o + 1); if (c->size >= 0x121) { if (dumbfile_seek(f, c->offset + 0xE1, DFS_SEEK_SET)) goto error_sd; if (dumbfile_mgetl(f) == DUMB_ID('S', 'A', 'M', 'P')) { size_t size = dumbfile_igetl(f); if (size + 0xE1 + 8 > c->size) goto error_sd; } } } 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; sigdata->restart_position = 0; memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) { int sep = 32 * dumb_it_default_panning_separation / 100; sigdata->channel_pan[n] = 32 - sep; sigdata->channel_pan[n + 1] = 32 + sep; sigdata->channel_pan[n + 2] = 32 + sep; sigdata->channel_pan[n + 3] = 32 - sep; } for (n = 0; (unsigned)n < stream->chunk_count; ++n) { struct riff_chunk *c = stream->chunks + n; switch (c->type) { case DUMB_ID('M', 'A', 'I', 'N'): if (dumbfile_seek(f, c->offset, DFS_SEEK_SET)) goto error_usd; dumbfile_getnc((char *)sigdata->name, 64, f); sigdata->name[64] = 0; sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M; o = dumbfile_getc(f); if (!(o & 1)) sigdata->flags |= IT_LINEAR_SLIDES; if ((o & ~3) || !(o & 2)) goto error_usd; // unknown flags sigdata->n_pchannels = dumbfile_getc(f); sigdata->speed = dumbfile_getc(f); sigdata->tempo = dumbfile_getc(f); dumbfile_skip(f, 4); // min period, max period, 16x amiga periods, often invalid sigdata->global_volume = dumbfile_getc(f); if (c->size < 0x48 + (unsigned)sigdata->n_pchannels) goto error_usd; for (o = 0; o < sigdata->n_pchannels; ++o) { p = dumbfile_getc(f); sigdata->channel_pan[o] = p; if (p >= 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->data = NULL; sample->flags = 0; sample->name[0] = 0; } for (n = 0; (unsigned)n < stream->chunk_count; ++n) { struct riff_chunk *c = stream->chunks + n; switch (c->type) { case DUMB_ID('O', 'R', 'D', 'R'): if (dumbfile_seek(f, c->offset, DFS_SEEK_SET)) goto error_usd; sigdata->n_orders = dumbfile_getc(f) + 1; if ((unsigned)sigdata->n_orders + 1 > c->size) goto error_usd; sigdata->order = malloc(sigdata->n_orders); if (!sigdata->order) goto error_usd; dumbfile_getnc((char *)sigdata->order, sigdata->n_orders, f); break; case DUMB_ID('P', 'A', 'T', 'T'): if (dumbfile_seek(f, c->offset, DFS_SEEK_SET)) goto error_usd; o = dumbfile_getc(f); p = (int)dumbfile_igetl(f); if (it_riff_am_process_pattern(sigdata->pattern + o, f, p, 0)) goto error_usd; break; case DUMB_ID('I', 'N', 'S', 'T'): { IT_SAMPLE *sample; if (dumbfile_seek(f, c->offset + 1, DFS_SEEK_SET)) goto error_usd; sample = sigdata->sample + dumbfile_getc(f); if (c->size >= 0x121) { if (dumbfile_seek(f, c->offset + 0xE1, DFS_SEEK_SET)) goto error_usd; if (dumbfile_mgetl(f) == DUMB_ID('S', 'A', 'M', 'P')) { size_t size = dumbfile_igetl(f); if (it_riff_am_process_sample(sample, f, size, 0)) goto error_usd; break; } } dumbfile_seek(f, c->offset + 2, DFS_SEEK_SET); dumbfile_getnc((char *)sample->name, 28, f); sample->name[28] = 0; } break; } } if (_dumb_it_fix_invalid_orders(sigdata) < 0) { _dumb_it_unload_sigdata(sigdata); return NULL; } 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(DUMBFILE *f, struct riff *stream) { DUMB_IT_SIGDATA *sigdata; int n, found; int o, p; if (!f || !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; (unsigned)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'): if (dumbfile_seek(f, c->offset, DFS_SEEK_SET)) goto error_sd; o = dumbfile_getc(f); if (o >= sigdata->n_patterns) sigdata->n_patterns = (int)(o + 1); o = (int)dumbfile_igetl(f); if ((unsigned)o + 5 > c->size) goto error_sd; break; case DUMB_ID('R', 'I', 'F', 'F'): { struct riff *str = c->nested; switch (str->type) { case DUMB_ID('A', 'I', ' ', ' '): for (o = 0; (unsigned)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; size_t size; unsigned sample_found; if (dumbfile_seek(f, chk->offset, DFS_SEEK_SET)) goto error_sd; size = dumbfile_igetl(f); if (size < 0x142) goto error_sd; sample_found = 0; dumbfile_skip(f, 1); p = dumbfile_getc(f); if (p >= sigdata->n_samples) sigdata->n_samples = (int)(p + 1); temp = riff_parse(f, chk->offset + 4 + size, chk->size - size - 4, 1); if (temp) { if (temp->type == DUMB_ID('A', 'S', ' ', ' ')) { for (p = 0; (unsigned)p < temp->chunk_count; ++p) { if (temp->chunks[p].type == DUMB_ID('S', 'A', 'M', 'P')) { if (sample_found) { riff_free(temp); goto error_sd; } 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; sigdata->restart_position = 0; memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) { int sep = 32 * dumb_it_default_panning_separation / 100; sigdata->channel_pan[n] = 32 - sep; sigdata->channel_pan[n + 1] = 32 + sep; sigdata->channel_pan[n + 2] = 32 + sep; sigdata->channel_pan[n + 3] = 32 - sep; } for (n = 0; (unsigned)n < stream->chunk_count; ++n) { struct riff_chunk *c = stream->chunks + n; switch (c->type) { case DUMB_ID('I', 'N', 'I', 'T'): if (dumbfile_seek(f, c->offset, DFS_SEEK_SET)) goto error_usd; dumbfile_getnc((char *)sigdata->name, 64, f); sigdata->name[64] = 0; sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M; o = dumbfile_getc(f); if (!(o & 1)) sigdata->flags |= IT_LINEAR_SLIDES; if ((o & ~3) || !(o & 2)) goto error_usd; // unknown flags sigdata->n_pchannels = dumbfile_getc(f); sigdata->speed = dumbfile_getc(f); sigdata->tempo = dumbfile_getc(f); dumbfile_skip(f, 4); sigdata->global_volume = dumbfile_getc(f); if (c->size < 0x48 + (unsigned)sigdata->n_pchannels) goto error_usd; for (o = 0; o < sigdata->n_pchannels; ++o) { p = dumbfile_getc(f); if (p <= 128) { sigdata->channel_pan[o] = p / 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->data = NULL; sample->flags = 0; sample->name[0] = 0; } for (n = 0; (unsigned)n < stream->chunk_count; ++n) { struct riff_chunk *c = stream->chunks + n; switch (c->type) { case DUMB_ID('O', 'R', 'D', 'R'): if (dumbfile_seek(f, c->offset, DFS_SEEK_SET)) goto error_usd; sigdata->n_orders = dumbfile_getc(f) + 1; if ((unsigned)sigdata->n_orders + 1 > c->size) goto error_usd; sigdata->order = malloc(sigdata->n_orders); if (!sigdata->order) goto error_usd; dumbfile_getnc((char *)sigdata->order, sigdata->n_orders, f); break; case DUMB_ID('P', 'A', 'T', 'T'): if (dumbfile_seek(f, c->offset, DFS_SEEK_SET)) goto error_usd; o = dumbfile_getc(f); p = (int)dumbfile_igetl(f); if (it_riff_am_process_pattern(sigdata->pattern + o, f, p, 1)) goto error_usd; break; case DUMB_ID('R', 'I', 'F', 'F'): { struct riff *str = c->nested; switch (str->type) { case DUMB_ID('A', 'I', ' ', ' '): for (o = 0; (unsigned)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; size_t size; unsigned sample_found; IT_SAMPLE *sample; if (dumbfile_seek(f, chk->offset, DFS_SEEK_SET)) goto error_usd; size = dumbfile_igetl(f); dumbfile_skip(f, 1); p = dumbfile_getc(f); temp = riff_parse(f, chk->offset + 4 + size, chk->size - size - 4, 1); sample_found = 0; sample = sigdata->sample + p; if (temp) { if (temp->type == DUMB_ID('A', 'S', ' ', ' ')) { for (p = 0; (unsigned)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 (dumbfile_seek(f, c->offset, DFS_SEEK_SET)) { riff_free(temp); goto error_usd; } if (it_riff_am_process_sample( sample, f, c->size, 1)) { riff_free(temp); goto error_usd; } sample_found = 1; } } } riff_free(temp); } if (!sample_found) { dumbfile_seek(f, chk->offset + 6, DFS_SEEK_SET); dumbfile_getnc((char *)sample->name, 32, f); 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(DUMBFILE *f, struct riff *stream) { sigdata_t *sigdata; long length; DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; sigdata = it_riff_amff_load_sigdata(f, 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] = (const char *)(((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(DUMBFILE *f, struct riff *stream) { sigdata_t *sigdata; DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; sigdata = it_riff_am_load_sigdata(f, stream); if (!sigdata) return NULL; { const char *tag[2][2]; tag[0][0] = "TITLE"; tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); tag[1][0] = "FORMAT"; tag[1][1] = "RIFF AM"; return make_duh(-1, 2, (const char *const(*)[2])tag, 1, &descptr, &sigdata); } }