Cog/Frameworks/Dumb/dumb/src/it/readoldpsm.c
2013-09-27 20:24:23 -07:00

688 lines
18 KiB
C

/* _______ ____ __ ___ ___
* \ _ \ \ / \ / \ \ / / ' ' '
* | | \ \ | | || | \/ | . .
* | | | | | | || ||\ /| |
* | | | | | | || || \/ | | ' ' '
* | | | | | | || || | | . .
* | |_/ / \ \__// || | |
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
* / \
* / . \
* readpsm.c - Code to read an old Protracker / / \ \
* Studio module from an open file. | < / \_
* | \/ /\ /
* By Chris Moeller. \_ / > /
* | \ / /
* | ' /
* \__/
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "dumb.h"
#include "internal/it.h"
static int psm_sample_compare(const void *e1, const void *e2)
{
const unsigned char * pa = e1;
const unsigned char * pb = e2;
int a = pa[37] | (pa[38] << 8) | (pa[39] << 16) | (pa[40] << 24);
int b = pb[37] | (pb[38] << 8) | (pb[39] << 16) | (pb[40] << 24);
return a - b;
}
static int it_old_psm_read_samples(IT_SAMPLE ** sample, DUMBFILE * f, int * num)
{
int n, o, count = *num, true_num, snum, offset, flags, finetune, delta;
unsigned char * buffer;
const unsigned char * sdata;
long sample_bytes;
buffer = malloc(count * 64);
if (!buffer) goto error;
if (dumbfile_getnc((char *)buffer, count * 64, f) < count * 64) goto error_fb;
true_num = 0;
for (n = 0; n < count; n++) {
snum = buffer[(n * 64) + 45] | (buffer[(n * 64) + 46] << 8);
if ((snum < 1) || (snum > 255)) goto error_fb;
if (true_num < snum) true_num = snum;
}
if (true_num > count) {
IT_SAMPLE * meh = realloc(*sample, true_num * sizeof(*meh));
if (!meh) goto error_fb;
for (n = count; n < true_num; n++) {
meh[n].data = NULL;
}
*sample = meh;
*num = true_num;
}
qsort(buffer, count, 64, &psm_sample_compare);
for (n = 0; n < true_num; n++) {
(*sample)[n].flags = 0;
}
for (n = 0; n < count; n++) {
IT_SAMPLE * s;
snum = buffer[(n * 64) + 45] | (buffer[(n * 64) + 46] << 8);
s = &((*sample)[snum - 1]);
memcpy(s->filename, buffer + (n * 64), 13);
s->filename[13] = 0;
memcpy(s->name, buffer + (n * 64) + 13, 24);
s->name[24] = 0;
offset = buffer[(n * 64) + 37] | (buffer[(n * 64) + 38] << 8) |
(buffer[(n * 64) + 39] << 16) | (buffer[(n * 64) + 40] << 24);
flags = buffer[(n * 64) + 47];
s->length = buffer[(n * 64) + 48] | (buffer[(n * 64) + 49] << 8) |
(buffer[(n * 64) + 50] << 16) | (buffer[(n * 64) + 51] << 24);
s->loop_start = buffer[(n * 64) + 52] | (buffer[(n * 64) + 53] << 8) |
(buffer[(n * 64) + 54] << 16) | (buffer[(n * 64) + 55] << 24);
s->loop_end = buffer[(n * 64) + 56] | (buffer[(n * 64) + 57] << 8) |
(buffer[(n * 64) + 58] << 16) | (buffer[(n * 64) + 59] << 24);
if (s->length <= 0) continue;
finetune = buffer[(n * 64) + 60];
s->default_volume = buffer[(n * 64) + 61];
s->C5_speed = buffer[(n * 64) + 62] | (buffer[(n * 64) + 63] << 8);
if (finetune & 15) {
finetune &= 15;
if (finetune >= 8) finetune -= 16;
//s->C5_speed = (long)((double)s->C5_speed * pow(DUMB_PITCH_BASE, finetune*32));
s->finetune = finetune * 32;
}
else s->finetune = 0;
s->flags |= IT_SAMPLE_EXISTS;
if (flags & 0x41) {
s->flags &= ~IT_SAMPLE_EXISTS;
continue;
}
if (flags & 0x20) s->flags |= IT_SAMPLE_PINGPONG_LOOP;
if (flags & 4) s->flags |= IT_SAMPLE_16BIT;
if (flags & 0x80) {
s->flags |= IT_SAMPLE_LOOP;
if ((unsigned int)s->loop_end > (unsigned int)s->length)
s->loop_end = s->length;
else if ((unsigned int)s->loop_start >= (unsigned int)s->loop_end)
s->flags &= ~IT_SAMPLE_LOOP;
else
s->length = s->loop_end;
}
s->global_volume = 64;
s->vibrato_speed = 0;
s->vibrato_depth = 0;
s->vibrato_rate = 0;
s->vibrato_waveform = IT_VIBRATO_SINE;
s->max_resampling_quality = -1;
sample_bytes = s->length * ((flags & 4) ? 2 : 1);
s->data = malloc(sample_bytes);
if (!s->data) goto error_fb;
if (dumbfile_seek(f, offset, DFS_SEEK_SET) || dumbfile_getnc(s->data, sample_bytes, f) < sample_bytes) goto error_fb;
sdata = ( const unsigned char * ) s->data;
if (flags & 0x10) {
if (flags & 8) {
if (flags & 4) {
for (o = 0; o < s->length; o++)
((short *)s->data)[o] = (sdata[o * 2] | (sdata[(o * 2) + 1] << 8)) ^ 0x8000;
} else {
for (o = 0; o < s->length; o++)
((signed char *)s->data)[o] = sdata[o] ^ 0x80;
}
} else {
if (flags & 4) {
for (o = 0; o < s->length; o++)
((short *)s->data)[o] = sdata[o * 2] | (sdata[(o * 2) + 1] << 8);
} else {
memcpy(s->data, sdata, s->length);
}
}
} else {
delta = 0;
if (flags & 8) {
/* unsigned delta? mehhh, does anything even use this? */
if (flags & 4) {
for (o = 0; o < s->length; o++) {
delta += (short)(sdata[o * 2] | (sdata[(o * 2) + 1] << 8));
((short *)s->data)[o] = delta ^ 0x8000;
}
} else {
for (o = 0; o < s->length; o++) {
delta += (signed char)sdata[o];
((signed char *)s->data)[o] = delta ^ 0x80;
}
}
} else {
if (flags & 4) {
for (o = 0; o < s->length; o++) {
delta += (short)(sdata[o * 2] | (sdata[(o * 2) + 1] << 8));
((short *)s->data)[o] = delta;
}
} else {
for (o = 0; o < s->length; o++) {
delta += (signed char)sdata[o];
((signed char *)s->data)[o] = delta;
}
}
}
}
}
free(buffer);
return 0;
error_fb:
free(buffer);
error:
return -1;
}
static int it_old_psm_read_patterns(IT_PATTERN * pattern, DUMBFILE * f, int num, int size, int pchans)
{
int n, offset, psize, rows, chans, row, flags, channel;
unsigned char * buffer, * ptr, * end;
IT_ENTRY * entry;
buffer = malloc(size);
if (!buffer) goto error;
if (dumbfile_getnc((char *)buffer, size, f) < size) goto error_fb;
offset = 0;
for (n = 0; n < num; n++) {
IT_PATTERN * p = &pattern[n];
if (offset >= size) goto error_fb;
ptr = buffer + offset;
psize = ptr[0] | (ptr[1] << 8);
rows = ptr[2];
chans = ptr[3];
if (!rows || !chans) {
p->n_rows = 1;
p->n_entries = 0;
continue;
}
psize = (psize + 15) & ~15;
end = ptr + psize;
ptr += 4;
p->n_rows = rows;
p->n_entries = rows;
row = 0;
while ((row < rows) && (ptr < end)) {
flags = *ptr++;
if (!flags) {
row++;
continue;
}
if (flags & 0xE0) {
p->n_entries++;
if (flags & 0x80) ptr += 2;
if (flags & 0x40) ptr++;
if (flags & 0x20) {
ptr++;
if (*ptr == 40) ptr += 3;
else ptr++;
}
}
}
entry = malloc(p->n_entries * sizeof(*p->entry));
if (!entry) goto error_fb;
p->entry = entry;
ptr = buffer + offset + 4;
row = 0;
while ((row < rows) && (ptr < end)) {
flags = *ptr++;
if (!flags) {
IT_SET_END_ROW(entry);
entry++;
row++;
continue;
}
if (flags & 0xE0) {
entry->mask = 0;
entry->channel = channel = flags & 0x1F;
if (channel >= chans)
{
//channel = 0;
//goto error_fb;
}
if (flags & 0x80) {
if ((*ptr < 60) && (channel < pchans)) {
entry->mask |= IT_ENTRY_NOTE;
entry->note = *ptr + 35;
}
ptr++;
if (*ptr) {
entry->mask |= IT_ENTRY_INSTRUMENT;
entry->instrument = *ptr;
}
ptr++;
}
if (flags & 0x40) {
if (*ptr <= 64) {
entry->mask |= IT_ENTRY_VOLPAN;
entry->volpan = *ptr;
}
ptr++;
}
if (flags & 0x20) {
entry->mask |= IT_ENTRY_EFFECT;
switch (*ptr) {
case 1:
entry->effect = IT_XM_FINE_VOLSLIDE_UP;
entry->effectvalue = ptr[1];
break;
case 2:
entry->effect = IT_VOLUME_SLIDE;
entry->effectvalue = (ptr[1] << 4) & 0xF0;
break;
case 3:
entry->effect = IT_XM_FINE_VOLSLIDE_DOWN;
entry->effectvalue = ptr[1];
break;
case 4:
entry->effect = IT_VOLUME_SLIDE;
entry->effectvalue = ptr[1] & 0xF;
break;
case 10:
entry->effect = IT_PORTAMENTO_UP;
entry->effectvalue = EFFECT_VALUE(0xF, ptr[1]);
break;
case 11:
entry->effect = IT_PORTAMENTO_UP;
entry->effectvalue = ptr[1];
break;
case 12:
entry->effect = IT_PORTAMENTO_DOWN;
entry->effectvalue = EFFECT_VALUE(ptr[1], 0xF);
break;
case 13:
entry->effect = IT_PORTAMENTO_DOWN;
entry->effectvalue = ptr[1];
break;
case 14:
entry->effect = IT_TONE_PORTAMENTO;
entry->effectvalue = ptr[1];
break;
case 15:
entry->effect = IT_S;
entry->effectvalue = EFFECT_VALUE(IT_S_SET_GLISSANDO_CONTROL, ptr[1] & 15);
break;
case 16:
entry->effect = IT_VOLSLIDE_TONEPORTA;
entry->effectvalue = ptr[1] << 4;
break;
case 17:
entry->effect = IT_VOLSLIDE_TONEPORTA;
entry->effectvalue = ptr[1] & 0xF;
break;
case 20:
entry->effect = IT_VIBRATO;
entry->effectvalue = ptr[1];
break;
case 21:
entry->effect = IT_S;
entry->effectvalue = EFFECT_VALUE(IT_S_SET_VIBRATO_WAVEFORM, ptr[1] & 11);
break;
case 22:
entry->effect = IT_VOLSLIDE_VIBRATO;
entry->effectvalue = ptr[1] << 4;
break;
case 23:
entry->effect = IT_VOLSLIDE_VIBRATO;
entry->effectvalue = ptr[1] & 0xF;
break;
case 30:
entry->effect = IT_TREMOLO;
entry->effectvalue = ptr[1];
break;
case 31:
entry->effect = IT_S;
entry->effectvalue = EFFECT_VALUE(IT_S_SET_TREMOLO_WAVEFORM, ptr[1] & 11);
break;
case 40:
entry->effect = IT_SET_SAMPLE_OFFSET;
entry->effectvalue = ptr[2];
ptr += 2;
break;
case 41:
entry->effect = IT_XM_RETRIGGER_NOTE;
entry->effectvalue = ptr[1];
break;
case 42:
entry->effect = IT_S;
entry->effectvalue = EFFECT_VALUE(IT_S_DELAYED_NOTE_CUT, ptr[1] & 0xF);
break;
case 43:
entry->effect = IT_S;
entry->effectvalue = EFFECT_VALUE(IT_S_NOTE_DELAY, ptr[1] & 0xF);
break;
case 50:
entry->effect = IT_JUMP_TO_ORDER;
entry->effectvalue = ptr[1];
break;
case 51:
entry->effect = IT_BREAK_TO_ROW;
entry->effectvalue = ptr[1];
break;
case 52:
entry->effect = IT_S;
entry->effectvalue = EFFECT_VALUE(IT_S_PATTERN_LOOP, ptr[1] & 0xF);
break;
case 53:
entry->effect = IT_S;
entry->effectvalue = EFFECT_VALUE(IT_S_PATTERN_DELAY, ptr[1] & 0xF);
break;
case 60:
entry->effect = IT_SET_SPEED;
entry->effectvalue = ptr[1];
break;
case 61:
entry->effect = IT_SET_SONG_TEMPO;
entry->effectvalue = ptr[1];
break;
case 70:
entry->effect = IT_ARPEGGIO;
entry->effectvalue = ptr[1];
break;
case 71:
entry->effect = IT_S;
entry->effectvalue = EFFECT_VALUE(IT_S_FINETUNE, ptr[1] & 0xF);
break;
case 72:
/* "balance" ... panning? */
entry->effect = IT_SET_PANNING;
entry->effectvalue = ((ptr[1] - ((ptr[1] & 8) >> 3)) << 5) / 7;
break;
default:
entry->mask &= ~IT_ENTRY_EFFECT;
}
ptr += 2;
}
if (entry->mask) entry++;
}
}
p->n_entries = entry - p->entry;
offset += psize;
}
free(buffer);
return 0;
error_fb:
free(buffer);
error:
return -1;
}
#define PSM_COMPONENT_ORDERS 0
#define PSM_COMPONENT_PANPOS 1
#define PSM_COMPONENT_PATTERNS 2
#define PSM_COMPONENT_SAMPLE_HEADERS 3
#define PSM_COMPONENT_COMMENTS 4
typedef struct PSM_COMPONENT
{
unsigned char type;
long offset;
}
PSM_COMPONENT;
static int psm_component_compare(const void *e1, const void *e2)
{
return ((const PSM_COMPONENT *)e1)->offset -
((const PSM_COMPONENT *)e2)->offset;
}
static DUMB_IT_SIGDATA *it_old_psm_load_sigdata(DUMBFILE *f)
{
DUMB_IT_SIGDATA *sigdata;
PSM_COMPONENT *component;
int n_components = 0;
int n, flags, version, pver, n_orders, n_channels, total_pattern_size;
if (dumbfile_mgetl(f) != DUMB_ID('P','S','M',254)) goto error;
sigdata = malloc(sizeof(*sigdata));
if (!sigdata) goto error;
if (dumbfile_getnc((char *)sigdata->name, 60, f) < 60 ||
sigdata->name[59] != 0x1A) goto error_sd;
sigdata->name[59] = 0;
flags = dumbfile_getc(f);
version = dumbfile_getc(f);
pver = dumbfile_getc(f);
sigdata->speed = dumbfile_getc(f);
sigdata->tempo = dumbfile_getc(f);
sigdata->mixing_volume = dumbfile_getc(f);
sigdata->n_orders = dumbfile_igetw(f);
n_orders = dumbfile_igetw(f);
sigdata->n_patterns = dumbfile_igetw(f);
sigdata->n_samples = dumbfile_igetw(f);
sigdata->n_pchannels = dumbfile_igetw(f);
n_channels = dumbfile_igetw(f);
if (dumbfile_error(f) ||
(flags & 1) ||
(version != 1 && version != 0x10) ||
(pver) ||
(sigdata->n_orders <= 0) ||
(sigdata->n_orders > 255) ||
(n_orders > 255) ||
(n_orders < sigdata->n_orders) ||
(sigdata->n_patterns > 255) ||
(sigdata->n_samples > 255) ||
(sigdata->n_pchannels > DUMB_IT_N_CHANNELS) ||
(sigdata->n_pchannels > n_channels) ||
(n_channels > DUMB_IT_N_CHANNELS))
goto error_sd;
sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX;
sigdata->global_volume = 128;
sigdata->pan_separation = 128;
sigdata->song_message = NULL;
sigdata->order = NULL;
sigdata->instrument = NULL;
sigdata->sample = NULL;
sigdata->pattern = NULL;
sigdata->midi = NULL;
sigdata->checkpoint = NULL;
sigdata->n_instruments = 0;
sigdata->restart_position = 0;
sigdata->order = malloc(sigdata->n_orders);
if (!sigdata->order) goto error_usd;
if (sigdata->n_samples) {
sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
if (!sigdata->sample) goto error_usd;
for (n = 0; n < sigdata->n_samples; n++)
sigdata->sample[n].data = NULL;
}
if (sigdata->n_patterns) {
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;
}
component = malloc(5 * sizeof(*component));
if (!component) goto error_usd;
for (n = 0; n < 5; n++) {
component[n_components].offset = dumbfile_igetl(f);
if (component[n_components].offset) {
component[n_components].type = n;
n_components++;
}
}
if (!n_components) goto error_fc;
total_pattern_size = dumbfile_igetl(f);
if (!total_pattern_size) goto error_fc;
qsort(component, n_components, sizeof(PSM_COMPONENT), &psm_component_compare);
memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) {
sigdata->channel_pan[n ] = 16;
sigdata->channel_pan[n+1] = 48;
sigdata->channel_pan[n+2] = 48;
sigdata->channel_pan[n+3] = 16;
}
for (n = 0; n < n_components; n++)
{
int o;
if ( dumbfile_seek(f, component[n].offset, DFS_SEEK_SET) ) goto error_fc;
switch (component[n].type) {
case PSM_COMPONENT_ORDERS:
if (dumbfile_getnc((char *)sigdata->order, sigdata->n_orders, f) < sigdata->n_orders) goto error_fc;
if (n_orders > sigdata->n_orders)
if (dumbfile_skip(f, n_orders - sigdata->n_orders))
goto error_fc;
if (dumbfile_igetw(f)) goto error_fc;
break;
case PSM_COMPONENT_PANPOS:
if (dumbfile_getnc((char *)sigdata->channel_pan, sigdata->n_pchannels, f) < sigdata->n_pchannels) goto error_fc;
for (o = 0; o < sigdata->n_pchannels; o++) {
sigdata->channel_pan[o] -= (sigdata->channel_pan[o] & 8) >> 3;
sigdata->channel_pan[o] = ((int)sigdata->channel_pan[o] << 5) / 7;
}
break;
case PSM_COMPONENT_PATTERNS:
if (it_old_psm_read_patterns(sigdata->pattern, f, sigdata->n_patterns, total_pattern_size, sigdata->n_pchannels)) goto error_fc;
break;
case PSM_COMPONENT_SAMPLE_HEADERS:
if (it_old_psm_read_samples(&sigdata->sample, f, &sigdata->n_samples)) goto error_fc;
break;
case PSM_COMPONENT_COMMENTS:
if (dumbfile_mgetl(f) == DUMB_ID('T','E','X','T')) {
o = dumbfile_igetw(f);
if (o > 0) {
sigdata->song_message = malloc(o + 1);
if (dumbfile_getnc((char *)sigdata->song_message, o, f) < o) goto error_fc;
sigdata->song_message[o] = 0;
}
}
break;
}
}
_dumb_it_fix_invalid_orders(sigdata);
free(component);
return sigdata;
error_fc:
free(component);
error_usd:
_dumb_it_unload_sigdata(sigdata);
return NULL;
error_sd:
free(sigdata);
error:
return NULL;
}
DUH *dumb_read_old_psm_quick(DUMBFILE *f)
{
sigdata_t *sigdata;
DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
sigdata = it_old_psm_load_sigdata(f);
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] = "PSM (old)";
return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
}
}