2013-09-28 00:24:23 -03:00
|
|
|
/* _______ ____ __ ___ ___
|
|
|
|
* \ _ \ \ / \ / \ \ / / ' ' '
|
|
|
|
* | | \ \ | | || | \/ | . .
|
|
|
|
* | | | | | | || ||\ /| |
|
|
|
|
* | | | | | | || || \/ | | ' ' '
|
|
|
|
* | | | | | | || || | | . .
|
|
|
|
* | |_/ / \ \__// || | |
|
|
|
|
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
|
|
|
* / \
|
|
|
|
* / . \
|
|
|
|
* readam.c - Code to read a RIFF DSMF module / / \ \
|
|
|
|
* from a parsed RIFF structure. | < / \_
|
|
|
|
* | \/ /\ /
|
2017-10-08 22:48:02 -03:00
|
|
|
* By Christopher Snowhill. \_ / > /
|
2013-09-28 00:24:23 -03:00
|
|
|
* | \ / /
|
|
|
|
* | ' /
|
|
|
|
* \__/
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "dumb.h"
|
|
|
|
#include "internal/it.h"
|
|
|
|
#include "internal/riff.h"
|
|
|
|
|
2017-09-26 20:11:54 -03:00
|
|
|
static int it_riff_dsmf_process_sample(IT_SAMPLE *sample, DUMBFILE *f,
|
|
|
|
int len) {
|
|
|
|
int flags;
|
|
|
|
|
|
|
|
dumbfile_getnc((char *)sample->filename, 13, f);
|
|
|
|
sample->filename[14] = 0;
|
|
|
|
|
|
|
|
flags = dumbfile_igetw(f);
|
|
|
|
sample->default_volume = dumbfile_getc(f);
|
|
|
|
sample->length = dumbfile_igetl(f);
|
|
|
|
sample->loop_start = dumbfile_igetl(f);
|
|
|
|
sample->loop_end = dumbfile_igetl(f);
|
|
|
|
dumbfile_skip(f, 32 - 28);
|
|
|
|
sample->C5_speed = dumbfile_igetw(f) * 2;
|
|
|
|
dumbfile_skip(f, 36 - 34);
|
|
|
|
dumbfile_getnc((char *)sample->name, 28, f);
|
|
|
|
sample->name[28] = 0;
|
|
|
|
|
|
|
|
/*if ( data[ 0x38 ] || data[ 0x39 ] || data[ 0x3A ] || data[ 0x3B ] )
|
|
|
|
return -1;*/
|
|
|
|
|
|
|
|
if (!sample->length) {
|
|
|
|
sample->flags &= ~IT_SAMPLE_EXISTS;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*if ( flags & ~( 2 | 1 ) )
|
|
|
|
return -1;*/
|
|
|
|
|
|
|
|
if (sample->length + 64 > len)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
sample->flags = IT_SAMPLE_EXISTS;
|
|
|
|
|
|
|
|
sample->default_pan = 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 & 1) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sample->data = malloc(sample->length);
|
|
|
|
if (!sample->data)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
dumbfile_getnc(sample->data, sample->length, f);
|
|
|
|
|
|
|
|
if (!(flags & 2)) {
|
|
|
|
for (flags = 0; flags < sample->length; ++flags)
|
|
|
|
((signed char *)sample->data)[flags] ^= 0x80;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2013-09-28 00:24:23 -03:00
|
|
|
}
|
|
|
|
|
2017-09-26 20:11:54 -03:00
|
|
|
static int it_riff_dsmf_process_pattern(IT_PATTERN *pattern, DUMBFILE *f,
|
|
|
|
int len) {
|
2013-09-28 00:24:23 -03:00
|
|
|
int length, row;
|
2017-09-26 20:11:54 -03:00
|
|
|
unsigned flags;
|
2013-09-28 00:24:23 -03:00
|
|
|
long start, end;
|
|
|
|
int p, q, r;
|
2017-09-26 20:11:54 -03:00
|
|
|
IT_ENTRY *entry;
|
2013-09-28 00:24:23 -03:00
|
|
|
|
2017-09-26 20:11:54 -03:00
|
|
|
length = dumbfile_igetw(f);
|
|
|
|
if (length > len)
|
|
|
|
return -1;
|
2013-09-28 00:24:23 -03:00
|
|
|
|
2017-09-26 20:11:54 -03:00
|
|
|
len = length - 2;
|
2013-09-28 00:24:23 -03:00
|
|
|
|
2017-09-26 20:11:54 -03:00
|
|
|
pattern->n_rows = 64;
|
|
|
|
pattern->n_entries = 64;
|
2013-09-28 00:24:23 -03:00
|
|
|
|
2017-09-26 20:11:54 -03:00
|
|
|
row = 0;
|
2013-09-28 00:24:23 -03:00
|
|
|
|
2017-09-26 20:11:54 -03:00
|
|
|
start = dumbfile_pos(f);
|
2013-09-28 00:24:23 -03:00
|
|
|
end = start + len;
|
|
|
|
|
2017-09-26 20:11:54 -03:00
|
|
|
while ((row < 64) && !dumbfile_error(f) && (dumbfile_pos(f) < end)) {
|
|
|
|
p = dumbfile_getc(f);
|
|
|
|
if (!p) {
|
|
|
|
++row;
|
|
|
|
continue;
|
|
|
|
}
|
2013-09-28 00:24:23 -03:00
|
|
|
|
|
|
|
flags = p & 0xF0;
|
|
|
|
|
2017-09-26 20:11:54 -03:00
|
|
|
if (flags) {
|
|
|
|
++pattern->n_entries;
|
|
|
|
if (flags & 0x80)
|
|
|
|
dumbfile_skip(f, 1);
|
|
|
|
if (flags & 0x40)
|
|
|
|
dumbfile_skip(f, 1);
|
|
|
|
if (flags & 0x20)
|
|
|
|
dumbfile_skip(f, 1);
|
|
|
|
if (flags & 0x10)
|
|
|
|
dumbfile_skip(f, 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pattern->n_entries == 64)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry));
|
|
|
|
if (!pattern->entry)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
entry = pattern->entry;
|
|
|
|
|
|
|
|
row = 0;
|
|
|
|
|
|
|
|
if (dumbfile_seek(f, start, DFS_SEEK_SET))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
while ((row < 64) && !dumbfile_error(f) && (dumbfile_pos(f) < end)) {
|
|
|
|
p = dumbfile_getc(f);
|
|
|
|
if (!p) {
|
|
|
|
IT_SET_END_ROW(entry);
|
|
|
|
++entry;
|
|
|
|
++row;
|
|
|
|
continue;
|
|
|
|
}
|
2013-09-28 00:24:23 -03:00
|
|
|
|
|
|
|
flags = p;
|
2017-09-26 20:11:54 -03:00
|
|
|
entry->channel = flags & 0x0F;
|
|
|
|
entry->mask = 0;
|
|
|
|
|
|
|
|
if (flags & 0xF0) {
|
|
|
|
if (flags & 0x80) {
|
|
|
|
q = dumbfile_getc(f);
|
|
|
|
if (q) {
|
|
|
|
entry->mask |= IT_ENTRY_NOTE;
|
2013-09-28 00:24:23 -03:00
|
|
|
entry->note = q - 1;
|
2017-09-26 20:11:54 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & 0x40) {
|
|
|
|
q = dumbfile_getc(f);
|
|
|
|
if (q) {
|
|
|
|
entry->mask |= IT_ENTRY_INSTRUMENT;
|
2013-09-28 00:24:23 -03:00
|
|
|
entry->instrument = q;
|
2017-09-26 20:11:54 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & 0x20) {
|
|
|
|
entry->mask |= IT_ENTRY_VOLPAN;
|
|
|
|
entry->volpan = dumbfile_getc(f);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & 0x10) {
|
|
|
|
q = dumbfile_getc(f);
|
|
|
|
r = dumbfile_getc(f);
|
|
|
|
_dumb_it_xm_convert_effect(q, r, entry, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entry->mask)
|
|
|
|
entry++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (row < 64) {
|
|
|
|
IT_SET_END_ROW(entry);
|
|
|
|
++entry;
|
|
|
|
++row;
|
|
|
|
}
|
|
|
|
|
|
|
|
pattern->n_entries = (int)((long)entry - (long)pattern->entry);
|
|
|
|
if (!pattern->n_entries)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
2013-09-28 00:24:23 -03:00
|
|
|
}
|
|
|
|
|
2017-09-26 20:11:54 -03:00
|
|
|
static DUMB_IT_SIGDATA *it_riff_dsmf_load_sigdata(DUMBFILE *f,
|
|
|
|
struct riff *stream) {
|
|
|
|
DUMB_IT_SIGDATA *sigdata;
|
|
|
|
|
|
|
|
int n, o, found;
|
|
|
|
|
|
|
|
if (!stream)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (stream->type != DUMB_ID('D', 'S', 'M', '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('S', 'O', 'N', 'G'):
|
|
|
|
/* initialization data */
|
|
|
|
if ((found) || (c->size < 192))
|
|
|
|
goto error_sd;
|
|
|
|
found = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DUMB_ID('P', 'A', 'T', 'T'):
|
|
|
|
++sigdata->n_patterns;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DUMB_ID('I', 'N', 'S', 'T'):
|
|
|
|
++sigdata->n_samples;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found || !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('S', 'O', 'N', 'G'):
|
|
|
|
if (dumbfile_seek(f, c->offset, DFS_SEEK_SET))
|
|
|
|
goto error_usd;
|
|
|
|
dumbfile_getnc((char *)sigdata->name, 28, f);
|
|
|
|
sigdata->name[28] = 0;
|
|
|
|
sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX;
|
|
|
|
dumbfile_skip(f, 36 - 28);
|
|
|
|
sigdata->n_orders = dumbfile_igetw(f);
|
2017-10-08 22:48:02 -03:00
|
|
|
if (sigdata->n_orders > 1024) // Whoa, nelly.
|
|
|
|
goto error_usd;
|
2017-09-26 20:11:54 -03:00
|
|
|
// sigdata->n_samples = ptr[ 38 ] | ( ptr[ 39 ] << 8 ); // whatever
|
|
|
|
// sigdata->n_patterns = ptr[ 40 ] | ( ptr[ 41 ] << 8 );
|
|
|
|
dumbfile_skip(f, 42 - 38);
|
|
|
|
sigdata->n_pchannels = dumbfile_igetw(f);
|
|
|
|
sigdata->global_volume = dumbfile_getc(f);
|
|
|
|
sigdata->mixing_volume = dumbfile_getc(f);
|
|
|
|
sigdata->speed = dumbfile_getc(f);
|
|
|
|
sigdata->tempo = dumbfile_getc(f);
|
|
|
|
|
|
|
|
for (o = 0; o < 16; ++o) {
|
|
|
|
sigdata->channel_pan[o] = dumbfile_getc(f) / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
sigdata->order = malloc(128);
|
|
|
|
if (!sigdata->order)
|
|
|
|
goto error_usd;
|
|
|
|
dumbfile_getnc((char *)sigdata->order, 128, f);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
sigdata->n_samples = 0;
|
|
|
|
sigdata->n_patterns = 0;
|
|
|
|
|
|
|
|
for (n = 0; (unsigned)n < stream->chunk_count; ++n) {
|
|
|
|
struct riff_chunk *c = stream->chunks + n;
|
|
|
|
switch (c->type) {
|
|
|
|
case DUMB_ID('P', 'A', 'T', 'T'):
|
|
|
|
if (dumbfile_seek(f, c->offset, DFS_SEEK_SET))
|
|
|
|
goto error_usd;
|
|
|
|
if (it_riff_dsmf_process_pattern(
|
|
|
|
sigdata->pattern + sigdata->n_patterns, f, c->size))
|
|
|
|
goto error_usd;
|
|
|
|
++sigdata->n_patterns;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DUMB_ID('I', 'N', 'S', 'T'):
|
|
|
|
if (dumbfile_seek(f, c->offset, DFS_SEEK_SET))
|
|
|
|
goto error_usd;
|
|
|
|
if (it_riff_dsmf_process_sample(
|
|
|
|
sigdata->sample + sigdata->n_samples, f, c->size))
|
|
|
|
goto error_usd;
|
|
|
|
++sigdata->n_samples;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-08 22:48:02 -03:00
|
|
|
if (_dumb_it_fix_invalid_orders(sigdata) < 0) {
|
|
|
|
_dumb_it_unload_sigdata(sigdata);
|
|
|
|
return NULL;
|
|
|
|
}
|
2017-09-26 20:11:54 -03:00
|
|
|
|
|
|
|
return sigdata;
|
2013-09-28 00:24:23 -03:00
|
|
|
|
|
|
|
error_usd:
|
2017-09-26 20:11:54 -03:00
|
|
|
_dumb_it_unload_sigdata(sigdata);
|
|
|
|
goto error;
|
2013-09-28 00:24:23 -03:00
|
|
|
error_sd:
|
2017-09-26 20:11:54 -03:00
|
|
|
free(sigdata);
|
2013-09-28 00:24:23 -03:00
|
|
|
error:
|
2017-09-26 20:11:54 -03:00
|
|
|
return NULL;
|
2013-09-28 00:24:23 -03:00
|
|
|
}
|
|
|
|
|
2017-09-26 20:11:54 -03:00
|
|
|
DUH *dumb_read_riff_dsmf(DUMBFILE *f, struct riff *stream) {
|
|
|
|
sigdata_t *sigdata;
|
2013-09-28 00:24:23 -03:00
|
|
|
|
2017-09-26 20:11:54 -03:00
|
|
|
DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
|
2013-09-28 00:24:23 -03:00
|
|
|
|
2017-09-26 20:11:54 -03:00
|
|
|
sigdata = it_riff_dsmf_load_sigdata(f, stream);
|
2013-09-28 00:24:23 -03:00
|
|
|
|
2017-09-26 20:11:54 -03:00
|
|
|
if (!sigdata)
|
|
|
|
return NULL;
|
2013-09-28 00:24:23 -03:00
|
|
|
|
2017-09-26 20:11:54 -03:00
|
|
|
{
|
|
|
|
const char *tag[2][2];
|
|
|
|
tag[0][0] = "TITLE";
|
2013-09-28 00:24:23 -03:00
|
|
|
tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name);
|
2017-09-26 20:11:54 -03:00
|
|
|
tag[1][0] = "FORMAT";
|
|
|
|
tag[1][1] = "RIFF DSMF";
|
|
|
|
return make_duh(-1, 2, (const char *const(*)[2])tag, 1, &descptr,
|
|
|
|
&sigdata);
|
|
|
|
}
|
2013-09-28 00:24:23 -03:00
|
|
|
}
|