Olav SørensenPushed v1.66 codef56d9f63 years agoHistory
790 lines · 18.2 KB · C
RawDownload
// for finding memory leaks in debug mode with Visual Studio#if defined _DEBUG && defined _MSC_VER#include <crtdbg.h>#endif #include <stdio.h>#include <stdint.h>#include <stdbool.h>#ifndef _WIN32#include <unistd.h>#endif#include "ft2_header.h"#include "scopes/ft2_scopes.h"#include "ft2_trim.h"#include "ft2_inst_ed.h"#include "ft2_sample_ed.h"#include "ft2_wav_renderer.h"#include "ft2_pattern_ed.h"#include "ft2_gui.h"#include "ft2_diskop.h"#include "ft2_sample_loader.h"#include "ft2_mouse.h"#include "ft2_midi.h"#include "ft2_events.h"#include "ft2_video.h"#include "ft2_structs.h"#include "ft2_sysreqs.h" bool loadDIGI(FILE *f, uint32_t filesize);bool loadMOD(FILE *f, uint32_t filesize);bool loadS3M(FILE *f, uint32_t filesize);bool loadSTK(FILE *f, uint32_t filesize);bool loadSTM(FILE *f, uint32_t filesize);bool loadXM(FILE *f, uint32_t filesize); enum{	FORMAT_UNKNOWN = 0,	FORMAT_POSSIBLY_STK = 1,	FORMAT_XM = 2,	FORMAT_MOD = 3,	FORMAT_S3M = 4,	FORMAT_STM = 5,	FORMAT_DIGI = 6}; // file extensions accepted by Disk Op. in module modechar *supportedModExtensions[] ={	"xm", "ft", "nst", "stk", "mod", "s3m", "stm", "fst",	"digi", 	// IMPORTANT: Remember comma after last entry above	"END_OF_LIST" // do NOT move, remove or edit this line!}; // globals for module loadersvolatile bool tmpLinearPeriodsFlag;int16_t patternNumRowsTmp[MAX_PATTERNS];note_t *patternTmp[MAX_PATTERNS];instr_t *instrTmp[1+256];song_t songTmp;// -------------------------- static volatile bool musicIsLoading, moduleLoaded, moduleFailedToLoad;static SDL_Thread *thread;static uint8_t oldPlayMode;static void setupLoadedModule(void);static void freeTmpModule(void); // Crude module detection routine. These aren't always accurate detections!static int8_t detectModule(FILE *f){	uint8_t D[256], I[4]; 	fseek(f, 0, SEEK_END);	uint32_t fileLength = (uint32_t)ftell(f);	rewind(f); 	memset(D, 0, sizeof (D));	fread(D, 1, sizeof (D), f);	fseek(f, 1080, SEEK_SET); // MOD ID	I[0] = I[1] = I[2] = I[3] = 0;	fread(I, 1, 4, f);	rewind(f); 	// DIGI Booster (non-Pro)	if (!memcmp("DIGI Booster module", &D[0x00], 19+1) && D[0x19] >= 1 && D[0x19] <= 8)		return FORMAT_DIGI; 	// Scream Tracker 3 S3M (and compatible trackers)	if (!memcmp("SCRM", &D[0x2C], 4) && D[0x1D] == 16) // XXX: byte=16 in all cases?		return FORMAT_S3M; 	// Scream Tracker 2 STM	if ((!memcmp("!Scream!", &D[0x14], 8) || !memcmp("BMOD2STM", &D[0x14], 8) ||		 !memcmp("WUZAMOD!", &D[0x14], 8) || !memcmp("SWavePro", &D[0x14], 8)) && D[0x1D] == 2) // XXX: byte=2 for "WUZAMOD!"/"SWavePro" ?	{		return FORMAT_STM;	} 	// Generic multi-channel MOD (1..9 channels)	if (isdigit(I[0]) && I[0] != '0' && I[1] == 'C' && I[2] == 'H' && I[3] == 'N') // xCHN		return FORMAT_MOD; 	// Digital Tracker (Atari Falcon)	if (I[0] == 'F' && I[1] == 'A' && I[2] == '0' && I[3] >= '4' && I[3] <= '8') // FA0x (x=4..8)		return FORMAT_MOD; 	// Generic multi-channel MOD (10..99 channels)	if (isdigit(I[0]) && isdigit(I[1]) && I[0] != '0' && I[2] == 'C' && I[3] == 'H') // xxCH		return FORMAT_MOD; 	// Generic multi-channel MOD (10..99 channels)	if (isdigit(I[0]) && isdigit(I[1]) && I[0] != '0' && I[2] == 'C' && I[3] == 'N') // xxCN (same as xxCH)		return FORMAT_MOD;		// ProTracker and generic MOD formats	if (!memcmp("M.K.", I, 4) || !memcmp("M!K!", I, 4) || !memcmp("NSMS", I, 4) ||		!memcmp("LARD", I, 4) || !memcmp("PATT", I, 4) || !memcmp("FLT4", I, 4) ||		!memcmp("FLT8", I, 4) || !memcmp("EXO4", I, 4) || !memcmp("EXO8", I, 4) ||		!memcmp("N.T.", I, 4) || !memcmp("M&K!", I, 4) || !memcmp("FEST", I, 4) ||		!memcmp("CD61", I, 4) || !memcmp("CD81", I, 4) || !memcmp("OKTA", I, 4) ||		!memcmp("OCTA", I, 4))	{		return FORMAT_MOD;	} 	/* Check if the file is a .it module (Impulse Tracker, not supported).	** Some people may attempt to load .IT files in the FT2 clone, so	** reject them here instead of accidentally loading them as .STK	*/	if (!memcmp("IMPM", D, 4) && D[0x16] == 0)		return FORMAT_UNKNOWN; 	/* Fasttracker II XM and compatible trackers.	** Note: This test can falsely be true for STK modules (and non-supported files) where the	** first 17 bytes start with "Extended Module: ". This is unlikely to happen.	*/	if (!memcmp("Extended Module: ", &D[0x00], 17))		return FORMAT_XM; 	/* Lastly, we assume that the file is either a 15-sample STK or an unsupported file.	** Let's assume it's an STK and do some sanity checks. If they fail, we have an	** unsupported file.	*/ 	// minimum and maximum (?) possible size for a supported STK	if (fileLength < 1624 || fileLength > 984634)		return FORMAT_UNKNOWN; 	// test STK numOrders+BPM for illegal values	fseek(f, 470, SEEK_SET);	D[0] = D[1] = 0;	fread(D, 1, 2, f);	rewind(f); 	if (D[0] <= 128 && D[1] <= 220)		return FORMAT_POSSIBLY_STK; 	return FORMAT_UNKNOWN;} static bool doLoadMusic(bool externalThreadFlag){	// setup message box functions	loaderMsgBox = externalThreadFlag ? myLoaderMsgBoxThreadSafe : myLoaderMsgBox;	loaderSysReq = externalThreadFlag ? okBoxThreadSafe : okBox; 	if (editor.tmpFilenameU == NULL)	{		loaderMsgBox("Generic memory fault during loading!");		goto loadError;	} 	FILE *f = UNICHAR_FOPEN(editor.tmpFilenameU, "rb");	if (f == NULL)	{		loaderMsgBox("General I/O error during loading! Is the file in use? Does it exist?");		goto loadError;	} 	int8_t format = detectModule(f);	fseek(f, 0, SEEK_END);	uint32_t filesize = ftell(f); 	rewind(f);	switch (format)	{		case FORMAT_XM: moduleLoaded = loadXM(f, filesize); break;		case FORMAT_S3M: moduleLoaded = loadS3M(f, filesize); break;		case FORMAT_STM: moduleLoaded = loadSTM(f, filesize); break;		case FORMAT_MOD: moduleLoaded = loadMOD(f, filesize); break;		case FORMAT_POSSIBLY_STK: moduleLoaded = loadSTK(f, filesize); break;		case FORMAT_DIGI: moduleLoaded = loadDIGI(f, filesize); break; 		default:			loaderMsgBox("This file is not a supported module!");		break;	}	fclose(f); 	if (!moduleLoaded)		goto loadError; 	moduleLoaded = true;	return true; loadError:	freeTmpModule();	moduleFailedToLoad = true;	return false;} static void clearTmpModule(void){	memset(patternTmp, 0, sizeof (patternTmp));	memset(instrTmp, 0, sizeof (instrTmp));	memset(&songTmp, 0, sizeof (songTmp)); 	for (uint32_t i = 0; i < MAX_PATTERNS; i++)		patternNumRowsTmp[i] = 64;} static int32_t SDLCALL loadMusicThread(void *ptr){	return doLoadMusic(true);	(void)ptr;} void loadMusic(UNICHAR *filenameU){	if (musicIsLoading || filenameU == NULL)		return; 	mouseAnimOn(); 	musicIsLoading = true;	moduleLoaded = false;	moduleFailedToLoad = false; 	clearTmpModule(); // clear stuff from last loading session (very important)	UNICHAR_STRCPY(editor.tmpFilenameU, filenameU); 	thread = SDL_CreateThread(loadMusicThread, NULL, NULL);	if (thread == NULL)	{		editor.loadMusicEvent = EVENT_NONE;		okBox(0, "System message", "Couldn't create thread!");		musicIsLoading = false;		return;	} 	SDL_DetachThread(thread);} bool loadMusicUnthreaded(UNICHAR *filenameU, bool autoPlay){	if (filenameU == NULL)		return false; 	clearTmpModule(); // clear stuff from last loading session (very important)	UNICHAR_STRCPY(editor.tmpFilenameU, filenameU); 	editor.loadMusicEvent = EVENT_NONE;	doLoadMusic(false); 	if (moduleLoaded)	{		setupLoadedModule();		if (autoPlay)			startPlaying(PLAYMODE_SONG, 0); 		return true;	} 	return false;} bool allocateTmpPatt(int32_t pattNum, uint16_t numRows){	patternTmp[pattNum] = (note_t *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1);	if (patternTmp[pattNum] == NULL)		return false; 	patternNumRowsTmp[pattNum] = numRows;	return true;} bool allocateTmpInstr(int16_t insNum){	if (instrTmp[insNum] != NULL)		return false; // already allocated 	instr_t *ins = (instr_t *)calloc(1, sizeof (instr_t));	if (ins == NULL)		return false; 	sample_t *s = ins->smp;	for (int32_t i = 0; i < MAX_SMP_PER_INST; i++, s++)	{		s->panning = 128;		s->volume = 64;	} 	instrTmp[insNum] = ins;	return true;} static void freeTmpModule(void) // called on module load error{	// free all patterns	for (int32_t i = 0; i < MAX_PATTERNS; i++)	{		if (patternTmp[i] != NULL)		{			free(patternTmp[i]);			patternTmp[i] = NULL;		}	} 	// free all instruments and samples	for (int32_t i = 1; i <= 256; i++) // if >128 instruments, we fake-load up to 128 extra (and discard them later)	{		if (instrTmp[i] == NULL)			continue; 		sample_t *s = instrTmp[i]->smp;		for (int32_t j = 0; j < MAX_SMP_PER_INST; j++, s++)			freeSmpData(s); 		free(instrTmp[i]);		instrTmp[i] = NULL;	}} bool tmpPatternEmpty(uint16_t pattNum){	if (patternTmp[pattNum] == NULL)		return true; 	uint8_t *scanPtr = (uint8_t *)patternTmp[pattNum];	const uint32_t scanLen = patternNumRowsTmp[pattNum] * TRACK_WIDTH; 	for (uint32_t i = 0; i < scanLen; i++)	{		if (scanPtr[i] != 0)			return false;	} 	return true;} void clearUnusedChannels(note_t *pattPtr, int16_t numRows, int32_t numChannels){	if (pattPtr == NULL || numChannels >= MAX_CHANNELS)		return; 	const int32_t width = sizeof (note_t) * (MAX_CHANNELS - numChannels); 	note_t *p = &pattPtr[numChannels];	for (int32_t i = 0; i < numRows; i++, p += MAX_CHANNELS)		memset(p, 0, width);} // called from input/video thread after the module was done loadingstatic void setupLoadedModule(void){	lockMixerCallback(); 	freeAllInstr();	freeAllPatterns(); 	oldPlayMode = playMode;	playMode = PLAYMODE_IDLE;	songPlaying = false; #ifdef HAS_MIDI	midi.currMIDIVibDepth = 0;	midi.currMIDIPitch = 0;#endif 	memset(editor.keyOnTab, 0, sizeof (editor.keyOnTab)); 	// copy over new pattern pointers and lengths	for (int32_t i = 0; i < MAX_PATTERNS; i++)	{		pattern[i] = patternTmp[i];		patternNumRows[i] = patternNumRowsTmp[i];	} 	// copy over song struct	memcpy(&song, &songTmp, sizeof (song_t));	fixSongName(); 	// copy over new instruments (includes sample pointers)	for (int16_t i = 1; i <= MAX_INST; i++)	{		instr[i] = instrTmp[i];		fixInstrAndSampleNames(i); 		if (instr[i] != NULL)		{			sanitizeInstrument(instr[i]);			for (int32_t j = 0; j < MAX_SMP_PER_INST; j++)			{				sample_t *s = &instr[i]->smp[j]; 				sanitizeSample(s);				if (s->dataPtr != NULL)					fixSample(s); // prepare sample for branchless linear interpolation			}		}	} 	// we are the owners of the allocated memory ptrs set by the loader thread now 	// support non-even channel numbers	if (song.numChannels & 1)	{		song.numChannels++;		if (song.numChannels > MAX_CHANNELS)			song.numChannels = MAX_CHANNELS;	} 	song.numChannels = CLAMP(song.numChannels, 2, MAX_CHANNELS);	song.songLength = CLAMP(song.songLength, 1, MAX_ORDERS);	song.BPM = CLAMP(song.BPM, MIN_BPM, MAX_BPM);	song.initialSpeed = song.speed = CLAMP(song.speed, 1, MAX_SPEED); 	if (song.songLoopStart >= song.songLength)		song.songLoopStart = 0; 	song.globalVolume = 64; 	// remove overflown stuff in pattern data (FT2 doesn't do this)	for (int32_t i = 0; i < MAX_PATTERNS; i++)	{		if (patternNumRows[i] <= 0)			patternNumRows[i] = 64; 		if (patternNumRows[i] > MAX_PATT_LEN)			patternNumRows[i] = MAX_PATT_LEN; 		if (pattern[i] == NULL)			continue; 		note_t *p = pattern[i];		for (int32_t j = 0; j < MAX_PATT_LEN * MAX_CHANNELS; j++, p++)		{			if (p->note > 97)				p->note = 0; 			if (p->instr > 128)				p->instr = 0; 			if (p->efx > 35)			{				p->efx = 0;				p->efxData = 0;			}		}	} 	setScrollBarEnd(SB_POS_ED, (song.songLength - 1) + 5);	setScrollBarPos(SB_POS_ED, 0, false); 	resetChannels();	setPos(0, 0, true);	setMixerBPM(song.BPM); 	editor.tmpPattern = editor.editPattern; // set kludge variable	editor.BPM = song.BPM;	editor.speed = song.speed;	editor.tick = song.tick;	editor.globalVolume = song.globalVolume; 	setLinearPeriods(tmpLinearPeriodsFlag); 	unlockMixerCallback(); 	editor.currVolEnvPoint = 0;	editor.currPanEnvPoint = 0; 	refreshScopes();	exitTextEditing();	updateTextBoxPointers();	resetChannelOffset();	updateChanNums();	resetWavRenderer();	clearPattMark();	resetTrimSizes();	resetPlaybackTime(); 	diskOpSetFilename(DISKOP_ITEM_MODULE, editor.tmpFilenameU); 	// redraw top part of screen	if (ui.extended)	{		togglePatternEditorExtended(); // exit

Lines 1–500 of 790.

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