ref: 4f4c60b34d48ac94175de4939c2fe07e73c348c5
dir: /src/modloaders/pt2_load_mod15.c/
/* 15-sample Amiga MOD loader (The Ultimate Soundtracker, etc.)
**
** Note: Data sanitation is done in the last stage
** of module loading, so you don't need to do that here.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "../pt2_header.h"
#include "../pt2_config.h"
#include "../pt2_structs.h"
#include "../pt2_replayer.h"
#include "../pt2_textout.h"
#include "../pt2_visuals.h"
static int32_t realSampleLengths[15];
module_t *loadMod15(uint8_t *buffer, uint32_t filesize)
{
(void)filesize;
module_t *m = createEmptyMod();
if (m == NULL)
{
statusOutOfMemory();
goto loadError;
}
bool veryLateSTKVerFlag = false; // "DFJ SoundTracker III" and later
bool lateSTKVerFlag = false; // "TJC SoundTracker II" and later
const uint8_t *p = buffer;
memcpy(m->header.name, p, 20); p += 20;
// read sample headers
moduleSample_t *s = m->samples;
for (int32_t i = 0; i < 15; i++, s++)
{
memcpy(s->text, p, 22); p += 22;
realSampleLengths[i] = ((p[0] << 8) | p[1]) * 2; p += 2;
s->length = (realSampleLengths[i] > config.maxSampleLength) ? config.maxSampleLength : realSampleLengths[i];
/* Only late versions of Ultimate SoundTracker can have samples larger than 9999 bytes.
** If detected, we know for sure that this is a late STK module.
*/
if (s->length > 9999)
lateSTKVerFlag = true;
p++; // Hi-byte of volume, no finetune in STK/UST modules. Skip it.
s->volume = *p++;
// in The Ultimate SoundTracker, sample loop start is in bytes, not words
s->loopStart = (p[0] << 8) | p[1]; p += 2;
s->loopLength = ((p[0] << 8) | p[1]) * 2; p += 2;
}
m->header.numOrders = *p++;
if (m->header.numOrders == 0 || m->header.numOrders > 128)
{
displayErrorMsg("NOT A MOD FILE !");
goto loadError;
}
uint8_t initTempo = *p++;
if (initTempo > 220)
{
displayErrorMsg("NOT A MOD FILE !");
goto loadError;
}
// 120 is a special case and means 50Hz (125BPM)
if (initTempo == 0)
initTempo = 120;
// jjk55.mod by Jesper Kyd has a bogus STK tempo value that should be ignored (XXX: hackish...)
if (!strcmp("jjk55", m->header.name))
initTempo = 120;
m->header.initialTempo = 125;
if (initTempo != 120)
{
if (initTempo > 220)
initTempo = 220;
// convert UST tempo to BPM
uint16_t ciaPeriod = (240 - initTempo) * 122;
double dHz = (double)CIA_PAL_CLK / (ciaPeriod+1); // +1, CIA triggers on underflow
int32_t BPM = (int32_t)((dHz * (125.0 / 50.0)) + 0.5);
m->header.initialTempo = (uint16_t)BPM;
}
// read orders and count number of patterns
int32_t numPatterns = 0;
for (int32_t i = 0; i < MOD_ORDERS; i++)
{
m->header.order[i] = *p++;
if (m->header.order[i] > numPatterns)
numPatterns = m->header.order[i];
}
numPatterns++;
if (numPatterns > MAX_PATTERNS)
{
displayErrorMsg("UNSUPPORTED MOD !");
goto loadError;
}
// load pattern data
for (int32_t i = 0; i < numPatterns; i++)
{
note_t *note = m->patterns[i];
for (int32_t j = 0; j < MOD_ROWS; j++)
{
for (int32_t k = 0; k < 4; k++, note++, p += 4)
{
note->period = ((p[0] & 0x0F) << 8) | p[1];
note->sample = (p[0] & 0xF0) | (p[2] >> 4);
note->command = p[2] & 0x0F;
note->param = p[3];
// added sanitation not present in original PT
if (note->sample > 31)
note->sample = 0;
if (note->command == 0xC || note->command == 0xD || note->command == 0xE)
{
// "TJC SoundTracker II" and later
lateSTKVerFlag = true;
}
if (note->command == 0xF)
{
// "DFJ SoundTracker III" and later
lateSTKVerFlag = true;
veryLateSTKVerFlag = true;
}
}
}
}
// pattern command conversion
for (int32_t i = 0; i < numPatterns; i++)
{
note_t *note = m->patterns[i];
for (int32_t j = 0; j < MOD_ROWS*4; j++, note++)
{
// convert STK effects to PT effects
if (!lateSTKVerFlag)
{
// old SoundTracker 1.x commands
if (note->command == 1)
{
// arpeggio
note->command = 0;
}
else if (note->command == 2)
{
// pitch slide
if (note->param & 0xF0)
{
// pitch slide down
note->command = 2;
note->param >>= 4;
}
else if (note->param & 0x0F)
{
// pitch slide up
note->command = 1;
}
}
}
else
{
// "DFJ SoundTracker II" or later
// TODO: This needs more detection and is NOT correct!
if (note->command == 0xD)
{
if (veryLateSTKVerFlag) // "DFJ SoundTracker III" or later
{
// pattern break w/ no param (param must be cleared to fix some songs)
note->param = 0;
}
else
{
// volume slide
note->command = 0xA;
}
}
}
// effect F with param 0x00 does nothing in UST/STK (I think?)
if (note->command == 0xF && note->param == 0)
note->command = 0;
// remove E8x (Karplus-Strong is only supported for ProTracker .MODs)
if (note->command == 0xE && (note->param >> 4) == 0x8)
{
note->command = 0;
note->param = 0;
}
// remove EFx (Invert Loop is only supported for ProTracker .MODs)
if (note->command == 0xE && (note->param >> 4) == 0xF)
{
note->command = 0;
note->param = 0;
}
}
}
// load sample data
s = m->samples;
for (int32_t i = 0; i < 15; i++, s++)
{
// For Ultimate SoundTracker modules, only the loop area of a looped sample is played.
if (s->loopStart > 0 && s->loopLength < s->length)
{
s->length -= s->loopStart;
p += s->loopStart;
s->loopStart = 0;
}
/* We don't support loading samples bigger than 65534 bytes in our PT2 clone,
** so skip overflown data in .MOD file if present.
*/
int32_t bytesToSkip = 0;
if (realSampleLengths[i] > config.maxSampleLength)
bytesToSkip = realSampleLengths[i] - config.maxSampleLength;
// For Ultimate SoundTracker modules, don't load sample data after loop end
int32_t loopEnd = s->loopStart + s->loopLength;
if (loopEnd > 2 && s->length > loopEnd)
{
bytesToSkip += s->length-loopEnd;
s->length = loopEnd;
}
memcpy(&m->sampleData[s->offset], p, s->length); p += s->length;
if (bytesToSkip > 0)
p += bytesToSkip;
}
return m;
loadError:
if (m != NULL)
{
for (int32_t i = 0; i < MAX_PATTERNS; i++)
{
if (m->patterns[i] != NULL)
free(m->patterns[i]);
}
if (m->sampleData != NULL)
free(m->sampleData);
free(m);
}
return NULL;
}