Olav SørensenPushed v1.62 code4188e803 years agoHistory
266 lines · 6.1 KB · C
RawDownload
/* 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.songLength = *p++; 	if (m->header.songLength == 0 || m->header.songLength > 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; 		const double dHz = (double)CIA_PAL_CLK / (ciaPeriod+1); // +1, CIA triggers on underflow 		m->header.initialTempo = (uint16_t)((dHz * (125.0 / 50.0)) + 0.5);	} 	// read orders and count number of patterns	int32_t numPatterns = 0;	for (int32_t i = 0; i < 128; i++)	{		m->header.patternTable[i] = *p++;		if (m->header.patternTable[i] > numPatterns)			numPatterns = m->header.patternTable[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;		} 		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;}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t