655 lines · 15.5 KB · C
RawDownload
/* Scream Tracker 3 (or compatible) S3M loader**** 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 <stdint.h>#include <stdbool.h>#include "../ft2_header.h"#include "../ft2_module_loader.h"#include "../ft2_sample_ed.h"#include "../ft2_tables.h"#include "../ft2_sysreqs.h" #ifdef _MSC_VER // please don't mess with these structs!#pragma pack(push)#pragma pack(1)#endiftypedef struct s3mSmpHdr_t{	uint8_t type, junk1[12], offsetInFileH;	uint16_t offsetInFile;	int32_t length, loopStart, loopEnd;	uint8_t volume, junk2, packFlag, flags;	int32_t midCFreq, junk3;	uint16_t junk4;	uint8_t junk5[6];	char name[28], ID[4];}#ifdef __GNUC____attribute__ ((packed))#endifs3mSmpHdr_t; typedef struct s3mHdr_t{	char name[28];	uint8_t junk1, type;	uint16_t junk2;	int16_t numOrders, numSamples, numPatterns;	uint16_t flags, junk3, version;	char ID[4];	uint8_t junk4, speed, BPM, junk5, junk6[12], chnSettings[32];}#ifdef __GNUC____attribute__ ((packed))#endifs3mHdr_t;#ifdef _MSC_VER#pragma pack(pop)#endif static uint8_t pattBuff[12288]; static int8_t countS3MChannels(uint16_t antPtn); bool loadS3M(FILE *f, uint32_t filesize){	uint8_t alastnfo[32], alastefx[32], alastvibnfo[32], s3mLastGInstr[32];	int16_t ii, kk, tmp;	int32_t patternOffsets[256], sampleOffsets[256], j, k;	note_t tmpNote;	sample_t *s;	s3mHdr_t hdr;	s3mSmpHdr_t smpHdr; 	tmpLinearPeriodsFlag = false; // use Amiga periods 	if (filesize < sizeof (hdr))	{		loaderMsgBox("Error: This file is either not a module, or is not supported.");		return false;	} 	memset(&hdr, 0, sizeof (hdr));	if (fread(&hdr, 1, sizeof (hdr), f) != sizeof (hdr))	{		loaderMsgBox("Error: This file is either not a module, or is not supported.");		return false;	} 	if (hdr.numSamples > MAX_INST || hdr.numOrders > MAX_ORDERS || hdr.numPatterns > MAX_PATTERNS ||		hdr.type != 16 || hdr.version < 1 || hdr.version > 2)	{		loaderMsgBox("Error loading .s3m: Incompatible module!");		return false;	} 	memset(songTmp.orders, 255, 256); // pad by 255	if (fread(songTmp.orders, hdr.numOrders, 1, f) != 1)	{		loaderMsgBox("General I/O error during loading! Is the file in use?");		return false;	} 	songTmp.songLength = hdr.numOrders; 	// remove pattern separators (254)	k = 0;	j = 0;	for (int32_t i = 0; i < songTmp.songLength; i++)	{		if (songTmp.orders[i] != 254)			songTmp.orders[j++] = songTmp.orders[i];		else			k++;	} 	if (k <= songTmp.songLength)		songTmp.songLength -= (uint16_t)k;	else		songTmp.songLength = 1; 	for (int32_t i = 1; i < songTmp.songLength; i++)	{		if (songTmp.orders[i] == 255)		{			songTmp.songLength = (uint16_t)i;			break;		}	}		// clear unused song table entries	if (songTmp.songLength < 255)		memset(&songTmp.orders[songTmp.songLength], 0, 255-songTmp.songLength); 	memcpy(songTmp.name, hdr.name, 20); 	songTmp.BPM = hdr.BPM;	songTmp.speed = hdr.speed; 	// load sample offsets	for (int32_t i = 0; i < hdr.numSamples; i++)	{		uint16_t offset;		if (fread(&offset, 2, 1, f) != 1)		{			loaderMsgBox("General I/O error during loading! Is the file in use?");			return false;		} 		sampleOffsets[i] = offset << 4;	} 	// load pattern offsets	for (int32_t i = 0; i < hdr.numPatterns; i++)	{		uint16_t offset;		if (fread(&offset, 2, 1, f) != 1)		{			loaderMsgBox("General I/O error during loading! Is the file in use?");			return false;		} 		patternOffsets[i] = offset << 4;	} 	// *** PATTERNS *** 	k = 0;	for (int32_t i = 0; i < hdr.numPatterns; i++)	{		if (patternOffsets[i]  == 0)			continue; // empty pattern 		memset(alastnfo, 0, sizeof (alastnfo));		memset(alastefx, 0, sizeof (alastefx));		memset(alastvibnfo, 0, sizeof (alastvibnfo));		memset(s3mLastGInstr, 0, sizeof (s3mLastGInstr)); 		fseek(f, patternOffsets[i], SEEK_SET);		if (feof(f))			continue; 		if (fread(&j, 2, 1, f) != 1)		{			loaderMsgBox("General I/O error during loading! Is the file in use?");			return false;		} 		if (j > 0 && j <= 12288)		{			if (!allocateTmpPatt(i, 64))			{				loaderMsgBox("Not enough memory!");				return false;			} 			fread(pattBuff, j, 1, f); 			k = 0;			kk = 0; 			while (k < j && kk < 64)			{				uint8_t bits = pattBuff[k++]; 				if (bits == 0)				{					kk++;				}				else				{					ii = bits & 31; 					memset(&tmpNote, 0, sizeof (tmpNote)); 					// note and sample					if (bits & 32)					{						tmpNote.note = pattBuff[k++];						tmpNote.instr = pattBuff[k++]; 						if (tmpNote.instr > MAX_INST)							tmpNote.instr = 0; 						     if (tmpNote.note == 254) tmpNote.note = NOTE_OFF;						else if (tmpNote.note == 255) tmpNote.note = 0;						else						{							tmpNote.note = 1 + (tmpNote.note & 0xF) + (tmpNote.note >> 4) * 12;							if (tmpNote.note > 96)								tmpNote.note = 0;						}					} 					// volume column					if (bits & 64)					{						tmpNote.vol = pattBuff[k++]; 						if (tmpNote.vol <= 64)							tmpNote.vol += 0x10;						else							tmpNote.vol = 0;					} 					// effect					if (bits & 128)					{						tmpNote.efx = pattBuff[k++];						tmpNote.efxData = pattBuff[k++]; 						if (tmpNote.efxData > 0)						{							alastnfo[ii] = tmpNote.efxData;							if (tmpNote.efx == 8 || tmpNote.efx == 21)								alastvibnfo[ii] = tmpNote.efxData; // H/U						} 						// in ST3, a lot of effects directly share the same memory!						if (tmpNote.efxData == 0 && tmpNote.efx != 7) // G						{							uint8_t efx = tmpNote.efx;							if (efx == 8 || efx == 21) // H/U								tmpNote.efxData = alastvibnfo[ii];							else if ((efx >= 4 && efx <= 12) || (efx >= 17 && efx <= 19)) // D/E/F/I/J/K/L/Q/R/S								tmpNote.efxData = alastnfo[ii]; 							/* If effect data is zero and effect type was the same as last one, clear out							** data if it's not J or S (those have no memory in the equivalent XM effects).							** Also goes for extra fine pitch slides and fine volume slides,							** since they get converted to other effects.							*/							if (efx == alastefx[ii] && tmpNote.efx != 10 && tmpNote.efx != 19) // J/S							{								uint8_t nfo = tmpNote.efxData;								bool extraFinePitchSlides = (efx == 5 || efx == 6) && ((nfo & 0xF0) == 0xE0);								bool fineVolSlides = (efx == 4 || efx == 11) &&								     ((nfo > 0xF0) || (((nfo & 0xF) == 0xF) && ((nfo & 0xF0) > 0))); 								if (!extraFinePitchSlides && !fineVolSlides)									tmpNote.efxData = 0;							}						} 						if (tmpNote.efx > 0)							alastefx[ii] = tmpNote.efx; 						switch (tmpNote.efx)						{							case 1: // A							{								tmpNote.efx = 0xF;								if (tmpNote.efxData == 0)								{									tmpNote.efx = 0;									tmpNote.efxData = 0;								}								else if (tmpNote.efxData > 0x1F)								{									tmpNote.efxData = 0x1F;								}							}							break; 							case 2: tmpNote.efx = 0xB; break; // B							case 3: tmpNote.efx = 0xD; break; // C							case 4: // D							{								if (tmpNote.efxData > 0xF0) // fine slide up								{									tmpNote.efx = 0xE;									tmpNote.efxData = 0xB0 | (tmpNote.efxData & 0xF);								}								else if ((tmpNote.efxData & 0x0F) == 0x0F && (tmpNote.efxData & 0xF0) > 0) // fine slide down								{									tmpNote.efx = 0xE;									tmpNote.efxData = 0xA0 | (tmpNote.efxData >> 4);								}								else								{									tmpNote.efx = 0xA;									if (tmpNote.efxData & 0x0F) // on D/K, last nybble has first priority in ST3										tmpNote.efxData &= 0x0F;								}							}							break; 							case 5: // E							case 6: // F							{								if ((tmpNote.efxData & 0xF0) >= 0xE0)								{									// convert to fine slide									if ((tmpNote.efxData & 0xF0) == 0xE0)										tmp = 0x21;									else										tmp = 0xE; 									tmpNote.efxData &= 0x0F; 									if (tmpNote.efx == 0x05)										tmpNote.efxData |= 0x20;									else										tmpNote.efxData |= 0x10; 									tmpNote.efx = (uint8_t)tmp; 									if (tmpNote.efx == 0x21 && tmpNote.efxData == 0)									{										tmpNote.efx = 0;									}								}								else								{									// convert to normal 1xx/2xx slide									tmpNote.efx = 7 - tmpNote.efx;								}							}							break; 							case 7: // G							{								tmpNote.efx = 0x03; 								// fix illegal slides (to new instruments)								if (tmpNote.instr != 0 && tmpNote.instr != s3mLastGInstr[ii])									tmpNote.instr = s3mLastGInstr[ii];							}							break; 							case 11: // K							{								if (tmpNote.efxData > 0xF0) // fine slide up								{									tmpNote.efx = 0xE;									tmpNote.efxData = 0xB0 | (tmpNote.efxData & 0xF); 									// if volume column is unoccupied, set to vibrato									if (tmpNote.vol == 0)										tmpNote.vol = 0xB0;								}								else if ((tmpNote.efxData & 0x0F) == 0x0F && (tmpNote.efxData & 0xF0) > 0) // fine slide down								{									tmpNote.efx = 0xE;									tmpNote.efxData = 0xA0 | (tmpNote.efxData >> 4); 									// if volume column is unoccupied, set to vibrato									if (tmpNote.vol == 0)										tmpNote.vol = 0xB0;								}								else								{									tmpNote.efx = 0x6;									if (tmpNote.efxData & 0x0F) // on D/K, last nybble has first priority in ST3										tmpNote.efxData &= 0x0F;								}							}							break; 							case 8: tmpNote.efx = 0x04; break; // H							case 9: tmpNote.efx = 0x1D; break; // I							case 10: tmpNote.efx = 0x00; break; // J							case 12: tmpNote.efx = 0x05; break; // L							case 15: tmpNote.efx = 0x09; break; // O							case 17: tmpNote.efx = 0x1B; break; // Q							case 18: tmpNote.efx = 0x07; break; // R 							case 19: // S							{								tmpNote.efx = 0xE;								tmp = tmpNote.efxData >> 4;								tmpNote.efxData &= 0x0F; 								     if (tmp == 0x1) tmpNote.efxData |= 0x30;								else if (tmp == 0x2) tmpNote.efxData |= 0x50;								else if (tmp == 0x3) tmpNote.efxData |= 0x40;								else if (tmp == 0x4) tmpNote.efxData |= 0x70;								// ignore S8x becuase it's not compatible with FT2 panning								else if (tmp == 0xB) tmpNote.efxData |= 0x60;								else if (tmp == 0xC) // Note Cut								{									tmpNote.efxData |= 0xC0;									if (tmpNote.efxData == 0xC0)									{										// EC0 does nothing in ST3 but cuts voice in FT2, remove effect										tmpNote.efx = 0;										tmpNote.efxData = 0;									}								}								else if (tmp == 0xD) // Note Delay								{									tmpNote.efxData |= 0xD0;									if (tmpNote.note == 0 || tmpNote.note == NOTE_OFF)									{										// EDx without a note does nothing in ST3 but retrigs in FT2, remove effect										tmpNote.efx = 0;										tmpNote.efxData = 0;									}									else if (tmpNote.efxData == 0xD0)									{										// ED0 prevents note/smp/vol from updating in ST3, remove everything										tmpNote.note = 0;										tmpNote.instr = 0;										tmpNote.vol = 0;										tmpNote.efx = 0;										tmpNote.efxData = 0;									}								}								else if (tmp == 0xE) tmpNote.efxData |= 0xE0;								else if (tmp == 0xF) tmpNote.efxData |= 0xF0;								else								{									tmpNote.efx = 0;									tmpNote.efxData = 0;								}							}							break; 							case 20: // T							{								tmpNote.efx = 0x0F;								if (tmpNote.efxData < 0x21) // Txx with a value lower than 33 (0x21) does nothing in ST3, remove effect								{									tmpNote.efx = 0;									tmpNote.efxData = 0;								}							}							break; 							case 22: // V							{								tmpNote.efx = 0x10;								if (tmpNote.efxData > 0x40)								{									// Vxx > 0x40 does nothing in ST3									tmpNote.efx = 0;									tmpNote.efxData = 0;								}							}							break; 							default:							{								tmpNote.efx = 0;								tmpNote.efxData = 0;							}							break;						}					} 					if (tmpNote.instr != 0 && tmpNote.efx != 0x3)						s3mLastGInstr[ii] = tmpNote.instr; 					patternTmp[i][(kk * MAX_CHANNELS) + ii] = tmpNote;				}			} 			if (tmpPatternEmpty((uint16_t)i))			{				if (patternTmp[i] != NULL)				{					free(patternTmp[i]);					patternTmp[i] = NULL;				}			}		}	}

Lines 1–500 of 655.

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