ft2-clone

Fasttracker 2 clone
Log | Files | Refs | README | LICENSE

ft2_module_saver.c (17728B)


      1 // for finding memory leaks in debug mode with Visual Studio
      2 #if defined _DEBUG && defined _MSC_VER
      3 #include <crtdbg.h>
      4 #endif
      5 
      6 #include <stdio.h>
      7 #include <stdbool.h>
      8 #include "ft2_header.h"
      9 #include "ft2_audio.h"
     10 #include "ft2_gui.h"
     11 #include "ft2_mouse.h"
     12 #include "ft2_sample_ed.h"
     13 #include "ft2_module_loader.h"
     14 #include "ft2_tables.h"
     15 #include "ft2_structs.h"
     16 
     17 static int8_t smpChunkBuf[1024];
     18 static uint8_t packedPattData[65536], modPattData[64*32*4];
     19 static SDL_Thread *thread;
     20 
     21 static const char modIDs[32][5] =
     22 {
     23 	"1CHN", "2CHN", "3CHN", "4CHN", "5CHN", "6CHN", "7CHN", "8CHN",
     24 	"9CHN", "10CH", "11CH", "12CH", "13CH", "14CH", "15CH", "16CH",
     25 	"17CH", "18CH", "19CH", "20CH", "21CH", "22CH", "23CH", "24CH",
     26 	"25CH", "26CH", "27CH", "28CH", "29CH", "30CH", "31CH", "32CH"
     27 };
     28 
     29 static uint16_t packPatt(uint8_t *writePtr, uint8_t *pattPtr, uint16_t numRows);
     30 
     31 bool saveXM(UNICHAR *filenameU)
     32 {
     33 	int16_t i, j, k, a;
     34 	size_t result;
     35 	xmHdr_t h;
     36 	xmPatHdr_t ph;
     37 	instr_t *ins;
     38 	xmInsHdr_t ih;
     39 	sample_t *s;
     40 	xmSmpHdr_t *dst;
     41 
     42 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");
     43 	if (f == NULL)
     44 	{
     45 		okBoxThreadSafe(0, "System message", "Error opening file for saving, is it in use?", NULL);
     46 		return false;
     47 	}
     48 
     49 	memcpy(h.ID, "Extended Module: ", 17);
     50 
     51 	// song name
     52 	int32_t nameLength = (int32_t)strlen(song.name);
     53 	if (nameLength > 20)
     54 		nameLength = 20;
     55 
     56 	memset(h.name, ' ', 20); // yes, FT2 pads the name with spaces
     57 	if (nameLength > 0)
     58 		memcpy(h.name, song.name, nameLength);
     59 
     60 	h.x1A = 0x1A;
     61 
     62 	// program/tracker name
     63 	nameLength = (int32_t)strlen(PROG_NAME_STR);
     64 	if (nameLength > 20)
     65 		nameLength = 20;
     66 
     67 	memset(h.progName, ' ', 20); // yes, FT2 pads the name with spaces
     68 	if (nameLength > 0)
     69 		memcpy(h.progName, PROG_NAME_STR, nameLength);
     70 
     71 	h.version = 0x0104;
     72 	h.headerSize = 20 + 256;
     73 	h.numOrders = song.songLength;
     74 	h.songLoopStart = song.songLoopStart;
     75 	h.numChannels = (uint16_t)song.numChannels;
     76 	h.speed = song.speed;
     77 	h.BPM = song.BPM;
     78 
     79 	// count number of patterns
     80 	i = MAX_PATTERNS;
     81 	do
     82 	{
     83 		if (patternEmpty(i-1))
     84 			i--;
     85 		else
     86 			break;
     87 	}
     88 	while (i > 0);
     89 	h.numPatterns = i;
     90 
     91 	// count number of instruments
     92 	i = 128;
     93 	while (i > 0 && getUsedSamples(i) == 0 && song.instrName[i][0] == '\0')
     94 		i--;
     95 	h.numInstr = i;
     96 
     97 	h.flags = audio.linearPeriodsFlag;
     98 	memcpy(h.orders, song.orders, 256);
     99 
    100 	if (fwrite(&h, sizeof (h), 1, f) != 1)
    101 	{
    102 		fclose(f);
    103 		okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!", NULL);
    104 		return false;
    105 	}
    106 
    107 	for (i = 0; i < h.numPatterns; i++)
    108 	{
    109 		if (patternEmpty(i))
    110 		{
    111 			if (pattern[i] != NULL)
    112 			{
    113 				free(pattern[i]);
    114 				pattern[i] = NULL;
    115 			}
    116 
    117 			patternNumRows[i] = 64;
    118 		}
    119 
    120 		ph.headerSize = sizeof (xmPatHdr_t);
    121 		ph.numRows = patternNumRows[i];
    122 		ph.type = 0;
    123 
    124 		if (pattern[i] == NULL)
    125 		{
    126 			ph.dataSize = 0;
    127 			if (fwrite(&ph, ph.headerSize, 1, f) != 1)
    128 			{
    129 				fclose(f);
    130 				okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!", NULL);
    131 				return false;
    132 			}
    133 		}
    134 		else
    135 		{
    136 			ph.dataSize = packPatt(packedPattData, (uint8_t *)pattern[i], patternNumRows[i]);
    137 
    138 			result = fwrite(&ph, ph.headerSize, 1, f);
    139 			result += fwrite(packedPattData, ph.dataSize, 1, f);
    140 
    141 			if (result != 2) // write was not OK
    142 			{
    143 				fclose(f);
    144 				okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!", NULL);
    145 				return false;
    146 			}
    147 		}
    148 	}
    149 
    150 	memset(&ih, 0, sizeof (ih)); // important, clears reserved stuff
    151 
    152 	for (i = 1; i <= h.numInstr; i++)
    153 	{
    154 		if (instr[i] == NULL)
    155 			j = 0;
    156 		else
    157 			j = i;
    158 
    159 		a = getUsedSamples(i);
    160 
    161 		nameLength = (int32_t)strlen(song.instrName[i]);
    162 		if (nameLength > 22)
    163 			nameLength = 22;
    164 
    165 		memset(ih.name, 0, 22); // pad with zero
    166 		if (nameLength > 0)
    167 			memcpy(ih.name, song.instrName[i], nameLength);
    168 
    169 		ih.type = 0;
    170 		ih.numSamples = a;
    171 		ih.sampleSize = sizeof (xmSmpHdr_t);
    172 
    173 		if (a > 0)
    174 		{
    175 			ins = instr[j];
    176 
    177 			memcpy(ih.note2SampleLUT, ins->note2SampleLUT, 96);
    178 			memcpy(ih.volEnvPoints, ins->volEnvPoints, 12*2*sizeof(int16_t));
    179 			memcpy(ih.panEnvPoints, ins->panEnvPoints, 12*2*sizeof(int16_t));
    180 			ih.volEnvLength = ins->volEnvLength;
    181 			ih.panEnvLength = ins->panEnvLength;
    182 			ih.volEnvSustain = ins->volEnvSustain;
    183 			ih.volEnvLoopStart = ins->volEnvLoopStart;
    184 			ih.volEnvLoopEnd = ins->volEnvLoopEnd;
    185 			ih.panEnvSustain = ins->panEnvSustain;
    186 			ih.panEnvLoopStart = ins->panEnvLoopStart;
    187 			ih.panEnvLoopEnd = ins->panEnvLoopEnd;
    188 			ih.volEnvFlags = ins->volEnvFlags;
    189 			ih.panEnvFlags = ins->panEnvFlags;
    190 			ih.vibType = ins->autoVibType;
    191 			ih.vibSweep = ins->autoVibSweep;
    192 			ih.vibDepth = ins->autoVibDepth;
    193 			ih.vibRate = ins->autoVibRate;
    194 			ih.fadeout = ins->fadeout;
    195 			ih.midiOn = ins->midiOn ? 1 : 0;
    196 			ih.midiChannel = ins->midiChannel;
    197 			ih.midiProgram = ins->midiProgram;
    198 			ih.midiBend = ins->midiBend;
    199 			ih.mute = ins->mute ? 1 : 0;
    200 			ih.instrSize = INSTR_HEADER_SIZE;
    201 			
    202 			for (k = 0; k < a; k++)
    203 			{
    204 				s = &instr[j]->smp[k];
    205 				dst = &ih.smp[k];
    206 
    207 				bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
    208 
    209 				dst->length = s->length;
    210 				dst->loopStart = s->loopStart;
    211 				dst->loopLength = s->loopLength;
    212 
    213 				if (sample16Bit)
    214 				{
    215 					dst->length <<= 1;
    216 					dst->loopStart <<= 1;
    217 					dst->loopLength <<= 1;
    218 				}
    219 
    220 				dst->volume = s->volume;
    221 				dst->finetune = s->finetune;
    222 				dst->flags = s->flags;
    223 				dst->panning = s->panning;
    224 				dst->relativeNote = s->relativeNote;
    225 
    226 				nameLength = (int32_t)strlen(s->name);
    227 				if (nameLength > 22)
    228 					nameLength = 22;
    229 
    230 				dst->nameLength = (uint8_t)nameLength;
    231 
    232 				memset(dst->name, ' ', 22); // yes, FT2 pads the name with spaces
    233 				if (nameLength > 0)
    234 					memcpy(dst->name, s->name, nameLength);
    235 
    236 				if (s->dataPtr == NULL)
    237 					dst->length = 0;
    238 			}
    239 		}
    240 		else
    241 		{
    242 			ih.instrSize = 22 + 11;
    243 		}
    244 
    245 		if (fwrite(&ih, ih.instrSize + (a * sizeof (xmSmpHdr_t)), 1, f) != 1)
    246 		{
    247 			fclose(f);
    248 			okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!", NULL);
    249 			return false;
    250 		}
    251 
    252 		for (k = 1; k <= a; k++)
    253 		{
    254 			s = &instr[j]->smp[k-1];
    255 			if (s->dataPtr != NULL)
    256 			{
    257 				unfixSample(s);
    258 				samp2Delta(s->dataPtr, s->length, s->flags);
    259 
    260 				result = fwrite(s->dataPtr, 1, SAMPLE_LENGTH_BYTES(s), f);
    261 
    262 				delta2Samp(s->dataPtr, s->length, s->flags);
    263 				fixSample(s);
    264 
    265 				if (result != (size_t)SAMPLE_LENGTH_BYTES(s)) // write not OK
    266 				{
    267 					fclose(f);
    268 					okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!", NULL);
    269 					return false;
    270 				}
    271 			}
    272 		}
    273 	}
    274 
    275 	removeSongModifiedFlag();
    276 
    277 	fclose(f);
    278 
    279 	editor.diskOpReadDir = true; // force diskop re-read
    280 
    281 	setMouseBusy(false);
    282 	return true;
    283 }
    284 
    285 static bool saveMOD(UNICHAR *filenameU)
    286 {
    287 	int16_t i;
    288 	int32_t j, k;
    289 	instr_t *ins;
    290 	sample_t *smp;
    291 	modHdr_t hdr;
    292 
    293 	// Commented out. This one was probably confusing to many people...
    294 	/*
    295 	if (audio.linearPeriodsFlag)
    296 		okBoxThreadSafe(0, "System message", "Warning: \"Frequency slides\" is not set to Amiga!");
    297 	*/
    298 
    299 	int32_t songLength = song.songLength;
    300 	if (songLength > 128)
    301 	{
    302 		songLength = 128;
    303 		okBoxThreadSafe(0, "System message", "Warning: Song length is above 128!", NULL);
    304 	}
    305 	
    306 	// calculate number of patterns referenced (max 128 orders)
    307 	int32_t numPatterns = 0;
    308 	for (i = 0; i < songLength; i++)
    309 	{
    310 		if (song.orders[i] > numPatterns)
    311 			numPatterns = song.orders[i];
    312 	}
    313 	numPatterns++;
    314 
    315 	if (numPatterns > 100)
    316 	{
    317 		numPatterns = 100;
    318 		okBoxThreadSafe(0, "System message", "Warning: Song has more than 100 patterns!", NULL);
    319 	}
    320 
    321 	// check if song has more than 31 instruments
    322 	for (i = 32; i <= 128; i++)
    323 	{
    324 		if (getRealUsedSamples(i) > 0)
    325 		{
    326 			okBoxThreadSafe(0, "System message", "Warning: Song has more than 31 instruments!", NULL);
    327 			break;
    328 		}
    329 	}
    330 
    331 	// check if the first 31 samples have a length above 65534 samples
    332 	bool test = false;
    333 	bool test2 = false;
    334 	for (i = 1; i <= 31; i++)
    335 	{
    336 		ins = instr[i];
    337 		if (ins == NULL)
    338 			continue;
    339 
    340 		smp = &ins->smp[0];
    341 
    342 		if (smp->length > 131070)
    343 			test = true;
    344 		else if (smp->length > 65534)
    345 			test2 = true;
    346 	}
    347 	if (test) okBoxThreadSafe(0, "System message", "Warning: Song has sample lengths that are too long for the MOD format!", NULL);
    348 	else if (test2) okBoxThreadSafe(0, "System message", "Warning: Song has sample lengths above 65534! Not all MOD players support this.", NULL);
    349 
    350 	// check if XM instrument features are being used
    351 	test = false;
    352 	for (i = 1; i <= 31; i++)
    353 	{
    354 		ins = instr[i];
    355 		if (ins == NULL)
    356 			continue;
    357 
    358 		smp = &ins->smp[0];
    359 
    360 		j = getRealUsedSamples(i);
    361 		if (j > 1)
    362 		{
    363 			test = true;
    364 			break;
    365 		}
    366 
    367 		if (j == 1)
    368 		{
    369 			if (ins->fadeout != 0 || ins->volEnvFlags != 0 || ins->panEnvFlags != 0 || ins->autoVibRate > 0 ||
    370 				GET_LOOPTYPE(smp->flags) == LOOP_BIDI || smp->relativeNote != 0 || ins->midiOn)
    371 			{
    372 				test = true;
    373 				break;
    374 			}
    375 		}
    376 	}
    377 	if (test) okBoxThreadSafe(0, "System message", "Warning: Song is using XM instrument features!", NULL);
    378 
    379 	bool tooLongPatterns = false;
    380 	bool tooManyInstr = false;
    381 	bool incompatEfx = false;
    382 	bool noteUnderflow = false;
    383 
    384 	for (i = 0; i < numPatterns; i++)
    385 	{
    386 		if (pattern[i] == NULL)
    387 			continue;
    388 
    389 		if (patternNumRows[i] < 64)
    390 		{
    391 			okBoxThreadSafe(0, "System message", "Error: Pattern lengths can't be below 64! Module wasn't saved.", NULL);
    392 			return false;
    393 		}
    394 
    395 		if (patternNumRows[i] > 64)
    396 			tooLongPatterns = true;
    397 
    398 		for (j = 0; j < 64; j++)
    399 		{
    400 			for (k = 0; k < song.numChannels; k++)
    401 			{
    402 				note_t *p = &pattern[i][(j * MAX_CHANNELS) + k];
    403 
    404 				if (p->instr > 31)
    405 					tooManyInstr = true;
    406 
    407 				if (p->efx > 0xF || p->vol != 0)
    408 					incompatEfx = true;
    409 
    410 				// added security that wasn't present in FT2
    411 				if (p->note > 0 && p->note < 10)
    412 					noteUnderflow = true;
    413 			}
    414 		}
    415 	}
    416 
    417 	if (tooLongPatterns) okBoxThreadSafe(0, "System message", "Warning: Song has pattern lengths above 64!", NULL);
    418 	if (tooManyInstr) okBoxThreadSafe(0, "System message", "Warning: Patterns have instrument numbers above 31!", NULL);
    419 	if (incompatEfx) okBoxThreadSafe(0, "System message", "Warning: Patterns have incompatible effects!", NULL);
    420 	if (noteUnderflow) okBoxThreadSafe(0, "System message", "Warning: Patterns have notes below A-0!", NULL);
    421 
    422 	// save module now
    423 
    424 	memset(&hdr, 0, sizeof (hdr));
    425 
    426 	// song name
    427 	int32_t nameLength = (int32_t)strlen(song.name);
    428 	if (nameLength > 20)
    429 		nameLength = 20;
    430 
    431 	memset(hdr.name, 0, 20); // pad with zeroes
    432 	if (nameLength > 0)
    433 		memcpy(hdr.name, song.name, nameLength);
    434 
    435 	hdr.numOrders = (uint8_t)songLength; // pre-clamped to 0..128
    436 
    437 	hdr.songLoopStart = (uint8_t)song.songLoopStart;
    438 	if (hdr.songLoopStart >= hdr.numOrders) // repeat-point must be lower than the song length
    439 		hdr.songLoopStart = 0;
    440 
    441 	memcpy(hdr.orders, song.orders, hdr.numOrders);
    442 
    443 	if (song.numChannels == 4)
    444 		memcpy(hdr.ID, (numPatterns > 64) ? "M!K!" : "M.K.", 4);
    445 	else
    446 		memcpy(hdr.ID, modIDs[song.numChannels-1], 4);
    447 
    448 	// fill MOD sample headers
    449 	for (i = 1; i <= 31; i++)
    450 	{
    451 		modSmpHdr_t *modSmp = &hdr.smp[i-1];
    452 
    453 		nameLength = (int32_t)strlen(song.instrName[i]);
    454 		if (nameLength > 22)
    455 			nameLength = 22;
    456 
    457 		memset(modSmp->name, 0, 22); // pad with zeroes
    458 		if (nameLength > 0)
    459 			memcpy(modSmp->name, song.instrName[i], nameLength);
    460 
    461 		if (instr[i] != NULL && getRealUsedSamples(i) != 0)
    462 		{
    463 			smp = &instr[i]->smp[0];
    464 
    465 			int32_t length = smp->length >> 1;
    466 			int32_t loopStart = smp->loopStart >> 1;
    467 			int32_t loopLength = smp->loopLength >> 1;
    468 
    469 			// length/loopStart/loopLength are now in units of words
    470 
    471 			if (length > UINT16_MAX)
    472 				length = UINT16_MAX;
    473 
    474 			if (GET_LOOPTYPE(smp->flags) == LOOP_OFF)
    475 			{
    476 				loopStart = 0;
    477 				loopLength = 1;
    478 			}
    479 			else // looped sample
    480 			{
    481 				if (loopLength == 0) // ProTracker hates loopLengths of zero
    482 					loopLength = 1;
    483 
    484 				if (loopStart+loopLength > length)
    485 				{
    486 					loopStart = 0;
    487 					loopLength = 1;
    488 				}
    489 			}
    490 
    491 			modSmp->length = (uint16_t)SWAP16(length);
    492 			modSmp->finetune = FINETUNE_XM2MOD(smp->finetune);
    493 			modSmp->volume = smp->volume;
    494 			modSmp->loopStart = (uint16_t)SWAP16(loopStart);
    495 			modSmp->loopLength = (uint16_t)SWAP16(loopLength);
    496 		}
    497 	}
    498 
    499 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");
    500 	if (f == NULL)
    501 	{
    502 		okBoxThreadSafe(0, "System message", "Error opening file for saving, is it in use?", NULL);
    503 		return false;
    504 	}
    505 
    506 	// write header
    507 	if (fwrite(&hdr, 1, sizeof (hdr), f) != sizeof (hdr))
    508 	{
    509 		okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!", NULL);
    510 		goto modSaveError;
    511 	}
    512 
    513 	// write pattern data
    514 	const int32_t patternBytes = song.numChannels * 64 * 4;
    515 	for (i = 0; i < numPatterns; i++)
    516 	{
    517 		if (pattern[i] == NULL) // empty pattern
    518 		{
    519 			memset(modPattData, 0, patternBytes);
    520 		}
    521 		else
    522 		{
    523 			int32_t offs = 0;
    524 			for (j = 0; j < 64; j++)
    525 			{
    526 				for (k = 0; k < song.numChannels; k++)
    527 				{
    528 					note_t *p = &pattern[i][(j * MAX_CHANNELS) + k];
    529 
    530 					uint8_t inst = p->instr;
    531 					uint8_t note = p->note;
    532 
    533 					// FT2 bugfix: prevent overflow
    534 					if (inst > 31)
    535 						inst = 0;
    536 
    537 					// FT2 bugfix: convert note-off into no note for MOD saving
    538 					if (note == NOTE_OFF)
    539 						note = 0;
    540 
    541 					// FT2 bugfix: clamp notes below 10 (A-0) to prevent 12-bit period overflow
    542 					if (note > 0 && note < 10)
    543 						note = 10;
    544 
    545 					if (note == 0)
    546 					{
    547 						modPattData[offs+0] = inst & 0xF0;
    548 						modPattData[offs+1] = 0;
    549 					}
    550 					else
    551 					{
    552 						modPattData[offs+0] = (inst & 0xF0) | ((modPeriods[note-1] >> 8) & 0x0F);
    553 						modPattData[offs+1] = modPeriods[note-1] & 0xFF;
    554 					}
    555 
    556 					// FT2 bugfix: if effect is overflowing (0xF in .MOD), set effect and param to 0
    557 					if (p->efx > 0x0F)
    558 					{
    559 						modPattData[offs+2] = (inst & 0x0F) << 4;
    560 						modPattData[offs+3] = 0;
    561 					}
    562 					else
    563 					{
    564 						modPattData[offs+2] = ((inst & 0x0F) << 4) | (p->efx & 0x0F);
    565 						modPattData[offs+3] = p->efxData;
    566 					}
    567 
    568 					offs += 4;
    569 				}
    570 			}
    571 		}
    572 
    573 		if (fwrite(modPattData, 1, patternBytes, f) != (size_t)patternBytes)
    574 		{
    575 			okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!", NULL);
    576 			goto modSaveError;
    577 		}
    578 	}
    579 
    580 	// write sample data
    581 	for (i = 1; i <= 31; i++)
    582 	{
    583 		if (instr[i] == NULL || getRealUsedSamples(i) == 0)
    584 			continue;
    585 
    586 		smp = &instr[i]->smp[0];
    587 		if (smp->dataPtr == NULL || smp->length <= 0)
    588 			continue;
    589 
    590 		modSmpHdr_t *modSmp = &hdr.smp[i-1];
    591 
    592 		unfixSample(smp);
    593 
    594 		int32_t sampleBytes = SWAP16(modSmp->length) * 2;
    595 
    596 		if (smp->flags & SAMPLE_16BIT) // 16-bit sample (convert to 8-bit)
    597 		{
    598 			int8_t *dstPtr = (int8_t *)smpChunkBuf;
    599 			int32_t writeLen = sampleBytes;
    600 			int32_t samplesWritten = 0;
    601 
    602 			while (samplesWritten < writeLen) // write in chunks
    603 			{
    604 	 			int32_t samplesToWrite = sizeof (smpChunkBuf);
    605 				if (samplesWritten+samplesToWrite > writeLen)
    606 					samplesToWrite = writeLen - samplesWritten;
    607 
    608 				int16_t *srcPtr16 = (int16_t *)smp->dataPtr + samplesWritten;
    609 				for (j = 0; j < samplesToWrite; j++)
    610 					dstPtr[j] = srcPtr16[j] >> 8; // convert 16-bit to 8-bit
    611 
    612 				if (fwrite(dstPtr, 1, samplesToWrite, f) != (size_t)samplesToWrite)
    613 				{
    614 					fixSample(smp);
    615 					okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!", NULL);
    616 					goto modSaveError;
    617 				}
    618 
    619 				samplesWritten += samplesToWrite;
    620 			}
    621 		}
    622 		else // 8-bit sample
    623 		{
    624 			if (fwrite(smp->dataPtr, 1, sampleBytes, f) != (size_t)sampleBytes)
    625 			{
    626 				fixSample(smp);
    627 				okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!", NULL);
    628 				goto modSaveError;
    629 			}
    630 		}
    631 
    632 		fixSample(smp);
    633 	}
    634 
    635 	fclose(f);
    636 	removeSongModifiedFlag();
    637 
    638 	editor.diskOpReadDir = true; // force diskop re-read
    639 
    640 	setMouseBusy(false);
    641 	return true;
    642 
    643 modSaveError:
    644 	fclose(f);
    645 	return false;
    646 }
    647 
    648 static int32_t SDLCALL saveMusicThread(void *ptr)
    649 {
    650 	assert(editor.tmpFilenameU != NULL);
    651 	if (editor.tmpFilenameU == NULL)
    652 		return false;
    653 
    654 	pauseAudio();
    655 
    656 	if (editor.moduleSaveMode == 1)
    657 		saveXM(editor.tmpFilenameU);
    658 	else
    659 		saveMOD(editor.tmpFilenameU);
    660 
    661 	resumeAudio();
    662 	return true;
    663 
    664 	(void)ptr;
    665 }
    666 
    667 void saveMusic(UNICHAR *filenameU)
    668 {
    669 	UNICHAR_STRCPY(editor.tmpFilenameU, filenameU);
    670 
    671 	mouseAnimOn();
    672 	thread = SDL_CreateThread(saveMusicThread, NULL, NULL);
    673 	if (thread == NULL)
    674 	{
    675 		okBoxThreadSafe(0, "System message", "Couldn't create thread!", NULL);
    676 		return;
    677 	}
    678 
    679 	SDL_DetachThread(thread);
    680 }
    681 
    682 static uint16_t packPatt(uint8_t *writePtr, uint8_t *pattPtr, uint16_t numRows)
    683 {
    684 	uint8_t bytes[5];
    685 
    686 	if (pattPtr == NULL)
    687 		return 0;
    688 
    689 	uint16_t totalPackLen = 0;
    690 
    691 	const int32_t pitch = sizeof (note_t) * (MAX_CHANNELS - song.numChannels);
    692 	for (int32_t row = 0; row < numRows; row++)
    693 	{
    694 		for (int32_t chn = 0; chn < song.numChannels; chn++)
    695 		{
    696 			bytes[0] = *pattPtr++;
    697 			bytes[1] = *pattPtr++;
    698 			bytes[2] = *pattPtr++;
    699 			bytes[3] = *pattPtr++;
    700 			bytes[4] = *pattPtr++;
    701 
    702 			uint8_t *firstBytePtr = writePtr++;
    703 
    704 			uint8_t packBits = 0;
    705 			if (bytes[0] > 0) { packBits |= 1; *writePtr++ = bytes[0]; } // note
    706 			if (bytes[1] > 0) { packBits |= 2; *writePtr++ = bytes[1]; } // instrument
    707 			if (bytes[2] > 0) { packBits |= 4; *writePtr++ = bytes[2]; } // volume column
    708 			if (bytes[3] > 0) { packBits |= 8; *writePtr++ = bytes[3]; } // effect
    709 
    710 			if (packBits == 15) // first four bits set?
    711 			{
    712 				// no packing needed, write pattern data as is
    713 
    714 				// point to first byte (and overwrite data)
    715 				writePtr = firstBytePtr;
    716 
    717 				*writePtr++ = bytes[0];
    718 				*writePtr++ = bytes[1];
    719 				*writePtr++ = bytes[2];
    720 				*writePtr++ = bytes[3];
    721 				*writePtr++ = bytes[4];
    722 
    723 				totalPackLen += 5;
    724 				continue;
    725 			}
    726 
    727 			if (bytes[4] > 0) { packBits |= 16; *writePtr++ = bytes[4]; } // effect parameter
    728 
    729 			*firstBytePtr = packBits | 128; // write pack bits byte
    730 			totalPackLen += (uint16_t)(writePtr - firstBytePtr); // bytes writen
    731 		}
    732 
    733 		// skip unused channels (unpacked patterns always have 32 channels)
    734 		pattPtr += pitch;
    735 	}
    736 
    737 	return totalPackLen;
    738 }