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 <stdint.h>
7 #include <stdbool.h>
8 #include <stdio.h>
9 #include <math.h>
10 #include "ft2_header.h"
11 #include "ft2_sample_ed.h"
12 #include "ft2_gui.h"
13 #include "scopes/ft2_scopes.h"
14 #include "ft2_pattern_ed.h"
15 #include "ft2_replayer.h"
16 #include "ft2_audio.h"
17 #include "ft2_mouse.h"
18 #include "ft2_structs.h"
19 
20 // this is truly a mess, but it works...
21 
22 static char byteFormatBuffer[64], tmpInstrName[1 + MAX_INST][22 + 1], tmpInstName[MAX_INST][22 + 1];
23 static bool removePatt, removeInst, removeSamp, removeChans, removeSmpDataAfterLoop, convSmpsTo8Bit;
24 static uint8_t instrUsed[MAX_INST], instrOrder[MAX_INST], pattUsed[MAX_PATTERNS], pattOrder[MAX_PATTERNS];
25 static int16_t oldPattLens[MAX_PATTERNS], tmpPattLens[MAX_PATTERNS];
26 static int64_t xmSize64 = -1, xmAfterTrimSize64 = -1, spaceSaved64 = -1;
27 static note_t *oldPatts[MAX_PATTERNS], *tmpPatt[MAX_PATTERNS];
28 static instr_t *tmpInstr[1 + MAX_INST], *tmpInst[MAX_INST]; // tmpInstr[x] = copy of instr[x] for "after trim" size calculation
29 static SDL_Thread *trimThread;
30 
31 void pbTrimCalc(void);
32 
freeTmpInstruments(void)33 static void freeTmpInstruments(void)
34 {
35 	for (int32_t i = 0; i <= MAX_INST; i++)
36 	{
37 		if (tmpInstr[i] != NULL)
38 		{
39 			// don't free samples, as the pointers are shared with main instruments...
40 
41 			free(tmpInstr[i]);
42 			tmpInstr[i] = NULL;
43 		}
44 	}
45 }
46 
setTmpInstruments(void)47 static bool setTmpInstruments(void)
48 {
49 	freeTmpInstruments();
50 
51 	for (int32_t i = 0; i <= MAX_INST; i++)
52 	{
53 		if (instr[i] != NULL)
54 		{
55 			tmpInstr[i] = (instr_t *)malloc(sizeof (instr_t));
56 			if (tmpInstr[i] == NULL)
57 			{
58 				freeTmpInstruments();
59 				return false;
60 			}
61 
62 			*tmpInstr[i] = *instr[i];
63 		}
64 	}
65 
66 	return true;
67 }
68 
remapInstrInSong(uint8_t src,uint8_t dst,int32_t ap)69 static void remapInstrInSong(uint8_t src, uint8_t dst, int32_t ap)
70 {
71 	for (int32_t i = 0; i < ap; i++)
72 	{
73 		note_t *pattPtr = pattern[i];
74 		if (pattPtr == NULL)
75 			continue;
76 
77 		const int32_t readLen = patternNumRows[i] * MAX_CHANNELS;
78 
79 		note_t *p = pattPtr;
80 		for (int32_t j = 0; j < readLen; j++, p++)
81 		{
82 			if (p->instr == src)
83 				p->instr = dst;
84 		}
85 	}
86 }
87 
getUsedTempSamples(uint16_t insNum)88 static int16_t getUsedTempSamples(uint16_t insNum)
89 {
90 	if (tmpInstr[insNum] == NULL)
91 		return 0;
92 
93 	instr_t *ins = tmpInstr[insNum];
94 
95 	int16_t i = 16 - 1;
96 	while (i >= 0 && ins->smp[i].dataPtr == NULL && ins->smp[i].name[0] == '\0')
97 		i--;
98 
99 	/* Yes, 'i' can be -1 here, and will be set to at least 0
100 	** because of ins->ta values. Possibly an FT2 bug...
101 	**/
102 	for (int16_t j = 0; j < 96; j++)
103 	{
104 		if (ins->note2SampleLUT[j] > i)
105 			i = ins->note2SampleLUT[j];
106 	}
107 
108 	return i+1;
109 }
110 
getTempInsAndSmpSize(void)111 static int64_t getTempInsAndSmpSize(void)
112 {
113 	int16_t j;
114 
115 	int16_t ai = MAX_INST;
116 	while (ai > 0 && getUsedTempSamples(ai) == 0 && tmpInstrName[ai][0] == '\0')
117 		ai--;
118 
119 	int64_t currSize64 = 0;
120 
121 	// count instrument and sample data size in song
122 	for (int16_t i = 1; i <= ai; i++)
123 	{
124 		if (tmpInstr[i] == NULL)
125 			j = 0;
126 		else
127 			j = i;
128 
129 		const int16_t a = getUsedTempSamples(i);
130 		if (a > 0)
131 			currSize64 += INSTR_HEADER_SIZE + (a * sizeof (xmSmpHdr_t));
132 		else
133 			currSize64 += 22+11;
134 
135 		instr_t *ins = tmpInstr[j];
136 		for (int16_t k = 0; k < a; k++)
137 		{
138 			sample_t *s = &ins->smp[k];
139 			if (s->dataPtr != NULL && s->length > 0)
140 			{
141 				if (s->flags & SAMPLE_16BIT)
142 					currSize64 += s->length << 1;
143 				else
144 					currSize64 += s->length;
145 			}
146 		}
147 	}
148 
149 	return currSize64;
150 }
151 
wipeInstrUnused(bool testWipeSize,int16_t * ai,int32_t ap,int32_t antChn)152 static void wipeInstrUnused(bool testWipeSize, int16_t *ai, int32_t ap, int32_t antChn)
153 {
154 	int16_t numRows;
155 	int32_t i, j, k;
156 	note_t *p;
157 
158 	int32_t numInsts = *ai;
159 
160 	// calculate what instruments are used
161 	memset(instrUsed, 0, numInsts);
162 	for (i = 0; i < ap; i++)
163 	{
164 		if (testWipeSize)
165 		{
166 			p = tmpPatt[i];
167 			numRows = tmpPattLens[i];
168 		}
169 		else
170 		{
171 			p = pattern[i];
172 			numRows = patternNumRows[i];
173 		}
174 
175 		if (p == NULL)
176 			continue;
177 
178 		for (j = 0; j < numRows; j++)
179 		{
180 			for (k = 0; k < antChn; k++)
181 			{
182 				uint8_t ins = p[(j * MAX_CHANNELS) + k].instr;
183 				if (ins > 0 && ins <= MAX_INST)
184 					instrUsed[ins-1] = true;
185 			}
186 		}
187 	}
188 
189 	int16_t instToDel = 0;
190 	uint8_t newInst = 0;
191 	int16_t newNumInsts = 0;
192 
193 	memset(instrOrder, 0, numInsts);
194 	for (i = 0; i < numInsts; i++)
195 	{
196 		if (instrUsed[i])
197 		{
198 			newNumInsts++;
199 			instrOrder[i] = newInst++;
200 		}
201 		else
202 		{
203 			instToDel++;
204 		}
205 	}
206 
207 	if (instToDel == 0)
208 		return;
209 
210 	if (testWipeSize)
211 	{
212 		for (i = 0; i < numInsts; i++)
213 		{
214 			if (!instrUsed[i] && tmpInstr[1+i] != NULL)
215 			{
216 				free(tmpInstr[1+i]);
217 				tmpInstr[1+i] = NULL;
218 			}
219 		}
220 
221 		// relocate instruments
222 
223 		memcpy(tmpInstName, &tmpInstrName[1], MAX_INST * sizeof (song.instrName[0]));
224 		memcpy(tmpInst, &tmpInstr[1], MAX_INST * sizeof (instr[0]));
225 
226 		memset(&tmpInstr[1], 0, numInsts * sizeof (tmpInstr[0]));
227 		memset(&tmpInstrName[1], 0, numInsts * sizeof (tmpInstrName[0]));
228 
229 		for (i = 0; i < numInsts; i++)
230 		{
231 			if (instrUsed[i])
232 			{
233 				newInst = instrOrder[i];
234 
235 				memcpy(&tmpInstr[1+newInst], &tmpInst[i], sizeof (tmpInst[0]));
236 				strcpy(tmpInstrName[1+newInst], tmpInstName[i]);
237 			}
238 		}
239 
240 		*ai = newNumInsts;
241 		return;
242 	}
243 
244 	// clear unused instruments
245 	for (i = 0; i < numInsts; i++)
246 	{
247 		if (!instrUsed[i])
248 			freeInstr(1 + i);
249 	}
250 
251 	// relocate instruments
252 
253 	memcpy(tmpInstName, &song.instrName[1], MAX_INST * sizeof (song.instrName[0]));
254 	memcpy(tmpInst, &instr[1], MAX_INST * sizeof (instr[0]));
255 
256 	memset(&instr[1], 0, numInsts * sizeof (instr[0]));
257 	memset(song.instrName[1], 0, numInsts * sizeof (song.instrName[0])); // XXX: Is this safe?
258 
259 	for (i = 0; i < numInsts; i++)
260 	{
261 		if (instrUsed[i])
262 		{
263 			newInst = instrOrder[i];
264 			remapInstrInSong(1 + (uint8_t)i, 1 + newInst, ap);
265 
266 			memcpy(&instr[1+newInst], &tmpInst[i], sizeof (instr[0]));
267 			strcpy(song.instrName[1+newInst], tmpInstName[i]);
268 		}
269 	}
270 
271 	*ai = newNumInsts;
272 
273 	setTmpInstruments();
274 }
275 
wipePattsUnused(bool testWipeSize,int16_t * ap)276 static void wipePattsUnused(bool testWipeSize, int16_t *ap)
277 {
278 	uint8_t newPatt;
279 	int16_t i, *pLens;
280 	note_t **p;
281 
282 	int16_t usedPatts = *ap;
283 	memset(pattUsed, 0, usedPatts);
284 
285 	int16_t newUsedPatts = 0;
286 	for (i = 0; i < song.songLength; i++)
287 	{
288 		newPatt = song.orders[i];
289 		if (newPatt < usedPatts && !pattUsed[newPatt])
290 		{
291 			pattUsed[newPatt] = true;
292 			newUsedPatts++;
293 		}
294 	}
295 
296 	if (newUsedPatts == 0 || newUsedPatts == usedPatts)
297 		return; // nothing to do!
298 
299 	newPatt = 0;
300 	memset(pattOrder, 0, usedPatts);
301 	for (i = 0; i < usedPatts; i++)
302 	{
303 		if (pattUsed[i])
304 			pattOrder[i] = newPatt++;
305 	}
306 
307 	if (testWipeSize)
308 	{
309 		p = tmpPatt;
310 		pLens = tmpPattLens;
311 	}
312 	else
313 	{
314 		p = pattern;
315 		pLens = patternNumRows;
316 	}
317 
318 	memcpy(oldPatts, p, usedPatts * sizeof (note_t *));
319 	memcpy(oldPattLens, pLens, usedPatts * sizeof (int16_t));
320 	memset(p, 0, usedPatts * sizeof (note_t *));
321 	memset(pLens, 0, usedPatts * sizeof (int16_t));
322 
323 	// relocate patterns
324 	for (i = 0; i < usedPatts; i++)
325 	{
326 		p[i] = NULL;
327 
328 		if (!pattUsed[i])
329 		{
330 			if (!testWipeSize && oldPatts[i] != NULL)
331 			{
332 				free(oldPatts[i]);
333 				oldPatts[i] = NULL;
334 			}
335 		}
336 		else
337 		{
338 			newPatt = pattOrder[i];
339 			p[newPatt] = oldPatts[i];
340 			pLens[newPatt] = oldPattLens[i];
341 		}
342 	}
343 
344 	if (!testWipeSize)
345 	{
346 		for (i = 0; i < MAX_PATTERNS; i++)
347 		{
348 			if (pattern[i] == NULL)
349 				patternNumRows[i] = 64;
350 		}
351 
352 		// reorder order list (and clear unused entries)
353 		for (i = 0; i < 256; i++)
354 		{
355 			if (i < song.songLength)
356 				song.orders[i] = pattOrder[song.orders[i]];
357 			else
358 				song.orders[i] = 0;
359 		}
360 	}
361 
362 	*ap = newUsedPatts;
363 }
364 
wipeSamplesUnused(bool testWipeSize,int16_t ai)365 static void wipeSamplesUnused(bool testWipeSize, int16_t ai)
366 {
367 	uint8_t smpUsed[16], smpOrder[16];
368 	int16_t j, k, l;
369 	instr_t *ins;
370 	sample_t tempSamples[16];
371 
372 	for (int16_t i = 1; i <= ai; i++)
373 	{
374 		if (!testWipeSize)
375 		{
376 			if (instr[i] == NULL)
377 				l = 0;
378 			else
379 				l = i;
380 
381 			ins = instr[l];
382 			l = getUsedSamples(i);
383 		}
384 		else
385 		{
386 			if (tmpInstr[i] == NULL)
387 				l = 0;
388 			else
389 				l = i;
390 
391 			ins = tmpInstr[l];
392 			l = getUsedTempSamples(i);
393 		}
394 
395 		memset(smpUsed, 0, l);
396 		if (l > 0)
397 		{
398 			sample_t *s = ins->smp;
399 			for (j = 0; j < l; j++, s++)
400 			{
401 				// check if sample is referenced in instrument
402 				for (k = 0; k < 96; k++)
403 				{
404 					if (ins->note2SampleLUT[k] == j)
405 					{
406 						smpUsed[j] = true;
407 						break; // sample is used
408 					}
409 				}
410 
411 				if (k == 96)
412 				{
413 					// sample is unused
414 
415 					if (s->dataPtr != NULL && !testWipeSize)
416 						freeSmpData(s);
417 
418 					memset(s, 0, sizeof (sample_t));
419 				}
420 			}
421 
422 			// create re-order list
423 			uint8_t newSamp = 0;
424 			memset(smpOrder, 0, l);
425 			for (j = 0; j < l; j++)
426 			{
427 				if (smpUsed[j])
428 					smpOrder[j] = newSamp++;
429 			}
430 
431 			// re-order samples
432 
433 			memcpy(tempSamples, ins->smp, l * sizeof (sample_t));
434 			memset(ins->smp, 0, l * sizeof (sample_t));
435 
436 			for (j = 0; j < l; j++)
437 			{
438 				if (smpUsed[j])
439 					ins->smp[smpOrder[j]] = tempSamples[j];
440 			}
441 
442 			// re-order note->sample list
443 			for (j = 0; j < 96; j++)
444 			{
445 				newSamp = ins->note2SampleLUT[j];
446 				if (smpUsed[newSamp])
447 					ins->note2SampleLUT[j] = smpOrder[newSamp];
448 				else
449 					ins->note2SampleLUT[j] = 0;
450 			}
451 		}
452 	}
453 }
454 
wipeSmpDataAfterLoop(bool testWipeSize,int16_t ai)455 static void wipeSmpDataAfterLoop(bool testWipeSize, int16_t ai)
456 {
457 	int16_t l;
458 	instr_t *ins;
459 
460 	for (int16_t i = 1; i <= ai; i++)
461 	{
462 		if (!testWipeSize)
463 		{
464 			if (instr[i] == NULL)
465 				l = 0;
466 			else
467 				l = i;
468 
469 			ins = instr[l];
470 			l = getUsedSamples(i);
471 		}
472 		else
473 		{
474 			if (tmpInstr[i] == NULL)
475 				l = 0;
476 			else
477 				l = i;
478 
479 			ins = tmpInstr[l];
480 			l = getUsedTempSamples(i);
481 		}
482 
483 		sample_t *s = ins->smp;
484 		for (int16_t j = 0; j < l; j++, s++)
485 		{
486 			if (s->dataPtr != NULL && GET_LOOPTYPE(s->flags) != LOOP_OFF && s->length > 0 && s->length > s->loopStart+s->loopLength)
487 			{
488 				if (!testWipeSize)
489 					unfixSample(s);
490 
491 				s->length = s->loopStart + s->loopLength;
492 				if (!testWipeSize)
493 				{
494 					if (s->length <= 0)
495 					{
496 						s->length = 0;
497 						freeSmpData(s);
498 					}
499 					else
500 					{
501 						reallocateSmpData(s, s->length, !!(s->flags & SAMPLE_16BIT));
502 					}
503 				}
504 
505 				if (!testWipeSize)
506 					fixSample(s);
507 			}
508 		}
509 	}
510 }
511 
convertSamplesTo8bit(bool testWipeSize,int16_t ai)512 static void convertSamplesTo8bit(bool testWipeSize, int16_t ai)
513 {
514 	int16_t k;
515 	instr_t *ins;
516 
517 	for (int16_t i = 1; i <= ai; i++)
518 	{
519 		if (!testWipeSize)
520 		{
521 			if (instr[i] == NULL)
522 				k = 0;
523 			else
524 				k = i;
525 
526 			ins = instr[k];
527 			k = getUsedSamples(i);
528 		}
529 		else
530 		{
531 			if (tmpInstr[i] == NULL)
532 				k = 0;
533 			else
534 				k = i;
535 
536 			ins = tmpInstr[k];
537 			k = getUsedTempSamples(i);
538 		}
539 
540 		sample_t *s = ins->smp;
541 		for (int16_t j = 0; j < k; j++, s++)
542 		{
543 			if (s->dataPtr != NULL && s->length > 0 && (s->flags & SAMPLE_16BIT))
544 			{
545 				if (testWipeSize)
546 				{
547 					s->flags &= ~SAMPLE_16BIT;
548 				}
549 				else
550 				{
551 					unfixSample(s);
552 
553 					const int16_t *src16 = (const int16_t *)s->dataPtr;
554 					int8_t *dst8 = s->dataPtr;
555 
556 					for (int32_t a = 0; a < s->length; a++)
557 						dst8[a] = src16[a] >> 8;
558 
559 					s->flags &= ~SAMPLE_16BIT;
560 
561 					reallocateSmpData(s, s->length, true);
562 					fixSample(s);
563 				}
564 			}
565 		}
566 	}
567 }
568 
getPackedPattSize(note_t * p,int32_t numRows,int32_t antChn)569 static uint16_t getPackedPattSize(note_t *p, int32_t numRows, int32_t antChn)
570 {
571 	uint8_t bytes[sizeof (note_t)];
572 
573 	uint16_t totalPackLen = 0;
574 	uint8_t *pattPtr = (uint8_t *)p;
575 	uint8_t *writePtr = pattPtr;
576 
577 	for (int32_t row = 0; row < numRows; row++)
578 	{
579 		for (int32_t chn = 0; chn < antChn; chn++)
580 		{
581 			bytes[0] = *pattPtr++;
582 			bytes[1] = *pattPtr++;
583 			bytes[2] = *pattPtr++;
584 			bytes[3] = *pattPtr++;
585 			bytes[4] = *pattPtr++;
586 
587 			uint8_t *firstBytePtr = writePtr++;
588 
589 			uint8_t packBits = 0;
590 			if (bytes[0] > 0) { packBits |= 1; writePtr++; } // note
591 			if (bytes[1] > 0) { packBits |= 2; writePtr++; } // instrument
592 			if (bytes[2] > 0) { packBits |= 4; writePtr++; } // volume column
593 			if (bytes[3] > 0) { packBits |= 8; writePtr++; } // effect
594 
595 			if (packBits == 15) // first four bits set?
596 			{
597 				// no packing needed, write pattern data as is
598 				totalPackLen += 5;
599 				writePtr += 5;
600 				continue;
601 			}
602 
603 			if (bytes[4] > 0) writePtr++; // effect parameter
604 
605 			totalPackLen += (uint16_t)(writePtr - firstBytePtr); // bytes writen
606 		}
607 
608 		// skip unused channels
609 		pattPtr += sizeof (note_t) * (MAX_CHANNELS - antChn);
610 	}
611 
612 	return totalPackLen;
613 }
614 
tmpPatternEmpty(uint16_t pattNum,int32_t numChannels)615 static bool tmpPatternEmpty(uint16_t pattNum, int32_t numChannels)
616 {
617 	if (tmpPatt[pattNum] == NULL)
618 		return true;
619 
620 	uint8_t *scanPtr = (uint8_t *)tmpPatt[pattNum];
621 	int32_t scanLen = numChannels * sizeof (note_t);
622 	int32_t numRows = tmpPattLens[pattNum];
623 
624 	for (int32_t i = 0; i < numRows; i++, scanPtr += TRACK_WIDTH)
625 	{
626 		for (int32_t j = 0; j < scanLen; j++)
627 		{
628 			if (scanPtr[j] != 0)
629 				return false;
630 		}
631 	}
632 
633 	return true;
634 }
635 
calculateXMSize(void)636 static int64_t calculateXMSize(void)
637 {
638 	// count header size in song
639 	int64_t currSize64 = sizeof (xmHdr_t);
640 
641 	// count number of patterns that would be saved
642 	int16_t ap = MAX_PATTERNS;
643 	do
644 	{
645 		if (patternEmpty(ap - 1))
646 			ap--;
647 		else
648 			break;
649 	}
650 	while (ap > 0);
651 
652 	// count number of instruments
653 	int16_t ai = 128;
654 	while (ai > 0 && getUsedSamples(ai) == 0 && song.instrName[ai][0] == '\0')
655 		ai--;
656 
657 	// count packed pattern data size in song
658 	for (int16_t i = 0; i < ap; i++)
659 	{
660 		currSize64 += sizeof (xmPatHdr_t);
661 		if (!patternEmpty(i))
662 			currSize64 += getPackedPattSize(pattern[i], patternNumRows[i], song.numChannels);
663 	}
664 
665 	// count instrument and sample data size in song
666 	for (int16_t i = 1; i <= ai; i++)
667 	{
668 		int16_t j;
669 		if (instr[i] == NULL)
670 			j = 0;
671 		else
672 			j = i;
673 
674 		const int16_t a = getUsedSamples(i);
675 		if (a > 0)
676 			currSize64 += INSTR_HEADER_SIZE + (a * sizeof (xmSmpHdr_t));
677 		else
678 			currSize64 += 22+11;
679 
680 		instr_t *ins = instr[j];
681 		for (int16_t k = 0; k < a; k++)
682 		{
683 			sample_t* s = &ins->smp[k];
684 			if (s->dataPtr != NULL && s->length > 0)
685 			{
686 				if (s->flags & SAMPLE_16BIT)
687 					currSize64 += s->length << 1;
688 				else
689 					currSize64 += s->length;
690 			}
691 		}
692 	}
693 
694 	return currSize64;
695 }
696 
calculateTrimSize(void)697 static int64_t calculateTrimSize(void)
698 {
699 	int16_t i, j, k;
700 
701 	int32_t numChannels = song.numChannels;
702 	int32_t pattDataLen = 0;
703 	int32_t newPattDataLen = 0;
704 	int64_t bytes64 = 0;
705 	int64_t oldInstrSize64 = 0;
706 
707 	// copy over temp data
708 	memcpy(tmpPatt, pattern, sizeof (tmpPatt));
709 	memcpy(tmpPattLens, patternNumRows, sizeof (tmpPattLens));
710 	memcpy(tmpInstrName, song.instrName, sizeof (tmpInstrName));
711 
712 	if (!setTmpInstruments())
713 	{
714 		okBox(0, "System message", "Not enough memory!");
715 		return 0;
716 	}
717 
718 	// get current size of all instruments and their samples
719 	if (removeInst || removeSamp || removeSmpDataAfterLoop || convSmpsTo8Bit)
720 		oldInstrSize64 = getTempInsAndSmpSize();
721 
722 	// count number of patterns that would be saved
723 	int16_t ap = MAX_PATTERNS;
724 	do
725 	{
726 		if (tmpPatternEmpty(ap - 1, numChannels))
727 			ap--;
728 		else
729 			break;
730 	}
731 	while (ap > 0);
732 
733 	// count number of instruments that would be saved
734 	int16_t ai = MAX_INST;
735 	while (ai > 0 && getUsedTempSamples(ai) == 0 && tmpInstrName[ai][0] == '\0')
736 		ai--;
737 
738 	// calculate "remove unused samples" size
739 	if (removeSamp) wipeSamplesUnused(true, ai);
740 
741 	// calculate "remove sample data after loop" size
742 	if (removeSmpDataAfterLoop) wipeSmpDataAfterLoop(true, ai);
743 
744 	// calculate "convert samples to 8-bit" size
745 	if (convSmpsTo8Bit) convertSamplesTo8bit(true, ai);
746 
747 	// get old pattern data length
748 	if (removeChans || removePatt)
749 	{
750 		for (i = 0; i < ap; i++)
751 		{
752 			pattDataLen += sizeof (xmPatHdr_t);
753 			if (!tmpPatternEmpty(i, numChannels))
754 				pattDataLen += getPackedPattSize(tmpPatt[i], tmpPattLens[i], numChannels);
755 		}
756 	}
757 
758 	// calculate "remove unused channels" size
759 	if (removeChans)
760 	{
761 		// get real number of channels
762 		int16_t highestChan = -1;
763 		for (i = 0; i < ap; i++)
764 		{
765 			note_t *pattPtr = tmpPatt[i];
766 			if (pattPtr == NULL)
767 				continue;
768 
769 			const int16_t numRows = tmpPattLens[i];
770 			for (j = 0; j < numRows; j++)
771 			{
772 				for (k = 0; k < numChannels; k++)
773 				{
774 					note_t *p = &pattPtr[(j * MAX_CHANNELS) + k];
775 					if (p->note > 0 || p->instr > 0 || p->vol > 0 || p->efx > 0 || p->efxData > 0)
776 					{
777 						if (k > highestChan)
778 							highestChan = k;
779 					}
780 				}
781 			}
782 		}
783 
784 		// set new number of channels (and make it an even number)
785 		if (highestChan >= 0)
786 		{
787 			highestChan++;
788 			if (highestChan & 1)
789 				highestChan++;
790 
791 			numChannels = (uint8_t)(CLAMP(highestChan, 2, numChannels));
792 		}
793 	}
794 
795 	// calculate "remove unused patterns" size
796 	if (removePatt) wipePattsUnused(true, &ap);
797 
798 	// calculate new pattern data size
799 	if (removeChans || removePatt)
800 	{
801 		for (i = 0; i < ap; i++)
802 		{
803 			newPattDataLen += sizeof (xmPatHdr_t);
804 			if (!tmpPatternEmpty(i, numChannels))
805 				newPattDataLen += getPackedPattSize(tmpPatt[i], tmpPattLens[i], numChannels);
806 		}
807 
808 		assert(pattDataLen >= newPattDataLen);
809 
810 		if (pattDataLen > newPattDataLen)
811 			bytes64 += (pattDataLen - newPattDataLen);
812 	}
813 
814 	// calculate "remove unused instruments" size
815 	if (removeInst) wipeInstrUnused(true, &ai, ap, numChannels);
816 
817 	// calculate new instruments and samples size
818 	if (removeInst || removeSamp || removeSmpDataAfterLoop || convSmpsTo8Bit)
819 	{
820 		int64_t newInstrSize64 = getTempInsAndSmpSize();
821 
822 		assert(oldInstrSize64 >= newInstrSize64);
823 		if (oldInstrSize64 > newInstrSize64)
824 			bytes64 += (oldInstrSize64 - newInstrSize64);
825 	}
826 
827 	freeTmpInstruments();
828 	return bytes64;
829 }
830 
trimThreadFunc(void * ptr)831 static int32_t SDLCALL trimThreadFunc(void *ptr)
832 {
833 	int16_t i, j, k;
834 
835 	if (!setTmpInstruments())
836 	{
837 		okBoxThreadSafe(0, "System message", "Not enough memory!");
838 		return true;
839 	}
840 
841 	// audio callback is not running now, so we're safe
842 
843 	// count number of patterns
844 	int16_t ap = MAX_PATTERNS;
845 	do
846 	{
847 		if (patternEmpty(ap - 1))
848 			ap--;
849 		else
850 			break;
851 	}
852 	while (ap > 0);
853 
854 	// count number of instruments
855 	int16_t ai = MAX_INST;
856 	while (ai > 0 && getUsedSamples(ai) == 0 && song.instrName[ai][0] == '\0')
857 		ai--;
858 
859 	// remove unused samples
860 	if (removeSamp)
861 		wipeSamplesUnused(false, ai);
862 
863 	// remove sample data after loop
864 	if (removeSmpDataAfterLoop)
865 		wipeSmpDataAfterLoop(false, ai);
866 
867 	// convert samples to 8-bit
868 	if (convSmpsTo8Bit)
869 		convertSamplesTo8bit(false, ai);
870 
871 	// removed unused channels
872 	if (removeChans)
873 	{
874 		// count used channels
875 		int16_t highestChan = -1;
876 		for (i = 0; i < ap; i++)
877 		{
878 			note_t *pattPtr = pattern[i];
879 			if (pattPtr == NULL)
880 				continue;
881 
882 			const int16_t numRows = patternNumRows[i];
883 			for (j = 0; j < numRows; j++)
884 			{
885 				for (k = 0; k < song.numChannels; k++)
886 				{
887 					note_t *p = &pattPtr[(j * MAX_CHANNELS) + k];
888 					if (p->note > 0 || p->vol > 0 || p->instr > 0 || p->efx > 0 || p->efxData > 0)
889 					{
890 						if (k > highestChan)
891 							highestChan = k;
892 					}
893 				}
894 			}
895 		}
896 
897 		// set new 'channels used' number
898 		if (highestChan >= 0)
899 		{
900 			highestChan++;
901 			if (highestChan & 1)
902 				highestChan++;
903 
904 			song.numChannels = (uint8_t)(CLAMP(highestChan, 2, song.numChannels));
905 		}
906 
907 		// clear potentially unused channel data
908 		if (song.numChannels < MAX_CHANNELS)
909 		{
910 			for (i = 0; i < MAX_PATTERNS; i++)
911 			{
912 				note_t *p = pattern[i];
913 				if (p == NULL)
914 					continue;
915 
916 				const int16_t numRows = patternNumRows[i];
917 				for (j = 0; j < numRows; j++)
918 					memset(&p[(j * MAX_CHANNELS) + song.numChannels], 0, sizeof (note_t) * (MAX_CHANNELS - song.numChannels));
919 			}
920 		}
921 	}
922 
923 	// clear unused patterns
924 	if (removePatt)
925 		wipePattsUnused(false, &ap);
926 
927 	// remove unused instruments
928 	if (removeInst)
929 		wipeInstrUnused(false, &ai, ap, song.numChannels);
930 
931 	freeTmpInstruments();
932 	editor.trimThreadWasDone = true;
933 
934 	return true;
935 	(void)ptr;
936 }
937 
trimThreadDone(void)938 void trimThreadDone(void)
939 {
940 	if (removePatt)
941 		setPos(song.songPos, song.row, false);
942 
943 	if (removeInst)
944 	{
945 		editor.currVolEnvPoint = 0;
946 		editor.currPanEnvPoint = 0;
947 	}
948 
949 	updateTextBoxPointers();
950 
951 	hideTopScreen();
952 	showTopScreen(true);
953 	showBottomScreen();
954 
955 	if (removeChans)
956 	{
957 		if (ui.patternEditorShown)
958 		{
959 			if (ui.channelOffset > song.numChannels-ui.numChannelsShown)
960 				setScrollBarPos(SB_CHAN_SCROLL, song.numChannels - ui.numChannelsShown, true);
961 		}
962 
963 		if (cursor.ch >= ui.channelOffset+ui.numChannelsShown)
964 			cursor.ch = ui.channelOffset+ui.numChannelsShown - 1;
965 	}
966 
967 	checkMarkLimits();
968 
969 	if (removeSamp || convSmpsTo8Bit)
970 		updateSampleEditorSample();
971 
972 	pbTrimCalc();
973 	setSongModifiedFlag();
974 	resumeAudio();
975 	setMouseBusy(false);
976 }
977 
formatBytes(uint64_t bytes,bool roundUp)978 static char *formatBytes(uint64_t bytes, bool roundUp)
979 {
980 	double dBytes;
981 
982 	if (bytes == 0)
983 	{
984 		strcpy(byteFormatBuffer, "0");
985 		return byteFormatBuffer;
986 	}
987 
988 	bytes %= 1000ULL*1024*1024*999; // wrap around gigabytes in case of overflow
989 	if (bytes >= 1024ULL*1024*1024*9)
990 	{
991 		// gigabytes
992 		dBytes = bytes / (1024.0*1024.0*1024.0);
993 		if (dBytes < 100)
994 			sprintf(byteFormatBuffer, "%.1fGB", dBytes);
995 		else
996 			sprintf(byteFormatBuffer, "%dGB", roundUp ? (int32_t)ceil(dBytes) : (int32_t)dBytes);
997 	}
998 	else if (bytes >= 1024*1024*9)
999 	{
1000 		// megabytes
1001 		dBytes = bytes / (1024.0*1024.0);
1002 		if (dBytes < 100)
1003 			sprintf(byteFormatBuffer, "%.1fMB", dBytes);
1004 		else
1005 			sprintf(byteFormatBuffer, "%dMB", roundUp ? (int32_t)ceil(dBytes) : (int32_t)dBytes);
1006 	}
1007 	else if (bytes >= 1024*9)
1008 	{
1009 		// kilobytes
1010 		dBytes = bytes / 1024.0;
1011 		if (dBytes < 100)
1012 			sprintf(byteFormatBuffer, "%.1fkB", dBytes);
1013 		else
1014 			sprintf(byteFormatBuffer, "%dkB", roundUp ? (int32_t)ceil(dBytes) : (int32_t)dBytes);
1015 	}
1016 	else
1017 	{
1018 		// bytes
1019 		sprintf(byteFormatBuffer, "%d", (int32_t)bytes);
1020 	}
1021 
1022 	return byteFormatBuffer;
1023 }
1024 
drawTrimScreen(void)1025 void drawTrimScreen(void)
1026 {
1027 	char sizeBuf[16];
1028 
1029 	drawFramework(0,   92, 136, 81, FRAMEWORK_TYPE1);
1030 	drawFramework(136, 92, 155, 81, FRAMEWORK_TYPE1);
1031 
1032 	textOutShadow(4,    95, PAL_FORGRND, PAL_DSKTOP2, "What to remove:");
1033 	textOutShadow(19,  109, PAL_FORGRND, PAL_DSKTOP2, "Unused patterns");
1034 	textOutShadow(19,  122, PAL_FORGRND, PAL_DSKTOP2, "Unused instruments");
1035 	textOutShadow(19,  135, PAL_FORGRND, PAL_DSKTOP2, "Unused samples");
1036 	textOutShadow(19,  148, PAL_FORGRND, PAL_DSKTOP2, "Unused channels");
1037 	textOutShadow(19,  161, PAL_FORGRND, PAL_DSKTOP2, "Smp. dat. after loop");
1038 
1039 	textOutShadow(155,  96, PAL_FORGRND, PAL_DSKTOP2, "Conv. samples to 8-bit");
1040 	textOutShadow(140, 111, PAL_FORGRND, PAL_DSKTOP2, ".xm size before");
1041 	textOutShadow(140, 124, PAL_FORGRND, PAL_DSKTOP2, ".xm size after");
1042 	textOutShadow(140, 137, PAL_FORGRND, PAL_DSKTOP2, "Bytes to save");
1043 
1044 	if (xmSize64 > -1)
1045 	{
1046 		sprintf(sizeBuf, "%s", formatBytes(xmSize64, true));
1047 		textOut(287 - textWidth(sizeBuf), 111, PAL_FORGRND, sizeBuf);
1048 	}
1049 	else
1050 	{
1051 		textOut(287 - textWidth("Unknown"), 111, PAL_FORGRND, "Unknown");
1052 	}
1053 
1054 	if (xmAfterTrimSize64 > -1)
1055 	{
1056 		sprintf(sizeBuf, "%s", formatBytes(xmAfterTrimSize64, true));
1057 		textOut(287 - textWidth(sizeBuf), 124, PAL_FORGRND, sizeBuf);
1058 	}
1059 	else
1060 	{
1061 		textOut(287 - textWidth("Unknown"), 124, PAL_FORGRND, "Unknown");
1062 	}
1063 
1064 	if (spaceSaved64 > -1)
1065 	{
1066 		sprintf(sizeBuf, "%s", formatBytes(spaceSaved64, false));
1067 		textOut(287 - textWidth(sizeBuf), 137, PAL_FORGRND, sizeBuf);
1068 	}
1069 	else
1070 	{
1071 		textOut(287 - textWidth("Unknown"), 137, PAL_FORGRND, "Unknown");
1072 	}
1073 
1074 	showCheckBox(CB_TRIM_PATT);
1075 	showCheckBox(CB_TRIM_INST);
1076 	showCheckBox(CB_TRIM_SAMP);
1077 	showCheckBox(CB_TRIM_CHAN);
1078 	showCheckBox(CB_TRIM_SMPD);
1079 	showCheckBox(CB_TRIM_CONV);
1080 	showPushButton(PB_TRIM_CALC);
1081 	showPushButton(PB_TRIM_TRIM);
1082 }
1083 
hideTrimScreen(void)1084 void hideTrimScreen(void)
1085 {
1086 	hideCheckBox(CB_TRIM_PATT);
1087 	hideCheckBox(CB_TRIM_INST);
1088 	hideCheckBox(CB_TRIM_SAMP);
1089 	hideCheckBox(CB_TRIM_CHAN);
1090 	hideCheckBox(CB_TRIM_SMPD);
1091 	hideCheckBox(CB_TRIM_CONV);
1092 	hidePushButton(PB_TRIM_CALC);
1093 	hidePushButton(PB_TRIM_TRIM);
1094 
1095 	ui.trimScreenShown = false;
1096 	ui.scopesShown = true;
1097 	drawScopeFramework();
1098 }
1099 
showTrimScreen(void)1100 void showTrimScreen(void)
1101 {
1102 	if (ui.extended)
1103 		exitPatternEditorExtended();
1104 
1105 	hideTopScreen();
1106 	showTopScreen(false);
1107 
1108 	ui.trimScreenShown = true;
1109 	ui.scopesShown = false;
1110 
1111 	drawTrimScreen();
1112 }
1113 
toggleTrimScreen(void)1114 void toggleTrimScreen(void)
1115 {
1116 	if (ui.trimScreenShown)
1117 		hideTrimScreen();
1118 	else
1119 		showTrimScreen();
1120 }
1121 
setInitialTrimFlags(void)1122 void setInitialTrimFlags(void)
1123 {
1124 	removePatt = true;
1125 	removeInst = true;
1126 	removeSamp = true;
1127 	removeChans = true;
1128 	removeSmpDataAfterLoop = true;
1129 	convSmpsTo8Bit = false;
1130 
1131 	checkBoxes[CB_TRIM_PATT].checked = true;
1132 	checkBoxes[CB_TRIM_INST].checked = true;
1133 	checkBoxes[CB_TRIM_SAMP].checked = true;
1134 	checkBoxes[CB_TRIM_CHAN].checked = true;
1135 	checkBoxes[CB_TRIM_SMPD].checked = true;
1136 	checkBoxes[CB_TRIM_CONV].checked = false;
1137 }
1138 
cbTrimUnusedPatt(void)1139 void cbTrimUnusedPatt(void)
1140 {
1141 	removePatt ^= 1;
1142 }
1143 
cbTrimUnusedInst(void)1144 void cbTrimUnusedInst(void)
1145 {
1146 	removeInst ^= 1;
1147 }
1148 
cbTrimUnusedSamp(void)1149 void cbTrimUnusedSamp(void)
1150 {
1151 	removeSamp ^= 1;
1152 }
1153 
cbTrimUnusedChans(void)1154 void cbTrimUnusedChans(void)
1155 {
1156 	removeChans ^= 1;
1157 }
1158 
cbTrimUnusedSmpData(void)1159 void cbTrimUnusedSmpData(void)
1160 {
1161 	removeSmpDataAfterLoop ^= 1;
1162 }
1163 
cbTrimSmpsTo8Bit(void)1164 void cbTrimSmpsTo8Bit(void)
1165 {
1166 	convSmpsTo8Bit ^= 1;
1167 }
1168 
pbTrimCalc(void)1169 void pbTrimCalc(void)
1170 {
1171 	xmSize64 = calculateXMSize();
1172 	spaceSaved64 = calculateTrimSize();
1173 
1174 	xmAfterTrimSize64 = xmSize64 - spaceSaved64;
1175 	if (xmAfterTrimSize64 < 0)
1176 		xmAfterTrimSize64 = 0;
1177 
1178 	if (ui.trimScreenShown)
1179 		drawTrimScreen();
1180 }
1181 
pbTrimDoTrim(void)1182 void pbTrimDoTrim(void)
1183 {
1184 	if (!removePatt && !removeInst && !removeSamp && !removeChans && !removeSmpDataAfterLoop && !convSmpsTo8Bit)
1185 		return; // nothing to trim...
1186 
1187 	if (okBox(2, "System request", "Are you sure you want to trim the song? Making a backup of the song first is recommended.") != 1)
1188 		return;
1189 
1190 	mouseAnimOn();
1191 	pauseAudio();
1192 
1193 	trimThread = SDL_CreateThread(trimThreadFunc, NULL, NULL);
1194 	if (trimThread == NULL)
1195 	{
1196 		resumeAudio();
1197 		mouseAnimOff();
1198 		return;
1199 	}
1200 
1201 	SDL_DetachThread(trimThread);
1202 }
1203 
resetTrimSizes(void)1204 void resetTrimSizes(void)
1205 {
1206 	xmSize64 = -1;
1207 	xmAfterTrimSize64 = -1;
1208 	spaceSaved64 = -1;
1209 
1210 	if (ui.trimScreenShown)
1211 		drawTrimScreen();
1212 }
1213