mirror of
https://github.com/Fledge68/WiiFlow_Lite.git
synced 2024-12-27 12:21:57 +01:00
576 lines
14 KiB
C
576 lines
14 KiB
C
/*
|
|
* 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;
|
|
}
|