Olav SørensenPushed v1.66 codef56d9f63 years agoHistory
738 lines · 17.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 <stdbool.h>#include "ft2_header.h"#include "ft2_audio.h"#include "ft2_gui.h"#include "ft2_mouse.h"#include "ft2_sample_ed.h"#include "ft2_module_loader.h"#include "ft2_tables.h"#include "ft2_structs.h" static int8_t smpChunkBuf[1024];static uint8_t packedPattData[65536], modPattData[64*32*4];static SDL_Thread *thread; static const char modIDs[32][5] ={	"1CHN", "2CHN", "3CHN", "4CHN", "5CHN", "6CHN", "7CHN", "8CHN",	"9CHN", "10CH", "11CH", "12CH", "13CH", "14CH", "15CH", "16CH",	"17CH", "18CH", "19CH", "20CH", "21CH", "22CH", "23CH", "24CH",	"25CH", "26CH", "27CH", "28CH", "29CH", "30CH", "31CH", "32CH"}; static uint16_t packPatt(uint8_t *writePtr, uint8_t *pattPtr, uint16_t numRows); bool saveXM(UNICHAR *filenameU){	int16_t i, j, k, a;	size_t result;	xmHdr_t h;	xmPatHdr_t ph;	instr_t *ins;	xmInsHdr_t ih;	sample_t *s;	xmSmpHdr_t *dst; 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");	if (f == NULL)	{		okBoxThreadSafe(0, "System message", "Error opening file for saving, is it in use?");		return false;	} 	memcpy(h.ID, "Extended Module: ", 17); 	// song name	int32_t nameLength = (int32_t)strlen(song.name);	if (nameLength > 20)		nameLength = 20; 	memset(h.name, ' ', 20); // yes, FT2 pads the name with spaces	if (nameLength > 0)		memcpy(h.name, song.name, nameLength); 	h.x1A = 0x1A; 	// program/tracker name	nameLength = (int32_t)strlen(PROG_NAME_STR);	if (nameLength > 20)		nameLength = 20; 	memset(h.progName, ' ', 20); // yes, FT2 pads the name with spaces	if (nameLength > 0)		memcpy(h.progName, PROG_NAME_STR, nameLength); 	h.version = 0x0104;	h.headerSize = 20 + 256;	h.numOrders = song.songLength;	h.songLoopStart = song.songLoopStart;	h.numChannels = (uint16_t)song.numChannels;	h.speed = song.speed;	h.BPM = song.BPM; 	// count number of patterns	i = MAX_PATTERNS;	do	{		if (patternEmpty(i-1))			i--;		else			break;	}	while (i > 0);	h.numPatterns = i; 	// count number of instruments	i = 128;	while (i > 0 && getUsedSamples(i) == 0 && song.instrName[i][0] == '\0')		i--;	h.numInstr = i; 	h.flags = audio.linearPeriodsFlag;	memcpy(h.orders, song.orders, 256); 	if (fwrite(&h, sizeof (h), 1, f) != 1)	{		fclose(f);		okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");		return false;	} 	for (i = 0; i < h.numPatterns; i++)	{		if (patternEmpty(i))		{			if (pattern[i] != NULL)			{				free(pattern[i]);				pattern[i] = NULL;			} 			patternNumRows[i] = 64;		} 		ph.headerSize = sizeof (xmPatHdr_t);		ph.numRows = patternNumRows[i];		ph.type = 0; 		if (pattern[i] == NULL)		{			ph.dataSize = 0;			if (fwrite(&ph, ph.headerSize, 1, f) != 1)			{				fclose(f);				okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");				return false;			}		}		else		{			ph.dataSize = packPatt(packedPattData, (uint8_t *)pattern[i], patternNumRows[i]); 			result = fwrite(&ph, ph.headerSize, 1, f);			result += fwrite(packedPattData, ph.dataSize, 1, f); 			if (result != 2) // write was not OK			{				fclose(f);				okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");				return false;			}		}	} 	memset(&ih, 0, sizeof (ih)); // important, clears reserved stuff 	for (i = 1; i <= h.numInstr; i++)	{		if (instr[i] == NULL)			j = 0;		else			j = i; 		a = getUsedSamples(i); 		nameLength = (int32_t)strlen(song.instrName[i]);		if (nameLength > 22)			nameLength = 22; 		memset(ih.name, 0, 22); // pad with zero		if (nameLength > 0)			memcpy(ih.name, song.instrName[i], nameLength); 		ih.type = 0;		ih.numSamples = a;		ih.sampleSize = sizeof (xmSmpHdr_t); 		if (a > 0)		{			ins = instr[j]; 			memcpy(ih.note2SampleLUT, ins->note2SampleLUT, 96);			memcpy(ih.volEnvPoints, ins->volEnvPoints, 12*2*sizeof(int16_t));			memcpy(ih.panEnvPoints, ins->panEnvPoints, 12*2*sizeof(int16_t));			ih.volEnvLength = ins->volEnvLength;			ih.panEnvLength = ins->panEnvLength;			ih.volEnvSustain = ins->volEnvSustain;			ih.volEnvLoopStart = ins->volEnvLoopStart;			ih.volEnvLoopEnd = ins->volEnvLoopEnd;			ih.panEnvSustain = ins->panEnvSustain;			ih.panEnvLoopStart = ins->panEnvLoopStart;			ih.panEnvLoopEnd = ins->panEnvLoopEnd;			ih.volEnvFlags = ins->volEnvFlags;			ih.panEnvFlags = ins->panEnvFlags;			ih.vibType = ins->autoVibType;			ih.vibSweep = ins->autoVibSweep;			ih.vibDepth = ins->autoVibDepth;			ih.vibRate = ins->autoVibRate;			ih.fadeout = ins->fadeout;			ih.midiOn = ins->midiOn ? 1 : 0;			ih.midiChannel = ins->midiChannel;			ih.midiProgram = ins->midiProgram;			ih.midiBend = ins->midiBend;			ih.mute = ins->mute ? 1 : 0;			ih.instrSize = INSTR_HEADER_SIZE;						for (k = 0; k < a; k++)			{				s = &instr[j]->smp[k];				dst = &ih.smp[k]; 				bool sample16Bit = !!(s->flags & SAMPLE_16BIT); 				dst->length = s->length;				dst->loopStart = s->loopStart;				dst->loopLength = s->loopLength; 				if (sample16Bit)				{					dst->length <<= 1;					dst->loopStart <<= 1;					dst->loopLength <<= 1;				} 				dst->volume = s->volume;				dst->finetune = s->finetune;				dst->flags = s->flags;				dst->panning = s->panning;				dst->relativeNote = s->relativeNote; 				nameLength = (int32_t)strlen(s->name);				if (nameLength > 22)					nameLength = 22; 				dst->nameLength = (uint8_t)nameLength; 				memset(dst->name, ' ', 22); // yes, FT2 pads the name with spaces				if (nameLength > 0)					memcpy(dst->name, s->name, nameLength); 				if (s->dataPtr == NULL)					dst->length = 0;			}		}		else		{			ih.instrSize = 22 + 11;		} 		if (fwrite(&ih, ih.instrSize + (a * sizeof (xmSmpHdr_t)), 1, f) != 1)		{			fclose(f);			okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");			return false;		} 		for (k = 1; k <= a; k++)		{			s = &instr[j]->smp[k-1];			if (s->dataPtr != NULL)			{				unfixSample(s);				samp2Delta(s->dataPtr, s->length, s->flags); 				result = fwrite(s->dataPtr, 1, SAMPLE_LENGTH_BYTES(s), f); 				delta2Samp(s->dataPtr, s->length, s->flags);				fixSample(s); 				if (result != (size_t)SAMPLE_LENGTH_BYTES(s)) // write not OK				{					fclose(f);					okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!");					return false;				}			}		}	} 	removeSongModifiedFlag(); 	fclose(f); 	editor.diskOpReadDir = true; // force diskop re-read 	setMouseBusy(false);	return true;} static bool saveMOD(UNICHAR *filenameU){	int16_t i;	int32_t j, k;	instr_t *ins;	sample_t *smp;	modHdr_t hdr; 	// Commented out. This one was probably confusing to many people...	/*	if (audio.linearPeriodsFlag)		okBoxThreadSafe(0, "System message", "Warning: \"Frequency slides\" is not set to Amiga!");	*/ 	int32_t songLength = song.songLength;	if (songLength > 128)	{		songLength = 128;		okBoxThreadSafe(0, "System message", "Warning: Song length is above 128!");	}		// calculate number of patterns referenced (max 128 orders)	int32_t numPatterns = 0;	for (i = 0; i < songLength; i++)	{		if (song.orders[i] > numPatterns)			numPatterns = song.orders[i];	}	numPatterns++; 	if (numPatterns > 100)	{		numPatterns = 100;		okBoxThreadSafe(0, "System message", "Warning: Song has more than 100 patterns!");	} 	// check if song has more than 31 instruments	for (i = 32; i <= 128; i++)	{		if (getRealUsedSamples(i) > 0)		{			okBoxThreadSafe(0, "System message", "Warning: Song has more than 31 instruments!");			break;		}	} 	// check if the first 31 samples have a length above 65534 samples	bool test = false;	bool test2 = false;	for (i = 1; i <= 31; i++)	{		ins = instr[i];		if (ins == NULL)			continue; 		smp = &ins->smp[0]; 		if (smp->length > 131070)			test = true;		else if (smp->length > 65534)			test2 = true;	}	if (test) okBoxThreadSafe(0, "System message", "Warning: Song has sample lengths that are too long for the MOD format!");	else if (test2) okBoxThreadSafe(0, "System message", "Warning: Song has sample lengths above 65534! Not all MOD players support this."); 	// check if XM instrument features are being used	test = false;	for (i = 1; i <= 31; i++)	{		ins = instr[i];		if (ins == NULL)			continue; 		smp = &ins->smp[0]; 		j = getRealUsedSamples(i);		if (j > 1)		{			test = true;			break;		} 		if (j == 1)		{			if (ins->fadeout != 0 || ins->volEnvFlags != 0 || ins->panEnvFlags != 0 || ins->autoVibRate > 0 ||				GET_LOOPTYPE(smp->flags) == LOOP_BIDI || smp->relativeNote != 0 || ins->midiOn)			{				test = true;				break;			}		}	}	if (test) okBoxThreadSafe(0, "System message", "Warning: Song is using XM instrument features!"); 	bool tooLongPatterns = false;	bool tooManyInstr = false;	bool incompatEfx = false;	bool noteUnderflow = false; 	for (i = 0; i < numPatterns; i++)	{		if (pattern[i] == NULL)			continue; 		if (patternNumRows[i] < 64)		{			okBoxThreadSafe(0, "System message", "Error: Pattern lengths can't be below 64! Module wasn't saved.");			return false;		} 		if (patternNumRows[i] > 64)			tooLongPatterns = true; 		for (j = 0; j < 64; j++)		{			for (k = 0; k < song.numChannels; k++)			{				note_t *p = &pattern[i][(j * MAX_CHANNELS) + k]; 				if (p->instr > 31)					tooManyInstr = true; 				if (p->efx > 0xF || p->vol != 0)					incompatEfx = true; 				// added security that wasn't present in FT2				if (p->note > 0 && p->note < 10)					noteUnderflow = true;			}		}	} 	if (tooLongPatterns) okBoxThreadSafe(0, "System message", "Warning: Song has pattern lengths above 64!");	if (tooManyInstr) okBoxThreadSafe(0, "System message", "Warning: Patterns have instrument numbers above 31!");	if (incompatEfx) okBoxThreadSafe(0, "System message", "Warning: Patterns have incompatible effects!");	if (noteUnderflow) okBoxThreadSafe(0, "System message", "Warning: Patterns have notes below A-0!"); 	// save module now 	memset(&hdr, 0, sizeof (hdr)); 	// song name	int32_t nameLength = (int32_t)strlen(song.name);	if (nameLength > 20)		nameLength = 20; 	memset(hdr.name, 0, 20); // pad with zeroes	if (nameLength > 0)		memcpy(hdr.name, song.name, nameLength); 	hdr.numOrders = (uint8_t)songLength; // pre-clamped to 0..128 	hdr.songLoopStart = (uint8_t)song.songLoopStart;	if (hdr.songLoopStart >= hdr.numOrders) // repeat-point must be lower than the song length		hdr.songLoopStart = 0; 	memcpy(hdr.orders, song.orders, hdr.numOrders); 	if (song.numChannels == 4)		memcpy(hdr.ID, (numPatterns > 64) ? "M!K!" : "M.K.", 4);	else		memcpy(hdr.ID, modIDs[song.numChannels-1], 4); 	// fill MOD sample headers	for (i = 1; i <= 31; i++)	{		modSmpHdr_t *modSmp = &hdr.smp[i-1]; 		nameLength = (int32_t)strlen(song.instrName[i]);		if (nameLength > 22)			nameLength = 22; 		memset(modSmp->name, 0, 22); // pad with zeroes		if (nameLength > 0)			memcpy(modSmp->name, song.instrName[i], nameLength); 		if (instr[i] != NULL && getRealUsedSamples(i) != 0)		{			smp = &instr[i]->smp[0]; 			int32_t length = smp->length >> 1;			int32_t loopStart = smp->loopStart >> 1;			int32_t loopLength = smp->loopLength >> 1; 			// length/loopStart/loopLength are now in units of words 			if (length > UINT16_MAX)				length = UINT16_MAX; 			if (GET_LOOPTYPE(smp->flags) == LOOP_OFF)			{				loopStart = 0;				loopLength = 1;			}			else // looped sample			{				if (loopLength == 0) // ProTracker hates loopLengths of zero					loopLength = 1; 				if (loopStart+loopLength > length)				{					loopStart = 0;					loopLength = 1;				}			} 			modSmp->length = (uint16_t)SWAP16(length);			modSmp->finetune = FINETUNE_XM2MOD(smp->finetune);			modSmp->volume = smp->volume;			modSmp->loopStart = (uint16_t)SWAP16(loopStart);			modSmp->loopLength = (uint16_t)SWAP16(loopLength);		}	} 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");	if (f == NULL)

Lines 1–500 of 738.

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