WiiFlow_Lite/portlibs/sources/libmodplay/source/s3m.c

576 lines
14 KiB
C
Raw Normal View History

2012-01-21 21:57:41 +01:00
/*
* Copyright (c) 2002, 2003, 2004, 2005, 2007 by Christian Nowak <chnowak@web.de>
* Last update: 20th October, 2007
*/
#include <stdlib.h>
#include <string.h>
#include "modplay.h"
#include "s3m.h"
#include "effects.h"
#define DESCRIPTION "Future Crew Screamtracker 3"
#define AUTHOR "Christian Nowak <chnowak@web.de>"
#define VERSION "v0.01b"
#define COPYRIGHT "Copyright (c) 2003, 2007"
#define SAFE_MALLOC(dest, a) \
dest = malloc(a); \
if (dest == NULL) { \
MODFILE_Free(s3m); \
return -2; \
}
/**
* int MODFILE_SetS3M(u8 *modfile, int modlength, MODFILE *mod);
*
* Processes the raw data of an S3M file and copies it to a
* structure. The structure can then be used as a handle
* of the S3M file. The original raw data isn't needed by the
* handle.
*
* Returns a value <0 on error.
*
* Parameters:
* modfile - A pointer to the raw S3M data
* modlength - The length of the raw data in bytes
* mod - A pointer to the S3M handle
**/
int MODFILE_SetS3M(u8 *s3mfile, int s3mlength, MODFILE *s3m) {
int ofs = 0;
int i;
BOOL panning_present = FALSE;
int remapChannels[MODPLAY_MAX_CHANNELS];
if (!MODFILE_IsS3M(s3mfile, s3mlength) || (s3m == NULL))
return -1;
/* 0 */
memcpy(s3m->songname, &s3mfile[ofs], 28);
ofs+= 28;
/* 1c */
ofs++;
/* 1d */
if (s3mfile[ofs++] != 0x10) {
return -1;
}
ofs += 2;
/* 20 */
s3m->songlength = s3mfile[ofs++];
s3m->songlength |= s3mfile[ofs++] << 8;
/* 22 */
s3m->nInstruments = s3mfile[ofs++];
s3m->nInstruments |= s3mfile[ofs++] << 8;
s3m->nSamples = s3m->nInstruments; /* S3M doesn't have multisamples */
/* 24 */
s3m->nPatterns = s3mfile[ofs++];
s3m->nPatterns |= s3mfile[ofs++] << 8;
/* 26 */
s3m->st2_vibrato = (s3mfile[ofs] & 0x02) != 0;
s3m->st2_tempo = (s3mfile[ofs] & 0x04) != 0;
s3m->amiga_sliding = (s3mfile[ofs] & 0x08) != 0;
s3m->optimize_vols = (s3mfile[ofs] & 0x10) != 0;
s3m->amiga_boundaries = (s3mfile[ofs] & 0x20) != 0;
s3m->enable_sfx = (s3mfile[ofs] & 0x40) != 0; /* Fast volume slides */
ofs += 2;
/* 28 */
s3m->tracker_version = (s3mfile[ofs] << 8) | s3mfile[ofs + 1];
ofs += 2;
/* 2a */
s3m->unsigned_samples = (s3mfile[ofs] == 2);
ofs += 2;
/* 2c */
if (memcmp(&s3mfile[ofs], "SCRM", 4) != 0) {
return -1;
}
ofs += 4;
/* 30 */
s3m->master_volume = s3mfile[ofs++];
/* 31 */
s3m->start_speed = s3mfile[ofs++];
/* 32 */
s3m->start_tempo = s3mfile[ofs++];
/* 33 */
ofs++;
/* 34 */
ofs++;
/* 35 */
panning_present = (s3mfile[ofs++] == 252);
/* 36 */
ofs = 0x40;
/* 40 */
for (i = 0; i < MODPLAY_MAX_CHANNELS; i++) {
s3m->channels[i].voiceInfo.enabled = FALSE;
remapChannels[i] = 255;
}
for (i = s3m->nChannels = 0; i < 32; i++, ofs++) {
if (s3mfile[ofs] < 16) {
remapChannels[i] = s3m->nChannels;
s3m->nChannels++;
s3m->channels[remapChannels[i]].voiceInfo.enabled = TRUE;
s3m->channels[remapChannels[i]].default_panning = s3mfile[ofs] < 8 ? 86 : 167;
}
}
s3m->nChannels++; /* Global FX channel */
/* 60 */
memcpy(s3m->playlist, &s3mfile[ofs], s3m->songlength);
ofs += s3m->songlength;
/* Parapointers to intruments */
/* s3m->instruments = malloc(s3m->nInstruments * sizeof(MOD_Instrument));*/
SAFE_MALLOC(s3m->instruments, s3m->nInstruments * sizeof(MOD_Instrument));
memset(s3m->instruments, 0, s3m->nInstruments * sizeof(MOD_Instrument));
/* s3m->samples = malloc(s3m->nSamples * sizeof(MOD_Sample));*/
SAFE_MALLOC(s3m->samples, s3m->nSamples * sizeof(MOD_Sample));
memset(s3m->samples, 0, s3m->nSamples * sizeof(MOD_Sample));
for (i = 0; i < s3m->nInstruments; i++, ofs += 2) {
int instrPointer = (s3mfile[ofs] | s3mfile[ofs+1] << 8) << 4;
s3m->instruments[i].volumeFade = 32767;
/* 0 */
/* s3m->instruments[i].adlib = s3mfile[instrPointer++] != 1;*/
if (/*!s3m->instruments[i].adlib*/s3mfile[instrPointer++] == 1) {
int sampleseg;
int j;
int temp;
s3m->samples[i].relative_note = 0;
s3m->samples[i].panning = 255;
/* 1 */
/* memcpy(s3m->instruments[i].dosname, &s3mfile[instrPointer], 12);
s3m->instruments[i].dosname[12] = '\0';*/
instrPointer += 12;
/* d */
sampleseg = s3mfile[instrPointer + 1] |
(s3mfile[instrPointer + 2] << 8) |
(s3mfile[instrPointer + 0] << 16);
sampleseg <<= 4;
instrPointer += 3;
/* 10 */
s3m->samples[i].sampleInfo.length = s3mfile[instrPointer] |
(s3mfile[instrPointer + 1] << 8) |
(s3mfile[instrPointer + 2] << 16) |
(s3mfile[instrPointer + 2] << 24);
instrPointer += 4;
/* 14 */
s3m->samples[i].sampleInfo.loop_start = s3mfile[instrPointer] |
(s3mfile[instrPointer + 1] << 8) |
(s3mfile[instrPointer + 2] << 16) |
(s3mfile[instrPointer + 3] << 24);
instrPointer += 4;
/* 18 */
s3m->samples[i].sampleInfo.loop_end = s3mfile[instrPointer] |
(s3mfile[instrPointer + 1] << 8) |
(s3mfile[instrPointer + 2] << 16) |
(s3mfile[instrPointer + 3] << 24);
instrPointer += 4;
/* 1c */
s3m->samples[i].default_volume = s3mfile[instrPointer++];
/* 1d */
instrPointer++;
/* 1e */
/* s3m->instruments[i].packed = (s3mfile[instrPointer++] == 1);*/
instrPointer++;
/* 1f */
s3m->samples[i].sampleInfo.looped = (s3mfile[instrPointer] & 1) != 0;
s3m->samples[i].sampleInfo.stereo = (s3mfile[instrPointer] & 2) != 0;
s3m->samples[i].sampleInfo.bit_16 = (s3mfile[instrPointer] & 4) != 0;
s3m->samples[i].sampleInfo.pingpong = FALSE;
instrPointer++;
if (!s3m->samples[i].sampleInfo.looped) {
s3m->samples[i].sampleInfo.loop_start =
s3m->samples[i].sampleInfo.loop_end =
s3m->samples[i].sampleInfo.length - 1;
}
/* 20 */
s3m->samples[i].default_middle_c = s3m->samples[i].middle_c =
s3mfile[instrPointer] |
(s3mfile[instrPointer + 1] << 8) |
(s3mfile[instrPointer + 2] << 16) |
(s3mfile[instrPointer + 3] << 24);
instrPointer += 4;
/* 24 */
instrPointer += 4;
/* 28 */
instrPointer += 2;
/* 2a */
instrPointer += 2;
/* 2c */
instrPointer += 4;
/* 30 */
memcpy(s3m->instruments[i].name, &s3mfile[instrPointer], 28);
instrPointer += 28;
/* 4c */
if (memcmp(&s3mfile[instrPointer], "SCRS", 4) != 0) {
return -1;
}
/* Define a new instrument */
strcpy(s3m->instruments[i].name, s3m->samples[i].name);
for (j = 0; j < 256; j++) {
s3m->instruments[i].samples[j] = &s3m->samples[i];
s3m->instruments[i].note[j] = j;
}
/* Disable instrument envelopes */
s3m->instruments[i].envVolume.enabled = FALSE;
s3m->instruments[i].envPanning.enabled = FALSE;
/* Copy sample data */
temp = s3m->samples[i].sampleInfo.length *
(s3m->samples[i].sampleInfo.bit_16 || s3m->samples[i].sampleInfo.stereo ? 2 : 1);
/* s3m->samples[i].sampleInfo.sampledata = malloc(temp);*/
SAFE_MALLOC(s3m->samples[i].sampleInfo.sampledata, temp);
memset(s3m->samples[i].sampleInfo.sampledata, 0, temp);
if (s3m->samples[i].sampleInfo.bit_16) {
u8 *di = &s3mfile[sampleseg];
int l = s3m->samples[i].sampleInfo.length;
for (j = 0; j < l; j++) {
u16 d;
d = di[j<<1] | di[(j << 1) + 1] << 8;
if (s3m->unsigned_samples)
d ^= 0x8000;
((u16*)s3m->samples[i].sampleInfo.sampledata)[j] = d;
}
} else {
u8 *di = &s3mfile[sampleseg];
int l = s3m->samples[i].sampleInfo.length;
for (j = 0;j < l; j++) {
u8 d;
d = di[j];
if (s3m->unsigned_samples)
d ^= 0x80;
((u8*)s3m->samples[i].sampleInfo.sampledata)[j] = d;
}
}
}
}
/* Parapointers to patterns */
/* s3m->patterns = malloc(s3m->nPatterns * sizeof(MOD_Note*));*/
SAFE_MALLOC(s3m->patterns, s3m->nPatterns * sizeof(MOD_Note*));
memset(s3m->patterns, 0, s3m->nPatterns * sizeof(MOD_Note*));
/* s3m->patternLengths = malloc(s3m->nPatterns * sizeof(int));*/
SAFE_MALLOC(s3m->patternLengths, s3m->nPatterns * sizeof(int));
for (i = 0; i < s3m->nPatterns; i++, ofs += 2) {
int pattPointer = (s3mfile[ofs] | s3mfile[ofs + 1] << 8) << 4;
int pattPackedLen;
int destChannel;
int destLine;
s3m->patternLengths[i] = 64;
/* s3m->patterns[i] = malloc(sizeof(MOD_Note) * s3m->nChannels * 64);*/
SAFE_MALLOC(s3m->patterns[i], sizeof(MOD_Note) * s3m->nChannels * 64);
memset(s3m->patterns[i], 255, sizeof(MOD_Note) * s3m->nChannels * 64);
pattPackedLen = s3mfile[pattPointer] |
(s3mfile[pattPointer + 1] << 8);
pattPointer += 2;
destChannel = destLine = 0;
while (1) {
int dest;
u8 compByte = s3mfile[pattPointer++];
if (compByte == 0) {
destLine++;
if (destLine >= 64) {
break;
}
} else {
destChannel = remapChannels[compByte & 31];
dest = (destLine * s3m->nChannels) + destChannel ;
if (destChannel != 255) {
/* Note/Instrument */
if (compByte & 32) {
s3m->patterns[i][dest].note = s3mfile[pattPointer++];
s3m->patterns[i][dest].instrument = s3mfile[pattPointer++];
}
/* Volume byte */
if (compByte & 64) {
s3m->patterns[i][dest].volume = s3mfile[pattPointer++];
}
/* Effect number/parameter */
if (compByte & 128) {
u16 effect = s3mfile[pattPointer++];
u8 operand = s3mfile[pattPointer++];
/* Convert effect/operand to internal format */
switch (effect) {
case 'A' - 'A' + 1: /* Set Speed */
effect = EFFECT_S3Ma0;
break;
case 'B' - 'A' + 1: /* Jump To Order xy */
s3m->patterns[i][(destLine * s3m->nChannels) + s3m->nChannels - 1].effect[0] = EFFECT_STb0;
s3m->patterns[i][(destLine * s3m->nChannels) + s3m->nChannels - 1].operand[0] = operand;
effect = EFFECT_NONE;
operand = 0;
break;
case 'C' - 'A' + 1: /* Break Pattern */
s3m->patterns[i][(destLine * s3m->nChannels) + s3m->nChannels - 1].effect[0] = EFFECT_STd0;
s3m->patterns[i][(destLine * s3m->nChannels) + s3m->nChannels - 1].operand[0] = operand;
effect = EFFECT_NONE;
operand = 0;
break;
case 'D' - 'A' + 1: /* Volume Slide */
effect = EFFECT_S3Md0;
break;
case 'E' - 'A' + 1: /* Portamento Down */
effect = EFFECT_S3Me0;
break;
case 'F' - 'A' + 1: /* Portamento Up */
effect = EFFECT_S3Mf0;
break;
case 'G' - 'A' + 1: /* Tone Portamento */
effect = EFFECT_ST30;
break;
case 'H' - 'A' + 1: /* Vibrato */
effect = EFFECT_ST40;
break;
case 'I' - 'A' + 1: /* Tremor */
effect = EFFECT_NONE;
break;
case 'J' - 'A' + 1: /* Arpeggio */
effect = EFFECT_ST00;
break;
case 'K' - 'A' + 1: /* Vibrato + Volume Slide */
effect = EFFECT_S3Mk0;
break;
case 'L' - 'A' + 1: /* Tone Portamento + Volume Slide */
effect = EFFECT_S3Ml0;
break;
case 'O' - 'A' + 1: /* Sample Offset */
effect = EFFECT_ST90;
break;
case 'Q' - 'A' + 1: /* Retrig + Volume Slide */
effect = EFFECT_S3Mq0;
break;
case 'R' - 'A' + 1: /* Tremolo */
effect = EFFECT_S3Mr0;
break;
case 'S' - 'A' + 1: /* Various */
switch ((operand >> 4) & 0x0f) {
case 0x02: /* Set Finetune */
effect = EFFECT_STe5;
operand = operand & 0x0f;
break;
case 0x03: /* Vibrato Waveform */
effect = EFFECT_STe4;
operand = operand & 0x0f;
break;
case 0x04: /* Tremolo Waveform */
effect = EFFECT_STe7;
operand = operand & 0x0f;
break;
case 0x08: /* Panning */
effect = EFFECT_STe8;
operand = operand & 0x0f;
break;
case 0x0b: /* Pattern Loop */
operand = operand & 0x0f;
s3m->patterns[i][(destLine * s3m->nChannels) + s3m->nChannels - 1].effect[0] = EFFECT_STe6;
s3m->patterns[i][(destLine * s3m->nChannels) + s3m->nChannels - 1].operand[0] = operand;
effect = EFFECT_NONE;
operand = 0;
break;
case 0x0c: /* Note Cut */
effect = EFFECT_STec;
operand = operand & 0x0f;
break;
case 0x0d: /* Note Delay */
effect = EFFECT_STed;
operand = operand & 0x0f;
break;
case 0x0e: /* Pattern Delay */
operand = operand & 0x0f;
s3m->patterns[i][(destLine * s3m->nChannels) + s3m->nChannels - 1].effect[0] = EFFECT_STee;
s3m->patterns[i][(destLine * s3m->nChannels) + s3m->nChannels - 1].operand[0] = operand;
effect = EFFECT_NONE;
operand = 0;
break;
default:
effect = EFFECT_NONE;
break;
}
break;
case 'T' - 'A' + 1: /* Set Tempo */
effect = EFFECT_S3Mt0;
break;
case 'V' - 'A' + 1: /* Set Global Volume */
effect = EFFECT_XM10;
break;
default:
effect = EFFECT_NONE;
break;
}
s3m->patterns[i][dest].effect[0] = effect;
s3m->patterns[i][dest].operand[0] = operand;
}
} else {
if (compByte & 32)
pattPointer += 2;
if (compByte & 64)
pattPointer++;
if (compByte & 128)
pattPointer += 2;
}
}
}
}
s3m->filetype = MODULE_S3M;
return 0;
}
/**
* BOOL MODFILE_IsS3M(u8 *modfile, int modlength);
*
* Checks whether the raw data in memory is a valid
* S3M file.
*
* Returns TRUE if the data is an S3M file, FALSE
* if not.
*
* Parameters:
*
* modfile - Pointer to the raw data to be checked
* modlength - Length of the raw data in bytes
**/
BOOL MODFILE_IsS3M(u8 *modfile, int modlength) {
if ((modfile == NULL) || (modlength < 0x30))
return FALSE;
return memcmp(&modfile[0x2c], "SCRM", 4) == 0;
}
int MODFILE_S3MGetFormatID(void) {
return MODULE_S3M;
}
char *MODFILE_S3MGetDescription(void) {
return DESCRIPTION;
}
char *MODFILE_S3MGetAuthor(void) {
return AUTHOR;
}
char *MODFILE_S3MGetVersion(void) {
return VERSION;
}
char *MODFILE_S3MGetCopyright(void) {
return COPYRIGHT;
}