shithub: pt2-clone

ref: 7c4e284d754b20615033274d02969a3bea691c45
dir: /src/pt2_module_saver.c/

View raw version
// for finding memory leaks in debug mode with Visual Studio 
#if defined _DEBUG && defined _MSC_VER
#include <crtdbg.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <sys/stat.h>
#include "pt2_mouse.h"
#include "pt2_header.h"
#include "pt2_textout.h"
#include "pt2_helpers.h"
#include "pt2_visuals.h"
#include "pt2_sampler.h"
#include "pt2_config.h"

bool modSave(char *fileName)
{
	int32_t i, j;
	FILE *f;

	f = fopen(fileName, "wb");
	if (f == NULL)
	{
		displayErrorMsg("FILE I/O ERROR !");
		return false;
	}

	fwrite(song->header.name, 1, 20, f);

	for (i = 0; i < MOD_SAMPLES; i++)
	{
		moduleSample_t *s = &song->samples[i];

		fwrite(s->text, 1, 22, f);

		uint16_t length = SWAP16(s->length >> 1);
		fwrite(&length, sizeof (int16_t), 1, f);

		fputc(s->fineTune & 0xF, f);
		fputc(((uint8_t)s->volume > 64) ? 64 : s->volume, f);

		int32_t loopStart = s->loopStart;
		int32_t loopLength = s->loopLength;

		if (loopLength < 2)
			loopLength = 2;

		if (loopStart+loopLength <= 2 || loopStart+loopLength > s->length)
		{
			loopStart = 0;
			loopLength = 2;
		}

		uint16_t loopStart16 = SWAP16(loopStart >> 1);
		uint16_t loopLength16 = SWAP16(loopLength >> 1);

		fwrite(&loopStart16, sizeof (int16_t), 1, f);
		fwrite(&loopLength16, sizeof (int16_t), 1, f);
	}

	fputc((uint8_t)song->header.numOrders, f);
	fputc(0x7F, f); // ProTracker puts 0x7F at this place (restartPos/tempo in other trackers)

	for (i = 0; i < MOD_ORDERS; i++)
		fputc((uint8_t)song->header.order[i], f);

	int32_t numPatterns = 0;
	for (i = 0; i < MOD_ORDERS; i++)
	{
		if (song->header.order[i] > numPatterns)
			numPatterns = song->header.order[i];
	}

	numPatterns++;
	if (numPatterns > MAX_PATTERNS)
		numPatterns = MAX_PATTERNS;

	fwrite((numPatterns <= 64) ? "M.K." : "M!K!", 1, 4, f);

	for (i = 0; i < numPatterns; i++)
	{
		note_t *note = song->patterns[i];
		for (j = 0; j < MOD_ROWS * AMIGA_VOICES; j++, note++)
		{
			fputc((note->sample & 0xF0) | (note->period >> 8), f);
			fputc(note->period & 0xFF, f);
			fputc(((note->sample << 4) & 0xF0) | (note->command & 0x0F), f);
			fputc(note->param, f);
		}
	}

	for (i = 0; i < MOD_SAMPLES; i++)
	{
		moduleSample_t *s = &song->samples[i];
		const int8_t *smpPtr8 = &song->sampleData[s->offset];

		// clear first two bytes of non-looping samples (prevents stuck "BEEEEEP")
		if (s->length >= 2 && s->loopStart+s->loopLength == 2)
		{
			fputc(0, f);
			fputc(0, f);

			fwrite(&smpPtr8[2], 1, s->length-2, f);
		}
		else
		{
			fwrite(smpPtr8, 1, s->length, f);
		}
	}

	fclose(f);

	displayMsg("MODULE SAVED !");
	setMsgPointer();

	diskop.cached = false;
	if (ui.diskOpScreenShown)
		ui.updateDiskOpFileList = true;

	updateWindowTitle(MOD_NOT_MODIFIED);
	return true;
}

bool saveModule(bool checkIfFileExist, bool giveNewFreeFilename)
{
	char fileName[128], tmpBuffer[64];
	int32_t i, j;
	struct stat statBuffer;

	memset(tmpBuffer, 0, sizeof (tmpBuffer));
	memset(fileName, 0, sizeof (fileName));

	if (config.modDot)
	{
		// extension.filename
		if (song->header.name[0] == '\0')
		{
			strcat(fileName, "mod.untitled");
		}
		else
		{
			strcat(fileName, "mod.");
			for (i = 4; i < 20+4; i++)
			{
				fileName[i] = (char)tolower(song->header.name[i-4]);
				if (fileName[i] == '\0') break;
				sanitizeFilenameChar(&fileName[i]);
			}
		}
	}
	else
	{
		// filename.extension
		if (song->header.name[0] == '\0')
		{
			strcat(fileName, "untitled.mod");
		}
		else
		{
			for (i = 0; i < 20; i++)
			{
				fileName[i] = (char)tolower(song->header.name[i]);
				if (fileName[i] == '\0') break;
				sanitizeFilenameChar(&fileName[i]);
			}
			strcat(fileName, ".mod");
		}
	}

	if (giveNewFreeFilename && stat(fileName, &statBuffer) == 0)
	{
		for (i = 1; i <= 999; i++)
		{
			if (config.modDot)
			{
				// extension.filename
				if (song->header.name[0] == '\0')
				{
					sprintf(fileName, "mod.untitled-%d", i);
				}
				else
				{
					for (j = 0; j < 20; j++)
					{
						tmpBuffer[j] = (char)tolower(song->header.name[j]);
						if (tmpBuffer[j] == '\0') break;
						sanitizeFilenameChar(&tmpBuffer[j]);
					}
					sprintf(fileName, "mod.%s-%d", tmpBuffer, i);
				}
			}
			else
			{
				// filename.extension
				if (song->header.name[0] == '\0')
				{
					sprintf(fileName, "untitled-%d.mod", i);
				}
				else
				{
					for (j = 0; j < 20; j++)
					{
						tmpBuffer[j] = (char)tolower(song->header.name[j]);
						if (tmpBuffer[j] == '\0') break;
						sanitizeFilenameChar(&tmpBuffer[j]);
					}
					sprintf(fileName, "%s-%d.mod", tmpBuffer, i);
				}
			}

			if (stat(fileName, &statBuffer) != 0)
				break; // this filename can be used
		}
	}

	if (checkIfFileExist && stat(fileName, &statBuffer) == 0)
	{
		ui.askScreenShown = true;
		ui.askScreenType = ASK_SAVEMOD_OVERWRITE;
		pointerSetMode(POINTER_MODE_MSG1, NO_CARRY);
		setStatusMessage("OVERWRITE FILE ?", NO_CARRY);
		renderAskDialog();
		return -1;
	}

	if (ui.askScreenShown)
	{
		ui.answerNo = false;
		ui.answerYes = false;
		ui.askScreenShown = false;
	}

	return modSave(fileName);
}