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 <stdint.h>
8 #include <stdbool.h>
9 #include "ft2_header.h"
10 #include "ft2_config.h"
11 #include "ft2_pattern_ed.h"
12 #include "ft2_gui.h"
13 #include "ft2_sample_ed.h"
14 #include "ft2_pattern_draw.h"
15 #include "ft2_inst_ed.h"
16 #include "scopes/ft2_scopes.h"
17 #include "ft2_diskop.h"
18 #include "ft2_audio.h"
19 #include "ft2_wav_renderer.h"
20 #include "ft2_mouse.h"
21 #include "ft2_video.h"
22 #include "ft2_tables.h"
23 #include "ft2_bmp.h"
24 #include "ft2_structs.h"
25 
26 pattMark_t pattMark; // globalized
27 
28 // for pattern marking w/ keyboard
29 static int8_t lastChMark;
30 static int16_t lastRowMark;
31 
32 // for pattern marking w/ mouse
33 static int32_t lastMarkX1 = -1, lastMarkX2 = -1, lastMarkY1 = -1, lastMarkY2 = -1;
34 
35 static const uint8_t ptnNumRows[8] = { 27, 25, 20, 19, 42, 40, 31, 30 };
36 static const uint8_t ptnLineSub[8] = { 13, 12,  9,  9, 20, 19, 15, 14 };
37 static const uint8_t iSwitchExtW[4] = { 40, 40, 40, 39 };
38 static const uint8_t iSwitchExtY[8] = { 2, 2, 2, 2, 19, 19, 19, 19 };
39 static const uint8_t iSwitchY[8] = { 2, 19, 36, 53, 73, 90, 107, 124 };
40 static const uint16_t iSwitchExtX[4] = { 221, 262, 303, 344 };
41 
42 static int32_t lastMouseX, lastMouseY;
43 static int32_t last_TimeH, last_TimeM, last_TimeS;
44 
allocatePattern(uint16_t pattNum)45 bool allocatePattern(uint16_t pattNum) // for tracker use only, not in loader!
46 {
47 	const bool audioWasntLocked = !audio.locked;
48 	if (audioWasntLocked)
49 		lockAudio();
50 
51 	if (pattern[pattNum] == NULL)
52 	{
53 		/* Original FT2 allocates only the amount of rows needed, but we don't
54 		** do that to avoid out of bondary row look-up between out-of-sync replayer
55 		** state and tracker state (yes it used to happen, rarely). We're not wasting
56 		** too much RAM for a modern computer anyway. Worst case: 256 allocated
57 		** patterns would be ~10MB.
58 		**/
59 
60 		pattern[pattNum] = (note_t *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1);
61 		if (pattern[pattNum] == NULL)
62 		{
63 			if (audioWasntLocked)
64 				unlockAudio();
65 
66 			return false;
67 		}
68 
69 		song.currNumRows = patternNumRows[pattNum];
70 	}
71 
72 	if (audioWasntLocked)
73 		unlockAudio();
74 
75 	return true;
76 }
77 
killPatternIfUnused(uint16_t pattNum)78 void killPatternIfUnused(uint16_t pattNum) // for tracker use only, not in loader!
79 {
80 	const bool audioWasntLocked = !audio.locked;
81 	if (audioWasntLocked)
82 		lockAudio();
83 
84 	if (patternEmpty(pattNum))
85 	{
86 		if (pattern[pattNum] != NULL)
87 		{
88 			free(pattern[pattNum]);
89 			pattern[pattNum] = NULL;
90 		}
91 	}
92 
93 	if (audioWasntLocked)
94 		unlockAudio();
95 }
96 
getMaxVisibleChannels(void)97 uint8_t getMaxVisibleChannels(void)
98 {
99 	assert(config.ptnMaxChannels >= 0 && config.ptnMaxChannels <= 3);
100 	if (config.ptnShowVolColumn)
101 		return maxVisibleChans1[config.ptnMaxChannels];
102 	else
103 		return maxVisibleChans2[config.ptnMaxChannels];
104 }
105 
updatePatternWidth(void)106 void updatePatternWidth(void)
107 {
108 	if (ui.numChannelsShown > ui.maxVisibleChannels)
109 		ui.numChannelsShown = ui.maxVisibleChannels;
110 
111 	assert(ui.numChannelsShown >= 2 && ui.numChannelsShown <= 12);
112 
113 	ui.patternChannelWidth = chanWidths[(ui.numChannelsShown / 2) - 1] + 3;
114 }
115 
updateAdvEdit(void)116 void updateAdvEdit(void)
117 {
118 	hexOutBg(92, 113, PAL_FORGRND, PAL_DESKTOP, editor.srcInstr, 2);
119 	hexOutBg(92, 126, PAL_FORGRND, PAL_DESKTOP, editor.curInstr, 2);
120 }
121 
setAdvEditCheckBoxes(void)122 void setAdvEditCheckBoxes(void)
123 {
124 	checkBoxes[CB_ENABLE_MASKING].checked = editor.copyMaskEnable;
125 	checkBoxes[CB_COPY_MASK_0].checked = editor.copyMask[0];
126 	checkBoxes[CB_COPY_MASK_1].checked = editor.copyMask[1];
127 	checkBoxes[CB_COPY_MASK_2].checked = editor.copyMask[2];
128 	checkBoxes[CB_COPY_MASK_3].checked = editor.copyMask[3];
129 	checkBoxes[CB_COPY_MASK_4].checked = editor.copyMask[4];
130 	checkBoxes[CB_PASTE_MASK_0].checked = editor.pasteMask[0];
131 	checkBoxes[CB_PASTE_MASK_1].checked = editor.pasteMask[1];
132 	checkBoxes[CB_PASTE_MASK_2].checked = editor.pasteMask[2];
133 	checkBoxes[CB_PASTE_MASK_3].checked = editor.pasteMask[3];
134 	checkBoxes[CB_PASTE_MASK_4].checked = editor.pasteMask[4];
135 	checkBoxes[CB_TRANSP_MASK_0].checked = editor.transpMask[0];
136 	checkBoxes[CB_TRANSP_MASK_1].checked = editor.transpMask[1];
137 	checkBoxes[CB_TRANSP_MASK_2].checked = editor.transpMask[2];
138 	checkBoxes[CB_TRANSP_MASK_3].checked = editor.transpMask[3];
139 	checkBoxes[CB_TRANSP_MASK_4].checked = editor.transpMask[4];
140 
141 	showCheckBox(CB_ENABLE_MASKING);
142 	showCheckBox(CB_COPY_MASK_0);
143 	showCheckBox(CB_COPY_MASK_1);
144 	showCheckBox(CB_COPY_MASK_2);
145 	showCheckBox(CB_COPY_MASK_3);
146 	showCheckBox(CB_COPY_MASK_4);
147 	showCheckBox(CB_PASTE_MASK_0);
148 	showCheckBox(CB_PASTE_MASK_1);
149 	showCheckBox(CB_PASTE_MASK_2);
150 	showCheckBox(CB_PASTE_MASK_3);
151 	showCheckBox(CB_PASTE_MASK_4);
152 	showCheckBox(CB_TRANSP_MASK_0);
153 	showCheckBox(CB_TRANSP_MASK_1);
154 	showCheckBox(CB_TRANSP_MASK_2);
155 	showCheckBox(CB_TRANSP_MASK_3);
156 	showCheckBox(CB_TRANSP_MASK_4);
157 }
158 
drawAdvEdit(void)159 void drawAdvEdit(void)
160 {
161 	drawFramework(  0,  92, 110,  17, FRAMEWORK_TYPE1);
162 	drawFramework(  0, 109, 110,  64, FRAMEWORK_TYPE1);
163 	drawFramework(110,  92, 124,  81, FRAMEWORK_TYPE1);
164 	drawFramework(234,  92,  19,  81, FRAMEWORK_TYPE1);
165 	drawFramework(253,  92,  19,  81, FRAMEWORK_TYPE1);
166 	drawFramework(272,  92,  19,  81, FRAMEWORK_TYPE1);
167 
168 	textOutShadow(  4,  96, PAL_FORGRND, PAL_DSKTOP2, "Instr. remap:");
169 	textOutShadow(  4, 113, PAL_FORGRND, PAL_DSKTOP2, "Old number");
170 	textOutShadow(  4, 126, PAL_FORGRND, PAL_DSKTOP2, "New number");
171 	textOutShadow(129,  96, PAL_FORGRND, PAL_DSKTOP2, "Masking enable");
172 	textOutShadow(114, 109, PAL_FORGRND, PAL_DSKTOP2, "Note");
173 	textOutShadow(114, 122, PAL_FORGRND, PAL_DSKTOP2, "Instrument number");
174 	textOutShadow(114, 135, PAL_FORGRND, PAL_DSKTOP2, "Volume column");
175 	textOutShadow(114, 148, PAL_FORGRND, PAL_DSKTOP2, "Effect digit 1");
176 	textOutShadow(114, 161, PAL_FORGRND, PAL_DSKTOP2, "Effect digit 2,3");
177 
178 	charOutShadow(239, 95, PAL_FORGRND, PAL_DSKTOP2, 'C');
179 	charOutShadow(258, 95, PAL_FORGRND, PAL_DSKTOP2, 'P');
180 	charOutShadow(277, 95, PAL_FORGRND, PAL_DSKTOP2, 'T');
181 
182 	showPushButton(PB_REMAP_TRACK);
183 	showPushButton(PB_REMAP_PATTERN);
184 	showPushButton(PB_REMAP_SONG);
185 	showPushButton(PB_REMAP_BLOCK);
186 
187 	setAdvEditCheckBoxes();
188 
189 	updateAdvEdit();
190 }
191 
hideAdvEdit(void)192 void hideAdvEdit(void)
193 {
194 	ui.advEditShown = false;
195 
196 	hidePushButton(PB_REMAP_TRACK);
197 	hidePushButton(PB_REMAP_PATTERN);
198 	hidePushButton(PB_REMAP_SONG);
199 	hidePushButton(PB_REMAP_BLOCK);
200 
201 	hideCheckBox(CB_ENABLE_MASKING);
202 	hideCheckBox(CB_COPY_MASK_0);
203 	hideCheckBox(CB_COPY_MASK_1);
204 	hideCheckBox(CB_COPY_MASK_2);
205 	hideCheckBox(CB_COPY_MASK_3);
206 	hideCheckBox(CB_COPY_MASK_4);
207 	hideCheckBox(CB_PASTE_MASK_0);
208 	hideCheckBox(CB_PASTE_MASK_1);
209 	hideCheckBox(CB_PASTE_MASK_2);
210 	hideCheckBox(CB_PASTE_MASK_3);
211 	hideCheckBox(CB_PASTE_MASK_4);
212 	hideCheckBox(CB_TRANSP_MASK_0);
213 	hideCheckBox(CB_TRANSP_MASK_1);
214 	hideCheckBox(CB_TRANSP_MASK_2);
215 	hideCheckBox(CB_TRANSP_MASK_3);
216 	hideCheckBox(CB_TRANSP_MASK_4);
217 
218 	ui.scopesShown = true;
219 	drawScopeFramework();
220 }
221 
showAdvEdit(void)222 void showAdvEdit(void)
223 {
224 	if (ui.extended)
225 		exitPatternEditorExtended();
226 
227 	hideTopScreen();
228 	showTopScreen(false);
229 
230 	ui.advEditShown = true;
231 	ui.scopesShown  = false;
232 	drawAdvEdit();
233 }
234 
toggleAdvEdit(void)235 void toggleAdvEdit(void)
236 {
237 	if (ui.advEditShown)
238 		hideAdvEdit();
239 	else
240 		showAdvEdit();
241 }
242 
drawTranspose(void)243 void drawTranspose(void)
244 {
245 	drawFramework(0,    92,  53,  16, FRAMEWORK_TYPE1);
246 	drawFramework(53,   92, 119,  16, FRAMEWORK_TYPE1);
247 	drawFramework(172,  92, 119,  16, FRAMEWORK_TYPE1);
248 	drawFramework(0,   108,  53,  65, FRAMEWORK_TYPE1);
249 	drawFramework(53,  108, 119,  65, FRAMEWORK_TYPE1);
250 	drawFramework(172, 108, 119,  65, FRAMEWORK_TYPE1);
251 
252 	textOutShadow(4,    95, PAL_FORGRND, PAL_DSKTOP2, "Transp.");
253 	textOutShadow(58,   95, PAL_FORGRND, PAL_DSKTOP2, "Current instrument");
254 	textOutShadow(188,  95, PAL_FORGRND, PAL_DSKTOP2, "All instruments");
255 	textOutShadow(4,   114, PAL_FORGRND, PAL_DSKTOP2, "Track");
256 	textOutShadow(4,   129, PAL_FORGRND, PAL_DSKTOP2, "Pattern");
257 	textOutShadow(4,   144, PAL_FORGRND, PAL_DSKTOP2, "Song");
258 	textOutShadow(4,   159, PAL_FORGRND, PAL_DSKTOP2, "Block");
259 
260 	showPushButton(PB_TRANSP_CUR_INS_TRK_UP);
261 	showPushButton(PB_TRANSP_CUR_INS_TRK_DN);
262 	showPushButton(PB_TRANSP_CUR_INS_TRK_12UP);
263 	showPushButton(PB_TRANSP_CUR_INS_TRK_12DN);
264 	showPushButton(PB_TRANSP_ALL_INS_TRK_UP);
265 	showPushButton(PB_TRANSP_ALL_INS_TRK_DN);
266 	showPushButton(PB_TRANSP_ALL_INS_TRK_12UP);
267 	showPushButton(PB_TRANSP_ALL_INS_TRK_12DN);
268 	showPushButton(PB_TRANSP_CUR_INS_PAT_UP);
269 	showPushButton(PB_TRANSP_CUR_INS_PAT_DN);
270 	showPushButton(PB_TRANSP_CUR_INS_PAT_12UP);
271 	showPushButton(PB_TRANSP_CUR_INS_PAT_12DN);
272 	showPushButton(PB_TRANSP_ALL_INS_PAT_UP);
273 	showPushButton(PB_TRANSP_ALL_INS_PAT_DN);
274 	showPushButton(PB_TRANSP_ALL_INS_PAT_12UP);
275 	showPushButton(PB_TRANSP_ALL_INS_PAT_12DN);
276 	showPushButton(PB_TRANSP_CUR_INS_SNG_UP);
277 	showPushButton(PB_TRANSP_CUR_INS_SNG_DN);
278 	showPushButton(PB_TRANSP_CUR_INS_SNG_12UP);
279 	showPushButton(PB_TRANSP_CUR_INS_SNG_12DN);
280 	showPushButton(PB_TRANSP_ALL_INS_SNG_UP);
281 	showPushButton(PB_TRANSP_ALL_INS_SNG_DN);
282 	showPushButton(PB_TRANSP_ALL_INS_SNG_12UP);
283 	showPushButton(PB_TRANSP_ALL_INS_SNG_12DN);
284 	showPushButton(PB_TRANSP_CUR_INS_BLK_UP);
285 	showPushButton(PB_TRANSP_CUR_INS_BLK_DN);
286 	showPushButton(PB_TRANSP_CUR_INS_BLK_12UP);
287 	showPushButton(PB_TRANSP_CUR_INS_BLK_12DN);
288 	showPushButton(PB_TRANSP_ALL_INS_BLK_UP);
289 	showPushButton(PB_TRANSP_ALL_INS_BLK_DN);
290 	showPushButton(PB_TRANSP_ALL_INS_BLK_12UP);
291 	showPushButton(PB_TRANSP_ALL_INS_BLK_12DN);
292 }
293 
showTranspose(void)294 void showTranspose(void)
295 {
296 	if (ui.extended)
297 		exitPatternEditorExtended();
298 
299 	hideTopScreen();
300 	showTopScreen(false);
301 
302 	ui.transposeShown = true;
303 	ui.scopesShown = false;
304 	drawTranspose();
305 }
306 
hideTranspose(void)307 void hideTranspose(void)
308 {
309 	hidePushButton(PB_TRANSP_CUR_INS_TRK_UP);
310 	hidePushButton(PB_TRANSP_CUR_INS_TRK_DN);
311 	hidePushButton(PB_TRANSP_CUR_INS_TRK_12UP);
312 	hidePushButton(PB_TRANSP_CUR_INS_TRK_12DN);
313 	hidePushButton(PB_TRANSP_ALL_INS_TRK_UP);
314 	hidePushButton(PB_TRANSP_ALL_INS_TRK_DN);
315 	hidePushButton(PB_TRANSP_ALL_INS_TRK_12UP);
316 	hidePushButton(PB_TRANSP_ALL_INS_TRK_12DN);
317 	hidePushButton(PB_TRANSP_CUR_INS_PAT_UP);
318 	hidePushButton(PB_TRANSP_CUR_INS_PAT_DN);
319 	hidePushButton(PB_TRANSP_CUR_INS_PAT_12UP);
320 	hidePushButton(PB_TRANSP_CUR_INS_PAT_12DN);
321 	hidePushButton(PB_TRANSP_ALL_INS_PAT_UP);
322 	hidePushButton(PB_TRANSP_ALL_INS_PAT_DN);
323 	hidePushButton(PB_TRANSP_ALL_INS_PAT_12UP);
324 	hidePushButton(PB_TRANSP_ALL_INS_PAT_12DN);
325 	hidePushButton(PB_TRANSP_CUR_INS_SNG_UP);
326 	hidePushButton(PB_TRANSP_CUR_INS_SNG_DN);
327 	hidePushButton(PB_TRANSP_CUR_INS_SNG_12UP);
328 	hidePushButton(PB_TRANSP_CUR_INS_SNG_12DN);
329 	hidePushButton(PB_TRANSP_ALL_INS_SNG_UP);
330 	hidePushButton(PB_TRANSP_ALL_INS_SNG_DN);
331 	hidePushButton(PB_TRANSP_ALL_INS_SNG_12UP);
332 	hidePushButton(PB_TRANSP_ALL_INS_SNG_12DN);
333 	hidePushButton(PB_TRANSP_CUR_INS_BLK_UP);
334 	hidePushButton(PB_TRANSP_CUR_INS_BLK_DN);
335 	hidePushButton(PB_TRANSP_CUR_INS_BLK_12UP);
336 	hidePushButton(PB_TRANSP_CUR_INS_BLK_12DN);
337 	hidePushButton(PB_TRANSP_ALL_INS_BLK_UP);
338 	hidePushButton(PB_TRANSP_ALL_INS_BLK_DN);
339 	hidePushButton(PB_TRANSP_ALL_INS_BLK_12UP);
340 	hidePushButton(PB_TRANSP_ALL_INS_BLK_12DN);
341 
342 	ui.transposeShown = false;
343 	ui.scopesShown = true;
344 	drawScopeFramework();
345 }
346 
toggleTranspose(void)347 void toggleTranspose(void)
348 {
349 	if (ui.transposeShown)
350 		hideTranspose();
351 	else
352 		showTranspose();
353 }
354 
355 // ----- PATTERN CURSOR FUNCTIONS -----
356 
cursorChannelLeft(void)357 void cursorChannelLeft(void)
358 {
359 	cursor.object = CURSOR_EFX2;
360 
361 	if (cursor.ch == 0)
362 	{
363 		cursor.ch = (uint8_t)(song.numChannels - 1);
364 		if (ui.pattChanScrollShown)
365 			setScrollBarPos(SB_CHAN_SCROLL, song.numChannels, true);
366 	}
367 	else
368 	{
369 		cursor.ch--;
370 		if (ui.pattChanScrollShown)
371 		{
372 			if (cursor.ch < ui.channelOffset)
373 				scrollBarScrollUp(SB_CHAN_SCROLL, 1);
374 		}
375 	}
376 }
377 
cursorChannelRight(void)378 void cursorChannelRight(void)
379 {
380 	cursor.object = CURSOR_NOTE;
381 
382 	if (cursor.ch >= song.numChannels-1)
383 	{
384 		cursor.ch = 0;
385 		if (ui.pattChanScrollShown)
386 			setScrollBarPos(SB_CHAN_SCROLL, 0, true);
387 	}
388 	else
389 	{
390 		cursor.ch++;
391 		if (ui.pattChanScrollShown && cursor.ch >= ui.channelOffset+ui.numChannelsShown)
392 			scrollBarScrollDown(SB_CHAN_SCROLL, 1);
393 	}
394 }
395 
cursorTabLeft(void)396 void cursorTabLeft(void)
397 {
398 	if (cursor.object == CURSOR_NOTE)
399 		cursorChannelLeft();
400 
401 	cursor.object = CURSOR_NOTE;
402 	ui.updatePatternEditor = true;
403 }
404 
cursorTabRight(void)405 void cursorTabRight(void)
406 {
407 	cursorChannelRight();
408 	cursor.object = CURSOR_NOTE;
409 	ui.updatePatternEditor = true;
410 }
411 
chanLeft(void)412 void chanLeft(void)
413 {
414 	cursorChannelLeft();
415 	cursor.object = CURSOR_NOTE;
416 	ui.updatePatternEditor = true;
417 }
418 
chanRight(void)419 void chanRight(void)
420 {
421 	cursorChannelRight();
422 	cursor.object = CURSOR_NOTE;
423 	ui.updatePatternEditor = true;
424 }
425 
cursorLeft(void)426 void cursorLeft(void)
427 {
428 	cursor.object--;
429 
430 	if (!config.ptnShowVolColumn)
431 	{
432 		while (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2)
433 			cursor.object--;
434 	}
435 
436 	if (cursor.object == -1)
437 	{
438 		cursor.object = CURSOR_EFX2;
439 		cursorChannelLeft();
440 	}
441 
442 	ui.updatePatternEditor = true;
443 }
444 
cursorRight(void)445 void cursorRight(void)
446 {
447 	cursor.object++;
448 
449 	if (!config.ptnShowVolColumn)
450 	{
451 		while (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2)
452 			cursor.object++;
453 	}
454 
455 	if (cursor.object == 8)
456 	{
457 		cursor.object = CURSOR_NOTE;
458 		cursorChannelRight();
459 	}
460 
461 	ui.updatePatternEditor = true;
462 }
463 
showPatternEditor(void)464 void showPatternEditor(void)
465 {
466 	ui.patternEditorShown = true;
467 	updateChanNums();
468 	drawPatternBorders();
469 	ui.updatePatternEditor = true;
470 }
471 
hidePatternEditor(void)472 void hidePatternEditor(void)
473 {
474 	hideScrollBar(SB_CHAN_SCROLL);
475 	hidePushButton(PB_CHAN_SCROLL_LEFT);
476 	hidePushButton(PB_CHAN_SCROLL_RIGHT);
477 
478 	ui.patternEditorShown = false;
479 }
480 
updatePatternEditorGUI(void)481 static void updatePatternEditorGUI(void)
482 {
483 	uint16_t i;
484 	pushButton_t *p;
485 	textBox_t *t;
486 
487 	if (ui.extended)
488 	{
489 		// extended pattern editor
490 
491 		// instrument names
492 		t = &textBoxes[TB_INST1];
493 		for (i = 0; i < 8; i++, t++)
494 		{
495 			if (i < 4)
496 			{
497 				t->x = 406;
498 				t->y = 5 + (i * 11);
499 			}
500 			else
501 			{
502 				t->x = 529;
503 				t->y = 5 + ((i - 4) * 11);
504 			}
505 
506 			t->w = 99;
507 			t->renderW = t->w - (t->tx * 2);
508 		}
509 
510 		scrollBars[SB_POS_ED].h = 23;
511 
512 		pushButtons[PB_POSED_POS_DOWN].y = 38;
513 		pushButtons[PB_POSED_PATT_UP].y = 20;
514 		pushButtons[PB_POSED_PATT_DOWN].y = 20;
515 		pushButtons[PB_POSED_DEL].y = 35;
516 		pushButtons[PB_SWAP_BANK].caption = "Swap B.";
517 		pushButtons[PB_SWAP_BANK].caption2 = NULL;
518 		pushButtons[PB_SWAP_BANK].x = 162;
519 		pushButtons[PB_SWAP_BANK].y = 35;
520 		pushButtons[PB_SWAP_BANK].w = 53;
521 		pushButtons[PB_SWAP_BANK].h = 16;
522 		pushButtons[PB_POSED_LEN_UP].x = 180;
523 		pushButtons[PB_POSED_LEN_UP].y = 3;
524 		pushButtons[PB_POSED_LEN_DOWN].x = 197;
525 		pushButtons[PB_POSED_LEN_DOWN].y = 3;
526 		pushButtons[PB_POSED_REP_UP].x = 180;
527 		pushButtons[PB_POSED_REP_UP].y = 17;
528 		pushButtons[PB_POSED_REP_DOWN].x = 197;
529 		pushButtons[PB_POSED_REP_DOWN].y = 17;
530 		pushButtons[PB_PATT_UP].x = 267;
531 		pushButtons[PB_PATT_UP].y = 37;
532 		pushButtons[PB_PATT_DOWN].x = 284;
533 		pushButtons[PB_PATT_DOWN].y = 37;
534 		pushButtons[PB_PATTLEN_UP].x = 348;
535 		pushButtons[PB_PATTLEN_UP].y = 37;
536 		pushButtons[PB_PATTLEN_DOWN].x = 365;
537 		pushButtons[PB_PATTLEN_DOWN].y = 37;
538 
539 		// instrument switcher
540 		p = &pushButtons[PB_RANGE1];
541 		for (i = 0; i < 16; i++, p++)
542 		{
543 			p->w = iSwitchExtW[i & 3];
544 			p->x = iSwitchExtX[i & 3];
545 			p->y = iSwitchExtY[i & 7];
546 		}
547 	}
548 	else
549 	{
550 		// instrument names
551 		t = &textBoxes[TB_INST1];
552 		for (i = 0; i < 8; i++, t++)
553 		{
554 			t->y = 5 + (i * 11);
555 			t->x = 446;
556 			t->w = 140;
557 			t->renderW = t->w - (t->tx * 2);
558 		}
559 
560 		// normal pattern editor
561 
562 		scrollBars[SB_POS_ED].h = 21;
563 
564 		pushButtons[PB_POSED_POS_DOWN].y = 36;
565 		pushButtons[PB_POSED_PATT_UP].y = 19;
566 		pushButtons[PB_POSED_PATT_DOWN].y = 19;
567 		pushButtons[PB_POSED_DEL].y = 33;
568 		pushButtons[PB_SWAP_BANK].caption = "Swap";
569 		pushButtons[PB_SWAP_BANK].caption2 = "Bank";
570 		pushButtons[PB_SWAP_BANK].x = 590;
571 		pushButtons[PB_SWAP_BANK].y = 144;
572 		pushButtons[PB_SWAP_BANK].w = 39;
573 		pushButtons[PB_SWAP_BANK].h = 27;
574 		pushButtons[PB_POSED_LEN_UP].x = 74;
575 		pushButtons[PB_POSED_LEN_UP].y = 50;
576 		pushButtons[PB_POSED_LEN_DOWN].x = 91;
577 		pushButtons[PB_POSED_LEN_DOWN].y = 50;
578 		pushButtons[PB_POSED_REP_UP].x = 74;
579 		pushButtons[PB_POSED_REP_UP].y = 62;
580 		pushButtons[PB_POSED_REP_DOWN].x = 91;
581 		pushButtons[PB_POSED_REP_DOWN].y = 62;
582 		pushButtons[PB_PATT_UP].x = 253;
583 		pushButtons[PB_PATT_UP].y = 34;
584 		pushButtons[PB_PATT_DOWN].x = 270;
585 		pushButtons[PB_PATT_DOWN].y = 34;
586 		pushButtons[PB_PATTLEN_UP].x = 253;
587 		pushButtons[PB_PATTLEN_UP].y = 48;
588 		pushButtons[PB_PATTLEN_DOWN].x = 270;
589 		pushButtons[PB_PATTLEN_DOWN].y = 48;
590 
591 		// instrument switcher
592 		p = &pushButtons[PB_RANGE1];
593 		for (i = 0; i < 16; i++, p++)
594 		{
595 			p->w = 39;
596 			p->x = 590;
597 			p->y = iSwitchY[i & 7];
598 		}
599 	}
600 }
601 
patternEditorExtended(void)602 void patternEditorExtended(void)
603 {
604 	// backup old screen flags
605 	ui._aboutScreenShown = ui.aboutScreenShown;
606 	ui._helpScreenShown = ui.helpScreenShown;
607 	ui._configScreenShown = ui.configScreenShown;
608 	ui._diskOpShown = ui.diskOpShown;
609 	ui._nibblesShown = ui.nibblesShown;
610 	ui._transposeShown = ui.transposeShown;
611 	ui._instEditorShown = ui.instEditorShown;
612 	ui._instEditorExtShown = ui.instEditorExtShown;
613 	ui._sampleEditorExtShown = ui.sampleEditorExtShown;
614 	ui._patternEditorShown = ui.patternEditorShown;
615 	ui._sampleEditorShown = ui.sampleEditorShown;
616 	ui._advEditShown= ui.advEditShown;
617 	ui._wavRendererShown = ui.wavRendererShown;
618 	ui._trimScreenShown = ui.trimScreenShown;
619 
620 	hideTopScreen();
621 	hideSampleEditor();
622 	hideInstEditor();
623 
624 	ui.extended = true;
625 	ui.patternEditorShown = true;
626 	updatePatternEditorGUI(); // change pattern editor layout (based on ui.extended flag)
627 	ui.updatePatternEditor = true; // redraw pattern editor
628 
629 	drawFramework(0,    0, 112, 53, FRAMEWORK_TYPE1);
630 	drawFramework(112,  0, 106, 33, FRAMEWORK_TYPE1);
631 	drawFramework(112, 33, 106, 20, FRAMEWORK_TYPE1);
632 	drawFramework(218,  0, 168, 53, FRAMEWORK_TYPE1);
633 
634 	// pos ed. stuff
635 
636 	drawFramework(2,  2, 51, 20, FRAMEWORK_TYPE2);
637 	drawFramework(2, 31, 51, 20, FRAMEWORK_TYPE2);
638 
639 	showScrollBar(SB_POS_ED);
640 
641 	showPushButton(PB_POSED_POS_UP);
642 	showPushButton(PB_POSED_POS_DOWN);
643 	showPushButton(PB_POSED_INS);
644 	showPushButton(PB_POSED_PATT_UP);
645 	showPushButton(PB_POSED_PATT_DOWN);
646 	showPushButton(PB_POSED_DEL);
647 	showPushButton(PB_POSED_LEN_UP);
648 	showPushButton(PB_POSED_LEN_DOWN);
649 	showPushButton(PB_POSED_REP_UP);
650 	showPushButton(PB_POSED_REP_DOWN);
651 	showPushButton(PB_SWAP_BANK);
652 	showPushButton(PB_PATT_UP);
653 	showPushButton(PB_PATT_DOWN);
654 	showPushButton(PB_PATTLEN_UP);
655 	showPushButton(PB_PATTLEN_DOWN);
656 
657 	showPushButton(PB_EXIT_EXT_PATT);
658 
659 	textOutShadow(116,  5, PAL_FORGRND, PAL_DSKTOP2, "Sng.len.");
660 	textOutShadow(116, 19, PAL_FORGRND, PAL_DSKTOP2, "Repst.");
661 	textOutShadow(222, 39, PAL_FORGRND, PAL_DSKTOP2, "Ptn.");
662 	textOutShadow(305, 39, PAL_FORGRND, PAL_DSKTOP2, "Ln.");
663 
664 	ui.instrSwitcherShown = true;
665 	showInstrumentSwitcher();
666 
667 	drawSongLength();
668 	drawSongLoopStart();
669 	drawEditPattern(editor.editPattern);
670 	drawPatternLength(editor.editPattern);
671 	drawPosEdNums(editor.songPos);
672 	ui.updatePosSections = true;
673 
674 	// kludge to fix scrollbar thumb when the scrollbar height changes during playback
675 	if (songPlaying)
676 		setScrollBarPos(SB_POS_ED, editor.songPos, false);
677 }
678 
exitPatternEditorExtended(void)679 void exitPatternEditorExtended(void)
680 {
681 	ui.extended = false;
682 	updatePatternEditorGUI();
683 	hidePushButton(PB_EXIT_EXT_PATT);
684 
685 	// set back top screen button maps
686 
687 	// set back old screen flags
688 	ui.aboutScreenShown = ui._aboutScreenShown;
689 	ui.helpScreenShown = ui._helpScreenShown;
690 	ui.configScreenShown = ui._configScreenShown;
691 	ui.diskOpShown = ui._diskOpShown;
692 	ui.nibblesShown = ui._nibblesShown;
693 	ui.transposeShown = ui._transposeShown;
694 	ui.instEditorShown = ui._instEditorShown;
695 	ui.instEditorExtShown = ui._instEditorExtShown;
696 	ui.sampleEditorExtShown = ui._sampleEditorExtShown;
697 	ui.patternEditorShown = ui._patternEditorShown;
698 	ui.sampleEditorShown = ui._sampleEditorShown;
699 	ui.advEditShown = ui._advEditShown;
700 	ui.wavRendererShown = ui._wavRendererShown;
701 	ui.trimScreenShown = ui.trimScreenShown;
702 
703 	showTopScreen(true);
704 	showBottomScreen();
705 
706 	// kludge to fix scrollbar thumb when the scrollbar height changes during playback
707 	if (songPlaying)
708 		setScrollBarPos(SB_POS_ED, editor.songPos, false);
709 }
710 
togglePatternEditorExtended(void)711 void togglePatternEditorExtended(void)
712 {
713 	if (ui.extended)
714 		exitPatternEditorExtended();
715 	else
716 		patternEditorExtended();
717 }
718 
clearPattMark(void)719 void clearPattMark(void)
720 {
721 	memset(&pattMark, 0, sizeof (pattMark));
722 
723 	lastMarkX1 = -1;
724 	lastMarkX2 = -1;
725 	lastMarkY1 = -1;
726 	lastMarkY2 = -1;
727 }
728 
checkMarkLimits(void)729 void checkMarkLimits(void)
730 {
731 	const uint16_t limitY = patternNumRows[editor.editPattern];
732 	pattMark.markY1 = CLAMP(pattMark.markY1, 0, limitY);
733 	pattMark.markY2 = CLAMP(pattMark.markY2, 0, limitY);
734 
735 	const uint16_t limitX = (uint16_t)(song.numChannels - 1);
736 	pattMark.markX1 = CLAMP(pattMark.markX1, 0, limitX);
737 	pattMark.markX2 = CLAMP(pattMark.markX2, 0, limitX);
738 
739 	// XXX: will probably never happen? FT2 has this in CheckMarkLimits() though...
740 	if (pattMark.markX1 > pattMark.markX2)
741 		pattMark.markX1 = pattMark.markX2;
742 }
743 
mouseXToCh(void)744 static int8_t mouseXToCh(void) // used to get channel num from mouse x (for pattern marking)
745 {
746 	assert(ui.patternChannelWidth > 0);
747 	if (ui.patternChannelWidth == 0)
748 		return 0;
749 
750 	int32_t mouseX = mouse.x - 29;
751 	mouseX = CLAMP(mouseX, 0, 573);
752 
753 	const int8_t chEnd = (ui.channelOffset + ui.numChannelsShown) - 1;
754 
755 	int8_t ch = ui.channelOffset + (int8_t)(mouseX / ui.patternChannelWidth);
756 	ch = CLAMP(ch, 0, chEnd);
757 
758 	// in some setups there can be non-used channels to the right, do clamping
759 	if (ch >= song.numChannels)
760 		ch = (int8_t)(song.numChannels - 1);
761 
762 	return ch;
763 }
764 
mouseYToRow(void)765 static int16_t mouseYToRow(void) // used to get row num from mouse y (for pattern marking)
766 {
767 	const pattCoordsMouse_t *pattCoordsMouse = &pattCoordMouseTable[config.ptnStretch][ui.pattChanScrollShown][ui.extended];
768 
769 	// clamp mouse y to boundaries
770 	const int16_t maxY = ui.pattChanScrollShown ? 382 : 396;
771 	const int16_t my = (int16_t)CLAMP(mouse.y, pattCoordsMouse->upperRowsY, maxY);
772 
773 	const uint8_t charHeight = config.ptnStretch ? 11 : 8;
774 
775 	// test top/middle/bottom rows
776 	if (my < pattCoordsMouse->midRowY)
777 	{
778 		// top rows
779 		int16_t row = editor.row - (pattCoordsMouse->numUpperRows - ((my - pattCoordsMouse->upperRowsY) / charHeight));
780 		if (row < 0)
781 			row = 0;
782 
783 		return row;
784 	}
785 	else if (my >= pattCoordsMouse->midRowY && my <= pattCoordsMouse->midRowY+10)
786 	{
787 		// current row (middle)
788 		return editor.row;
789 	}
790 	else
791 	{
792 		// bottom rows
793 		int16_t row = (editor.row + 1) + ((my - pattCoordsMouse->lowerRowsY) / charHeight);
794 
795 		// prevent being able to mark the next unseen row on the bottom (in some configurations)
796 		const uint8_t mode = (ui.extended * 4) + (config.ptnStretch * 2) + ui.pattChanScrollShown;
797 
798 		const int16_t maxRow = (ptnNumRows[mode] + (editor.row - ptnLineSub[mode])) - 1;
799 		if (row > maxRow)
800 			row = maxRow;
801 
802 		// clamp to pattern length
803 		const int16_t patternLen = patternNumRows[editor.editPattern];
804 		if (row >= patternLen)
805 			row = patternLen - 1;
806 
807 		return row;
808 	}
809 }
810 
handlePatternDataMouseDown(bool mouseButtonHeld)811 void handlePatternDataMouseDown(bool mouseButtonHeld)
812 {
813 	int16_t y1, y2;
814 
815 	// non-FT2 feature: Use right mouse button to remove pattern marking
816 	if (mouse.rightButtonPressed)
817 	{
818 		clearPattMark();
819 		ui.updatePatternEditor = true;
820 		return;
821 	}
822 
823 	if (!mouseButtonHeld)
824 	{
825 		// we clicked inside the pattern data area for the first time, set initial vars
826 
827 		mouse.lastUsedObjectType = OBJECT_PATTERNMARK;
828 
829 		lastMouseX = mouse.x;
830 		lastMouseY = mouse.y;
831 
832 		lastChMark = mouseXToCh();
833 		lastRowMark = mouseYToRow();
834 
835 		pattMark.markX1 = lastChMark;
836 		pattMark.markX2 = lastChMark;
837 		pattMark.markY1 = lastRowMark;
838 		pattMark.markY2 = lastRowMark + 1;
839 
840 		checkMarkLimits();
841 
842 		ui.updatePatternEditor = true;
843 		return;
844 	}
845 
846 	// we're holding down the mouse button inside the pattern data area
847 
848 	bool forceMarking = songPlaying;
849 
850 	// scroll left/right with mouse
851 	if (ui.pattChanScrollShown)
852 	{
853 		if (mouse.x < 29)
854 		{
855 			scrollBarScrollUp(SB_CHAN_SCROLL, 1);
856 			forceMarking = true;
857 		}
858 		else if (mouse.x > 604)
859 		{
860 			scrollBarScrollDown(SB_CHAN_SCROLL, 1);
861 			forceMarking = true;
862 		}
863 	}
864 
865 	// mark channels
866 	if (forceMarking || lastMouseX != mouse.x)
867 	{
868 		lastMouseX = mouse.x;
869 
870 		int8_t chTmp = mouseXToCh();
871 		if (chTmp < lastChMark)
872 		{
873 			pattMark.markX1 = chTmp;
874 			pattMark.markX2 = lastChMark;
875 		}
876 		else
877 		{
878 			pattMark.markX2 = chTmp;
879 			pattMark.markX1 = lastChMark;
880 		}
881 
882 		if (lastMarkX1 != pattMark.markX1 || lastMarkX2 != pattMark.markX2)
883 		{
884 			checkMarkLimits();
885 			ui.updatePatternEditor = true;
886 
887 			lastMarkX1 = pattMark.markX1;
888 			lastMarkX2 = pattMark.markX2;
889 		}
890 	}
891 
892 	// scroll down/up with mouse (if song is not playing)
893 	if (!songPlaying)
894 	{
895 		y1 = ui.extended ? 56 : 176;
896 		y2 = ui.pattChanScrollShown ? 382 : 396;
897 
898 		if (mouse.y < y1)
899 		{
900 			if (editor.row > 0)
901 				setPos(-1, editor.row - 1, true);
902 
903 			forceMarking = true;
904 			ui.updatePatternEditor = true;
905 		}
906 		else if (mouse.y > y2)
907 		{
908 			const int16_t numRows = patternNumRows[editor.editPattern];
909 			if (editor.row < numRows-1)
910 				setPos(-1, editor.row + 1, true);
911 
912 			forceMarking = true;
913 			ui.updatePatternEditor = true;
914 		}
915 	}
916 
917 	// mark rows
918 	if (forceMarking || lastMouseY != mouse.y)
919 	{
920 		lastMouseY = mouse.y;
921 
922 		const int16_t rowTmp = mouseYToRow();
923 		if (rowTmp < lastRowMark)
924 		{
925 			pattMark.markY1 = rowTmp;
926 			pattMark.markY2 = lastRowMark + 1;
927 		}
928 		else
929 		{
930 			pattMark.markY2 = rowTmp + 1;
931 			pattMark.markY1 = lastRowMark;
932 		}
933 
934 		if (lastMarkY1 != pattMark.markY1 || lastMarkY2 != pattMark.markY2)
935 		{
936 			checkMarkLimits();
937 			ui.updatePatternEditor = true;
938 
939 			lastMarkY1 = pattMark.markY1;
940 			lastMarkY2 = pattMark.markY2;
941 		}
942 	}
943 }
944 
rowOneUpWrap(void)945 void rowOneUpWrap(void)
946 {
947 	const bool audioWasntLocked = !audio.locked;
948 	if (audioWasntLocked)
949 		lockAudio();
950 
951 	song.row = (song.row - 1 + song.currNumRows) % song.currNumRows;
952 	if (!songPlaying)
953 	{
954 		editor.row = (uint8_t)song.row;
955 		ui.updatePatternEditor = true;
956 	}
957 
958 	if (audioWasntLocked)
959 		unlockAudio();
960 }
961 
rowOneDownWrap(void)962 void rowOneDownWrap(void)
963 {
964 	const bool audioWasntLocked = !audio.locked;
965 	if (audioWasntLocked)
966 		lockAudio();
967 
968 	if (songPlaying)
969 	{
970 		song.tick = 2;
971 	}
972 	else
973 	{
974 		song.row = (song.row + 1 + song.currNumRows) % song.currNumRows;
975 		editor.row = (uint8_t)song.row;
976 		ui.updatePatternEditor = true;
977 	}
978 
979 	if (audioWasntLocked)
980 		unlockAudio();
981 }
982 
rowUp(uint16_t amount)983 void rowUp(uint16_t amount)
984 {
985 	const bool audioWasntLocked = !audio.locked;
986 	if (audioWasntLocked)
987 		lockAudio();
988 
989 	song.row -= amount;
990 	if (song.row < 0)
991 		song.row = 0;
992 
993 	if (!songPlaying)
994 	{
995 		editor.row = (uint8_t)song.row;
996 		ui.updatePatternEditor = true;
997 	}
998 
999 	if (audioWasntLocked)
1000 		unlockAudio();
1001 }
1002 
rowDown(uint16_t amount)1003 void rowDown(uint16_t amount)
1004 {
1005 	const bool audioWasntLocked = !audio.locked;
1006 	if (audioWasntLocked)
1007 		lockAudio();
1008 
1009 	song.row += amount;
1010 	if (song.row >= song.currNumRows)
1011 		song.row = song.currNumRows - 1;
1012 
1013 	if (!songPlaying)
1014 	{
1015 		editor.row = (uint8_t)song.row;
1016 		ui.updatePatternEditor = true;
1017 	}
1018 
1019 	if (audioWasntLocked)
1020 		unlockAudio();
1021 }
1022 
keybPattMarkUp(void)1023 void keybPattMarkUp(void)
1024 {
1025 	int8_t xPos = cursor.ch;
1026 	int16_t row = editor.row;
1027 
1028 	if (xPos != pattMark.markX1 && xPos != pattMark.markX2)
1029 	{
1030 		pattMark.markX1 = xPos;
1031 		pattMark.markX2 = xPos;
1032 		pattMark.markY1 = row;
1033 		pattMark.markY2 = row + 1;
1034 	}
1035 
1036 	if (row == pattMark.markY1-1)
1037 	{
1038 		pattMark.markY1 = row;
1039 	}
1040 	else if (row == pattMark.markY2)
1041 	{
1042 		pattMark.markY2 = row - 1;
1043 	}
1044 	else if (row != pattMark.markY1 && row != pattMark.markY2)
1045 	{
1046 		pattMark.markX1 = xPos;
1047 		pattMark.markX2 = xPos;
1048 		pattMark.markY1 = row;
1049 		pattMark.markY2 = row + 1;
1050 
1051 	}
1052 
1053 	checkMarkLimits();
1054 	rowOneUpWrap();
1055 }
1056 
keybPattMarkDown(void)1057 void keybPattMarkDown(void)
1058 {
1059 	int8_t xPos = cursor.ch;
1060 	int16_t row = editor.row;
1061 
1062 	if (xPos != pattMark.markX1 && xPos != pattMark.markX2)
1063 	{
1064 		pattMark.markX1 = xPos;
1065 		pattMark.markX2 = xPos;
1066 		pattMark.markY1 = row;
1067 		pattMark.markY2 = row + 1;
1068 	}
1069 
1070 	if (row == pattMark.markY2)
1071 	{
1072 		pattMark.markY2 = row + 1;
1073 	}
1074 	else if (row == pattMark.markY1-1)
1075 	{
1076 		pattMark.markY1 = row + 2;
1077 	}
1078 	else if (row != pattMark.markY1 && row != pattMark.markY2)
1079 	{
1080 		pattMark.markX1 = xPos;
1081 		pattMark.markX2 = xPos;
1082 		pattMark.markY1 = row;
1083 		pattMark.markY2 = row + 1;
1084 	}
1085 
1086 	checkMarkLimits();
1087 	rowOneDownWrap();
1088 }
1089 
keybPattMarkLeft(void)1090 void keybPattMarkLeft(void)
1091 {
1092 	int8_t xPos = cursor.ch;
1093 	int16_t row = editor.row;
1094 
1095 	if (row != pattMark.markY1-1 && row != pattMark.markY2)
1096 	{
1097 		pattMark.markY1 = row - 1;
1098 		pattMark.markY2 = row;
1099 	}
1100 
1101 	if (xPos == pattMark.markX1)
1102 	{
1103 		pattMark.markX1 = xPos - 1;
1104 	}
1105 	else if (xPos == pattMark.markX2)
1106 	{
1107 		pattMark.markX2 = xPos - 1;
1108 	}
1109 	else if (xPos != pattMark.markX1 && xPos != pattMark.markX2)
1110 	{
1111 		pattMark.markX1 = xPos - 1;
1112 		pattMark.markX2 = xPos;
1113 		pattMark.markY1 = row - 1;
1114 		pattMark.markY2 = row;
1115 	}
1116 
1117 	checkMarkLimits();
1118 	chanLeft();
1119 }
1120 
keybPattMarkRight(void)1121 void keybPattMarkRight(void)
1122 {
1123 	int8_t xPos = cursor.ch;
1124 	int16_t row = editor.row;
1125 
1126 	if (row != pattMark.markY1-1 && row != pattMark.markY2)
1127 	{
1128 		pattMark.markY1 = row - 1;
1129 		pattMark.markY2 = row;
1130 	}
1131 
1132 	if (xPos == pattMark.markX2)
1133 	{
1134 		pattMark.markX2 = xPos + 1;
1135 	}
1136 	else if (xPos == pattMark.markX1)
1137 	{
1138 		pattMark.markX1 = xPos + 1;
1139 	}
1140 	else if (xPos != pattMark.markX1 && xPos != pattMark.markX2)
1141 	{
1142 		pattMark.markX1 = xPos;
1143 		pattMark.markX2 = xPos + 1;
1144 		pattMark.markY1 = row - 1;
1145 		pattMark.markY2 = row;
1146 	}
1147 
1148 	checkMarkLimits();
1149 	chanRight();
1150 }
1151 
loadTrack(UNICHAR * filenameU)1152 bool loadTrack(UNICHAR *filenameU)
1153 {
1154 	note_t loadBuff[MAX_PATT_LEN];
1155 	xtHdr_t h;
1156 
1157 	FILE *f = UNICHAR_FOPEN(filenameU, "rb");
1158 	if (f == NULL)
1159 	{
1160 		okBox(0, "System message", "General I/O error during loading! Is the file in use?");
1161 		return false;
1162 	}
1163 
1164 	if (fread(&h, 1, sizeof (h), f) != sizeof (h))
1165 	{
1166 		okBox(0, "System message", "General I/O error during loading! Is the file in use?");
1167 		goto trackLoadError;
1168 	}
1169 
1170 	if (h.version != 1)
1171 	{
1172 		okBox(0, "System message", "Incompatible format version!");
1173 		goto trackLoadError;
1174 	}
1175 
1176 	if (h.numRows > MAX_PATT_LEN)
1177 		h.numRows = MAX_PATT_LEN;
1178 
1179 	int16_t numRows = patternNumRows[editor.editPattern];
1180 	if (numRows > h.numRows)
1181 		numRows = h.numRows;
1182 
1183 	if (fread(loadBuff, numRows * sizeof (note_t), 1, f) != 1)
1184 	{
1185 		okBox(0, "System message", "General I/O error during loading! Is the file in use?");
1186 		goto trackLoadError;
1187 	}
1188 
1189 	if (!allocatePattern(editor.editPattern))
1190 	{
1191 		okBox(0, "System message", "Not enough memory!");
1192 		goto trackLoadError;
1193 	}
1194 
1195 	lockMixerCallback();
1196 	for (int32_t i = 0; i < numRows; i++)
1197 	{
1198 		note_t *p = &pattern[editor.editPattern][(i * MAX_CHANNELS) + cursor.ch];
1199 
1200 		*p = loadBuff[i];
1201 
1202 		// sanitize stuff (FT2 doesn't do this!)
1203 
1204 		if (p->note > 97)
1205 			p->note = 0;
1206 
1207 		if (p->instr > 128)
1208 			p->instr = 0;
1209 
1210 		if (p->efx > 35)
1211 		{
1212 			p->efx = 0;
1213 			p->efxData = 0;
1214 		}
1215 	}
1216 	unlockMixerCallback();
1217 
1218 	fclose(f);
1219 
1220 	ui.updatePatternEditor = true;
1221 	ui.updatePosSections = true;
1222 
1223 	diskOpSetFilename(DISKOP_ITEM_TRACK, filenameU);
1224 	setSongModifiedFlag();
1225 
1226 	return true;
1227 
1228 trackLoadError:
1229 	fclose(f);
1230 	return false;
1231 }
1232 
saveTrack(UNICHAR * filenameU)1233 bool saveTrack(UNICHAR *filenameU)
1234 {
1235 	note_t saveBuff[MAX_PATT_LEN];
1236 	xtHdr_t h;
1237 
1238 	note_t *p = pattern[editor.editPattern];
1239 	if (p == NULL)
1240 	{
1241 		okBox(0, "System message", "The current pattern is empty!");
1242 		return false;
1243 	}
1244 
1245 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");
1246 	if (f == NULL)
1247 	{
1248 		okBox(0, "System message", "General I/O error during saving! Is the file in use?");
1249 		return false;
1250 	}
1251 
1252 	h.version = 1;
1253 	h.numRows = patternNumRows[editor.editPattern];
1254 
1255 	for (int32_t i = 0; i < h.numRows; i++)
1256 		saveBuff[i] = p[(i * MAX_CHANNELS) + cursor.ch];
1257 
1258 	if (fwrite(&h, sizeof (h), 1, f) !=  1)
1259 	{
1260 		fclose(f);
1261 		okBox(0, "System message", "General I/O error during saving! Is the file in use?");
1262 		return false;
1263 	}
1264 
1265 	if (fwrite(saveBuff, h.numRows * sizeof (note_t), 1, f) != 1)
1266 	{
1267 		fclose(f);
1268 		okBox(0, "System message", "General I/O error during saving! Is the file in use?");
1269 		return false;
1270 	}
1271 
1272 	fclose(f);
1273 	return true;
1274 }
1275 
loadPattern(UNICHAR * filenameU)1276 bool loadPattern(UNICHAR *filenameU)
1277 {
1278 	xpHdr_t h;
1279 
1280 	FILE *f = UNICHAR_FOPEN(filenameU, "rb");
1281 	if (f == NULL)
1282 	{
1283 		okBox(0, "System message", "General I/O error during loading! Is the file in use?");
1284 		return false;
1285 	}
1286 
1287 	if (!allocatePattern(editor.editPattern))
1288 	{
1289 		okBox(0, "System message", "Not enough memory!");
1290 		goto loadPattError;
1291 	}
1292 
1293 	if (fread(&h, 1, sizeof (h), f) != sizeof (h))
1294 	{
1295 		okBox(0, "System message", "General I/O error during loading! Is the file in use?");
1296 		goto loadPattError;
1297 	}
1298 
1299 	if (h.version != 1)
1300 	{
1301 		okBox(0, "System message", "Incompatible format version!");
1302 		goto loadPattError;
1303 	}
1304 
1305 	if (h.numRows > MAX_PATT_LEN)
1306 		h.numRows = MAX_PATT_LEN;
1307 
1308 	lockMixerCallback();
1309 
1310 	note_t *p = pattern[editor.editPattern];
1311 	if (fread(p, h.numRows * TRACK_WIDTH, 1, f) != 1)
1312 	{
1313 		unlockMixerCallback();
1314 		okBox(0, "System message", "General I/O error during loading! Is the file in use?");
1315 		goto loadPattError;
1316 	}
1317 
1318 	// sanitize data (FT2 doesn't do this!)
1319 	for (int32_t row = 0; row < h.numRows; row++)
1320 	{
1321 		for (int32_t ch = 0; ch < MAX_CHANNELS; ch++)
1322 		{
1323 			p = &pattern[editor.editPattern][(row * MAX_CHANNELS) + ch];
1324 
1325 			if (p->note > 97)
1326 				p->note = 0;
1327 
1328 			if (p->instr > 128)
1329 				p->instr = 128;
1330 
1331 			if (p->efx > 35)
1332 			{
1333 				p->efx = 0;
1334 				p->efxData = 0;
1335 			}
1336 		}
1337 	}
1338 
1339 	// set new pattern length (FT2 doesn't do this, strange...)
1340 	song.currNumRows = patternNumRows[editor.editPattern] = h.numRows;
1341 
1342 	if (song.row >= song.currNumRows)
1343 	{
1344 		song.row = song.currNumRows-1;
1345 		if (!songPlaying)
1346 			editor.row = song.row;
1347 	}
1348 
1349 	unlockMixerCallback();
1350 
1351 	fclose(f);
1352 
1353 	ui.updatePatternEditor = true;
1354 	ui.updatePosSections = true;
1355 
1356 	diskOpSetFilename(DISKOP_ITEM_PATTERN, filenameU);
1357 	setSongModifiedFlag();
1358 
1359 	return true;
1360 
1361 loadPattError:
1362 	fclose(f);
1363 	return false;
1364 }
1365 
savePattern(UNICHAR * filenameU)1366 bool savePattern(UNICHAR *filenameU)
1367 {
1368 	xpHdr_t h;
1369 
1370 	note_t *p = pattern[editor.editPattern];
1371 	if (p == NULL)
1372 	{
1373 		okBox(0, "System message", "The current pattern is empty!");
1374 		return false;
1375 	}
1376 
1377 	FILE *f = UNICHAR_FOPEN(filenameU, "wb");
1378 	if (f == NULL)
1379 	{
1380 		okBox(0, "System message", "General I/O error during saving! Is the file in use?");
1381 		return false;
1382 	}
1383 
1384 	h.version = 1;
1385 	h.numRows = patternNumRows[editor.editPattern];
1386 
1387 	if (fwrite(&h, 1, sizeof (h), f) != sizeof (h))
1388 	{
1389 		fclose(f);
1390 		okBox(0, "System message", "General I/O error during saving! Is the file in use?");
1391 		return false;
1392 	}
1393 
1394 	if (fwrite(p, h.numRows * TRACK_WIDTH, 1, f) != 1)
1395 	{
1396 		fclose(f);
1397 		okBox(0, "System message", "General I/O error during saving! Is the file in use?");
1398 		return false;
1399 	}
1400 
1401 	fclose(f);
1402 	return true;
1403 }
1404 
scrollChannelLeft(void)1405 void scrollChannelLeft(void)
1406 {
1407 	scrollBarScrollLeft(SB_CHAN_SCROLL, 1);
1408 }
1409 
scrollChannelRight(void)1410 void scrollChannelRight(void)
1411 {
1412 	scrollBarScrollRight(SB_CHAN_SCROLL, 1);
1413 }
1414 
setChannelScrollPos(uint32_t pos)1415 void setChannelScrollPos(uint32_t pos)
1416 {
1417 	if (!ui.pattChanScrollShown)
1418 	{
1419 		ui.channelOffset = 0;
1420 		return;
1421 	}
1422 
1423 	if (ui.channelOffset == (uint8_t)pos)
1424 		return;
1425 
1426 	ui.channelOffset = (uint8_t)pos;
1427 
1428 	assert(song.numChannels > ui.numChannelsShown);
1429 	if (ui.channelOffset >= song.numChannels-ui.numChannelsShown)
1430 		ui.channelOffset = (uint8_t)(song.numChannels-ui.numChannelsShown);
1431 
1432 	if (cursor.ch >= ui.channelOffset+ui.numChannelsShown)
1433 	{
1434 		cursor.object = CURSOR_NOTE;
1435 		cursor.ch = (ui.channelOffset + ui.numChannelsShown) - 1;
1436 	}
1437 	else if (cursor.ch < ui.channelOffset)
1438 	{
1439 		cursor.object = CURSOR_NOTE;
1440 		cursor.ch = ui.channelOffset;
1441 	}
1442 
1443 	ui.updatePatternEditor = true;
1444 }
1445 
jumpToChannel(uint8_t chNr)1446 void jumpToChannel(uint8_t chNr) // for ALT+q..i ALT+a..k
1447 {
1448 	if (ui.sampleEditorShown || ui.instEditorShown)
1449 		return;
1450 
1451 	chNr %= song.numChannels;
1452 	if (cursor.ch == chNr)
1453 		return;
1454 
1455 	if (ui.pattChanScrollShown)
1456 	{
1457 		assert(song.numChannels > ui.numChannelsShown);
1458 
1459 		if (chNr >= ui.channelOffset+ui.numChannelsShown)
1460 			scrollBarScrollDown(SB_CHAN_SCROLL, (chNr - (ui.channelOffset + ui.numChannelsShown)) + 1);
1461 		else if (chNr < ui.channelOffset)
1462 			scrollBarScrollUp(SB_CHAN_SCROLL, ui.channelOffset - chNr);
1463 	}
1464 
1465 	cursor.ch = chNr; // set it here since scrollBarScrollX() changes it...
1466 	ui.updatePatternEditor = true;
1467 }
1468 
sbPosEdPos(uint32_t pos)1469 void sbPosEdPos(uint32_t pos)
1470 {
1471 	const bool audioWasntLocked = !audio.locked;
1472 	if (audioWasntLocked)
1473 		lockAudio();
1474 
1475 	if (song.songPos != (int16_t)pos)
1476 		setNewSongPos((int16_t)pos);
1477 
1478 	if (audioWasntLocked)
1479 		unlockAudio();
1480 }
1481 
pbPosEdPosUp(void)1482 void pbPosEdPosUp(void)
1483 {
1484 	incSongPos();
1485 }
1486 
pbPosEdPosDown(void)1487 void pbPosEdPosDown(void)
1488 {
1489 	decSongPos();
1490 }
1491 
pbPosEdIns(void)1492 void pbPosEdIns(void)
1493 {
1494 	if (song.songLength >= 255)
1495 		return;
1496 
1497 	lockMixerCallback();
1498 
1499 	const uint8_t oldPatt = song.orders[song.songPos];
1500 	for (uint16_t i = 0; i < 255-song.songPos; i++)
1501 		song.orders[255-i] = song.orders[254-i];
1502 	song.orders[song.songPos] = oldPatt;
1503 
1504 	song.songLength++;
1505 
1506 	ui.updatePosSections = true;
1507 	ui.updatePosEdScrollBar = true;
1508 	setSongModifiedFlag();
1509 
1510 	unlockMixerCallback();
1511 }
1512 
pbPosEdDel(void)1513 void pbPosEdDel(void)
1514 {
1515 	if (song.songLength <= 1)
1516 		return;
1517 
1518 	lockMixerCallback();
1519 
1520 	if (song.songPos < 254)
1521 	{
1522 		for (uint16_t i = 0; i < 254-song.songPos; i++)
1523 			song.orders[song.songPos+i] = song.orders[song.songPos+1+i];
1524 	}
1525 
1526 	song.songLength--;
1527 	if (song.songLoopStart >= song.songLength)
1528 		song.songLoopStart = song.songLength - 1;
1529 
1530 	if (song.songPos > song.songLength-1)
1531 	{
1532 		editor.songPos = song.songPos = song.songLength-1;
1533 		setPos(song.songPos, -1, false);
1534 	}
1535 
1536 	ui.updatePosSections = true;
1537 	ui.updatePosEdScrollBar = true;
1538 	setSongModifiedFlag();
1539 
1540 	unlockMixerCallback();
1541 }
1542 
pbPosEdPattUp(void)1543 void pbPosEdPattUp(void)
1544 {
1545 	if (song.orders[song.songPos] == 255)
1546 		return;
1547 
1548 	lockMixerCallback();
1549 	if (song.orders[song.songPos] < 255)
1550 	{
1551 		song.orders[song.songPos]++;
1552 		song.pattNum = song.orders[song.songPos];
1553 
1554 		song.currNumRows = patternNumRows[song.pattNum];
1555 		if (song.row >= song.currNumRows)
1556 		{
1557 			song.row = song.currNumRows-1;
1558 			if (!songPlaying)
1559 				editor.row = song.row;
1560 		}
1561 
1562 		if (!songPlaying)
1563 			editor.editPattern = (uint8_t)song.pattNum;
1564 
1565 		checkMarkLimits();
1566 		ui.updatePatternEditor = true;
1567 		ui.updatePosSections = true;
1568 
1569 		setSongModifiedFlag();
1570 	}
1571 	unlockMixerCallback();
1572 }
1573 
pbPosEdPattDown(void)1574 void pbPosEdPattDown(void)
1575 {
1576 	if (song.orders[song.songPos] == 0)
1577 		return;
1578 
1579 	lockMixerCallback();
1580 	if (song.orders[song.songPos] > 0)
1581 	{
1582 		song.orders[song.songPos]--;
1583 		song.pattNum = song.orders[song.songPos];
1584 
1585 		song.currNumRows = patternNumRows[song.pattNum];
1586 		if (song.row >= song.currNumRows)
1587 		{
1588 			song.row = song.currNumRows-1;
1589 			if (!songPlaying)
1590 				editor.row = song.row;
1591 		}
1592 
1593 		if (!songPlaying)
1594 			editor.editPattern = (uint8_t)song.pattNum;
1595 
1596 		checkMarkLimits();
1597 		ui.updatePatternEditor = true;
1598 		ui.updatePosSections = true;
1599 
1600 		setSongModifiedFlag();
1601 	}
1602 	unlockMixerCallback();
1603 }
1604 
pbPosEdLenUp(void)1605 void pbPosEdLenUp(void)
1606 {
1607 	if (song.songLength >= 255)
1608 		return;
1609 
1610 	const bool audioWasntLocked = !audio.locked;
1611 	if (audioWasntLocked)
1612 		lockAudio();
1613 
1614 	song.songLength++;
1615 
1616 	ui.updatePosSections = true;
1617 	ui.updatePosEdScrollBar = true;
1618 	setSongModifiedFlag();
1619 
1620 	if (audioWasntLocked)
1621 		unlockAudio();
1622 }
1623 
pbPosEdLenDown(void)1624 void pbPosEdLenDown(void)
1625 {
1626 	if (song.songLength <= 1)
1627 		return;
1628 
1629 	const bool audioWasntLocked = !audio.locked;
1630 	if (audioWasntLocked)
1631 		lockAudio();
1632 
1633 	song.songLength--;
1634 	if (song.songLoopStart >= song.songLength)
1635 		song.songLoopStart = song.songLength - 1;
1636 
1637 	if (song.songPos >= song.songLength)
1638 	{
1639 		song.songPos = song.songLength - 1;
1640 		setPos(song.songPos, -1, false);
1641 	}
1642 
1643 	ui.updatePosSections = true;
1644 	ui.updatePosEdScrollBar = true;
1645 	setSongModifiedFlag();
1646 
1647 	if (audioWasntLocked)
1648 		unlockAudio();
1649 }
1650 
pbPosEdRepSUp(void)1651 void pbPosEdRepSUp(void)
1652 {
1653 	const bool audioWasntLocked = !audio.locked;
1654 	if (audioWasntLocked)
1655 		lockAudio();
1656 
1657 	if (song.songLoopStart < song.songLength-1)
1658 	{
1659 		song.songLoopStart++;
1660 		ui.updatePosSections = true;
1661 		setSongModifiedFlag();
1662 	}
1663 
1664 	if (audioWasntLocked)
1665 		unlockAudio();
1666 }
1667 
pbPosEdRepSDown(void)1668 void pbPosEdRepSDown(void)
1669 {
1670 	const bool audioWasntLocked = !audio.locked;
1671 	if (audioWasntLocked)
1672 		lockAudio();
1673 
1674 	if (song.songLoopStart > 0)
1675 	{
1676 		song.songLoopStart--;
1677 		ui.updatePosSections = true;
1678 		setSongModifiedFlag();
1679 	}
1680 
1681 	if (audioWasntLocked)
1682 		unlockAudio();
1683 }
1684 
pbBPMUp(void)1685 void pbBPMUp(void)
1686 {
1687 	if (song.BPM == 255)
1688 		return;
1689 
1690 	const bool audioWasntLocked = !audio.locked;
1691 	if (audioWasntLocked)
1692 		lockAudio();
1693 
1694 	if (song.BPM < 255)
1695 	{
1696 		song.BPM++;
1697 		setMixerBPM(song.BPM);
1698 
1699 		// if song is playing, the update is handled in the audio/video sync queue
1700 		if (!songPlaying)
1701 		{
1702 			editor.BPM = song.BPM;
1703 			drawSongBPM(song.BPM);
1704 		}
1705 	}
1706 
1707 	if (audioWasntLocked)
1708 		unlockAudio();
1709 }
1710 
pbBPMDown(void)1711 void pbBPMDown(void)
1712 {
1713 	if (song.BPM == 32)
1714 		return;
1715 
1716 	const bool audioWasntLocked = !audio.locked;
1717 	if (audioWasntLocked)
1718 		lockAudio();
1719 
1720 	if (song.BPM > 32)
1721 	{
1722 		song.BPM--;
1723 		setMixerBPM(song.BPM);
1724 
1725 		// if song is playing, the update is handled in the audio/video sync queue
1726 		if (!songPlaying)
1727 		{
1728 			editor.BPM = song.BPM;
1729 			drawSongBPM(editor.BPM);
1730 		}
1731 	}
1732 
1733 	if (audioWasntLocked)
1734 		unlockAudio();
1735 }
1736 
pbSpeedUp(void)1737 void pbSpeedUp(void)
1738 {
1739 	if (song.speed == 31)
1740 		return;
1741 
1742 	const bool audioWasntLocked = !audio.locked;
1743 	if (audioWasntLocked)
1744 		lockAudio();
1745 
1746 	if (song.speed < 31)
1747 	{
1748 		song.speed++;
1749 
1750 		// if song is playing, the update is handled in the audio/video sync queue
1751 		if (!songPlaying)
1752 		{
1753 			editor.speed = song.speed;
1754 			drawSongSpeed(editor.speed);
1755 		}
1756 	}
1757 
1758 	if (audioWasntLocked)
1759 		unlockAudio();
1760 }
1761 
pbSpeedDown(void)1762 void pbSpeedDown(void)
1763 {
1764 	if (song.speed == 0)
1765 		return;
1766 
1767 	const bool audioWasntLocked = !audio.locked;
1768 	if (audioWasntLocked)
1769 		lockAudio();
1770 
1771 	if (song.speed > 0)
1772 	{
1773 		song.speed--;
1774 
1775 		// if song is playing, the update is handled in the audio/video sync queue
1776 		if (!songPlaying)
1777 		{
1778 			editor.speed = song.speed;
1779 			drawSongSpeed(editor.speed);
1780 		}
1781 	}
1782 
1783 	if (audioWasntLocked)
1784 		unlockAudio();
1785 }
1786 
pbIncAdd(void)1787 void pbIncAdd(void)
1788 {
1789 	if (editor.editRowSkip == 16)
1790 		editor.editRowSkip = 0;
1791 	else
1792 		editor.editRowSkip++;
1793 
1794 	drawIDAdd();
1795 }
1796 
pbDecAdd(void)1797 void pbDecAdd(void)
1798 {
1799 	if (editor.editRowSkip == 0)
1800 		editor.editRowSkip = 16;
1801 	else
1802 		editor.editRowSkip--;
1803 
1804 	drawIDAdd();
1805 }
1806 
pbAddChan(void)1807 void pbAddChan(void)
1808 {
1809 	if (song.numChannels > 30)
1810 		return;
1811 
1812 	lockMixerCallback();
1813 	song.numChannels += 2;
1814 
1815 	hideTopScreen();
1816 	showTopLeftMainScreen(true);
1817 	showTopRightMainScreen();
1818 
1819 	if (ui.patternEditorShown)
1820 		showPatternEditor();
1821 
1822 	setSongModifiedFlag();
1823 	unlockMixerCallback();
1824 }
1825 
pbSubChan(void)1826 void pbSubChan(void)
1827 {
1828 	if (song.numChannels < 4)
1829 		return;
1830 
1831 	lockMixerCallback();
1832 
1833 	song.numChannels -= 2;
1834 
1835 	checkMarkLimits();
1836 
1837 	hideTopScreen();
1838 	showTopLeftMainScreen(true);
1839 	showTopRightMainScreen();
1840 
1841 	if (ui.patternEditorShown)
1842 		showPatternEditor();
1843 
1844 	setSongModifiedFlag();
1845 	unlockMixerCallback();
1846 }
1847 
pbEditPattUp(void)1848 void pbEditPattUp(void)
1849 {
1850 	const bool audioWasntLocked = !audio.locked;
1851 	if (audioWasntLocked)
1852 		lockAudio();
1853 
1854 	if (song.pattNum < 255)
1855 	{
1856 		song.pattNum++;
1857 
1858 		song.currNumRows = patternNumRows[song.pattNum];
1859 		if (song.row >= song.currNumRows)
1860 		{
1861 			song.row = song.currNumRows-1;
1862 			if (!songPlaying)
1863 				editor.row = song.row;
1864 		}
1865 
1866 		if (!songPlaying)
1867 			editor.editPattern = (uint8_t)song.pattNum;
1868 
1869 		checkMarkLimits();
1870 		ui.updatePatternEditor = true;
1871 		ui.updatePosSections = true;
1872 	}
1873 
1874 	if (audioWasntLocked)
1875 		unlockAudio();
1876 }
1877 
pbEditPattDown(void)1878 void pbEditPattDown(void)
1879 {
1880 	const bool audioWasntLocked = !audio.locked;
1881 	if (audioWasntLocked)
1882 		lockAudio();
1883 
1884 	if (song.pattNum > 0)
1885 	{
1886 		song.pattNum--;
1887 
1888 		song.currNumRows = patternNumRows[song.pattNum];
1889 		if (song.row >= song.currNumRows)
1890 		{
1891 			song.row = song.currNumRows-1;
1892 			if (!songPlaying)
1893 				editor.row = song.row;
1894 		}
1895 
1896 		if (!songPlaying)
1897 			editor.editPattern = (uint8_t)song.pattNum;
1898 
1899 		checkMarkLimits();
1900 		ui.updatePatternEditor = true;
1901 		ui.updatePosSections = true;
1902 	}
1903 
1904 	if (audioWasntLocked)
1905 		unlockAudio();
1906 }
1907 
pbPattLenUp(void)1908 void pbPattLenUp(void)
1909 {
1910 	const uint16_t numRows = patternNumRows[editor.editPattern];
1911 	if (numRows >= MAX_PATT_LEN)
1912 		return;
1913 
1914 	const bool audioWasntLocked = !audio.locked;
1915 	if (audioWasntLocked)
1916 		lockAudio();
1917 
1918 	song.pattNum = editor.editPattern; // kludge
1919 	setPatternLen(editor.editPattern, numRows+1);
1920 
1921 	ui.updatePatternEditor = true;
1922 	ui.updatePosSections = true;
1923 	setSongModifiedFlag();
1924 
1925 	if (audioWasntLocked)
1926 		unlockAudio();
1927 }
1928 
pbPattLenDown(void)1929 void pbPattLenDown(void)
1930 {
1931 	const uint16_t numRows = patternNumRows[editor.editPattern];
1932 	if (numRows <= 1)
1933 		return;
1934 
1935 	const bool audioWasntLocked = !audio.locked;
1936 	if (audioWasntLocked)
1937 		lockAudio();
1938 
1939 	song.pattNum = editor.editPattern; // kludge
1940 	setPatternLen(editor.editPattern, numRows-1);
1941 
1942 	ui.updatePatternEditor = true;
1943 	ui.updatePosSections = true;
1944 	setSongModifiedFlag();
1945 
1946 	if (audioWasntLocked)
1947 		unlockAudio();
1948 }
1949 
drawPosEdNums(int16_t songPos)1950 void drawPosEdNums(int16_t songPos)
1951 {
1952 	if (songPos >= song.songLength)
1953 		songPos = song.songLength - 1;
1954 
1955 	// clear
1956 	if (ui.extended)
1957 	{
1958 		clearRect(8,  4, 39, 16);
1959 		fillRect(8, 23, 39, 7, PAL_DESKTOP);
1960 		clearRect(8, 33, 39, 16);
1961 	}
1962 	else
1963 	{
1964 		clearRect(8,  4, 39, 15);
1965 		fillRect(8, 22, 39, 7, PAL_DESKTOP);
1966 		clearRect(8, 32, 39, 15);
1967 	}
1968 
1969 	const uint32_t color1 = video.palette[PAL_PATTEXT];
1970 	const uint32_t color2 = video.palette[PAL_FORGRND];
1971 
1972 	// top two
1973 	for (int16_t y = 0; y < 2; y++)
1974 	{
1975 		int16_t entry = songPos - (2 - y);
1976 		if (entry < 0)
1977 			continue;
1978 
1979 		assert(entry < 256);
1980 
1981 		if (ui.extended)
1982 		{
1983 			pattTwoHexOut(8,  4 + (y * 9), (uint8_t)entry, color1);
1984 			pattTwoHexOut(32, 4 + (y * 9), song.orders[entry], color1);
1985 		}
1986 		else
1987 		{
1988 			pattTwoHexOut(8,  4 + (y * 8), (uint8_t)entry, color1);
1989 			pattTwoHexOut(32, 4 + (y * 8), song.orders[entry], color1);
1990 		}
1991 	}
1992 
1993 	assert(songPos < 256);
1994 
1995 	// middle
1996 	if (ui.extended)
1997 	{
1998 		pattTwoHexOut(8,  23, (uint8_t)songPos, color2);
1999 		pattTwoHexOut(32, 23, song.orders[songPos], color2);
2000 	}
2001 	else
2002 	{
2003 		pattTwoHexOut(8,  22, (uint8_t)songPos, color2);
2004 		pattTwoHexOut(32, 22, song.orders[songPos], color2);
2005 	}
2006 
2007 	// bottom two
2008 	for (int16_t y = 0; y < 2; y++)
2009 	{
2010 		int16_t entry = songPos + (1 + y);
2011 		if (entry >= song.songLength)
2012 			break;
2013 
2014 		if (ui.extended)
2015 		{
2016 			pattTwoHexOut(8,  33 + (y * 9), (uint8_t)entry, color1);
2017 			pattTwoHexOut(32, 33 + (y * 9), song.orders[entry], color1);
2018 		}
2019 		else
2020 		{
2021 			pattTwoHexOut(8,  32 + (y * 8), (uint8_t)entry, color1);
2022 			pattTwoHexOut(32, 32 + (y * 8), song.orders[entry], color1);
2023 		}
2024 	}
2025 }
2026 
drawSongLength(void)2027 void drawSongLength(void)
2028 {
2029 	int16_t x, y;
2030 
2031 	if (ui.extended)
2032 	{
2033 		x = 165;
2034 		y = 5;
2035 	}
2036 	else
2037 	{
2038 		x = 59;
2039 		y = 52;
2040 	}
2041 
2042 	hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, (uint8_t)song.songLength, 2);
2043 }
2044 
drawSongLoopStart(void)2045 void drawSongLoopStart(void)
2046 {
2047 	int16_t x, y;
2048 
2049 	if (ui.extended)
2050 	{
2051 		x = 165;
2052 		y = 19;
2053 	}
2054 	else
2055 	{
2056 		x = 59;
2057 		y = 64;
2058 	}
2059 
2060 	hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, (uint8_t)song.songLoopStart, 2);
2061 }
2062 
drawSongBPM(uint16_t val)2063 void drawSongBPM(uint16_t val)
2064 {
2065 	if (ui.extended)
2066 		return;
2067 
2068 	if (val > 255)
2069 		val = 255;
2070 
2071 	textOutFixed(145, 36, PAL_FORGRND, PAL_DESKTOP, dec3StrTab[val]);
2072 }
2073 
drawSongSpeed(uint16_t val)2074 void drawSongSpeed(uint16_t val)
2075 {
2076 	if (ui.extended)
2077 		return;
2078 
2079 	if (val > 99)
2080 		val = 99;
2081 
2082 	textOutFixed(152, 50, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[val]);
2083 }
2084 
drawEditPattern(uint16_t editPattern)2085 void drawEditPattern(uint16_t editPattern)
2086 {
2087 	int16_t x, y;
2088 
2089 	if (ui.extended)
2090 	{
2091 		x = 252;
2092 		y = 39;
2093 	}
2094 	else
2095 	{
2096 		x = 237;
2097 		y = 36;
2098 	}
2099 
2100 	hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, editPattern, 2);
2101 }
2102 
drawPatternLength(uint16_t editPattern)2103 void drawPatternLength(uint16_t editPattern)
2104 {
2105 	int16_t x, y;
2106 
2107 	if (ui.extended)
2108 	{
2109 		x = 326;
2110 		y = 39;
2111 	}
2112 	else
2113 	{
2114 		x = 230;
2115 		y = 50;
2116 	}
2117 
2118 	hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, patternNumRows[editPattern], 3);
2119 }
2120 
drawGlobalVol(uint16_t val)2121 void drawGlobalVol(uint16_t val)
2122 {
2123 	if (ui.extended)
2124 		return;
2125 
2126 	assert(val <= 64);
2127 	textOutFixed(87, 80, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[val]);
2128 }
2129 
drawIDAdd(void)2130 void drawIDAdd(void)
2131 {
2132 	assert(editor.editRowSkip <= 16);
2133 	textOutFixed(152, 64, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[editor.editRowSkip]);
2134 }
2135 
resetPlaybackTime(void)2136 void resetPlaybackTime(void)
2137 {
2138 	song.musicTime64 = 0;
2139 	last_TimeH = 0;
2140 	last_TimeM = 0;
2141 	last_TimeS = 0;
2142 }
2143 
drawPlaybackTime(void)2144 void drawPlaybackTime(void)
2145 {
2146 	if (songPlaying)
2147 	{
2148 		const uint32_t ms1024 = song.musicTime64 >> 32; // milliseconds (scaled from 1000 to 1024)
2149 		uint32_t seconds = ms1024 >> 10;
2150 
2151 		last_TimeH = seconds / 3600;
2152 		seconds -= last_TimeH * 3600;
2153 
2154 		last_TimeM = seconds / 60;
2155 		seconds -= last_TimeM * 60;
2156 
2157 		last_TimeS = seconds;
2158 	}
2159 
2160 	textOutFixed(235, 80, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[last_TimeH]);
2161 	textOutFixed(255, 80, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[last_TimeM]);
2162 	textOutFixed(275, 80, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[last_TimeS]);
2163 }
2164 
drawSongName(void)2165 void drawSongName(void)
2166 {
2167 	drawFramework(421, 155, 166, 18, FRAMEWORK_TYPE1);
2168 	drawFramework(423, 157, 162, 14, FRAMEWORK_TYPE2);
2169 	drawTextBox(TB_SONG_NAME);
2170 }
2171 
changeLogoType(uint8_t logoType)2172 void changeLogoType(uint8_t logoType)
2173 {
2174 	pushButtons[PB_LOGO].bitmapFlag = true;
2175 
2176 	if (logoType == 0)
2177 	{
2178 		pushButtons[PB_LOGO].bitmapUnpressed = &bmp.ft2LogoBadges[(154 * 32) * 0];
2179 		pushButtons[PB_LOGO].bitmapPressed = &bmp.ft2LogoBadges[(154 * 32) * 1];
2180 	}
2181 	else
2182 	{
2183 		pushButtons[PB_LOGO].bitmapUnpressed = &bmp.ft2LogoBadges[(154 * 32) * 2];
2184 		pushButtons[PB_LOGO].bitmapPressed = &bmp.ft2LogoBadges[(154 * 32) * 3];
2185 	}
2186 
2187 	drawPushButton(PB_LOGO);
2188 }
2189 
changeBadgeType(uint8_t badgeType)2190 void changeBadgeType(uint8_t badgeType)
2191 {
2192 	pushButtons[PB_BADGE].bitmapFlag = true;
2193 
2194 	if (badgeType == 0)
2195 	{
2196 		pushButtons[PB_BADGE].bitmapUnpressed = &bmp.ft2ByBadges[(25 * 32) * 0];
2197 		pushButtons[PB_BADGE].bitmapPressed = &bmp.ft2ByBadges[(25 * 32) * 1];
2198 	}
2199 	else
2200 	{
2201 		pushButtons[PB_BADGE].bitmapUnpressed = &bmp.ft2ByBadges[(25 * 32) * 2];
2202 		pushButtons[PB_BADGE].bitmapPressed = &bmp.ft2ByBadges[(25 * 32) * 3];
2203 	}
2204 
2205 	drawPushButton(PB_BADGE);
2206 }
2207 
updateInstrumentSwitcher(void)2208 void updateInstrumentSwitcher(void)
2209 {
2210 	int16_t y;
2211 
2212 	if (ui.aboutScreenShown || ui.configScreenShown || ui.helpScreenShown || ui.nibblesShown)
2213 		return; // don't redraw instrument switcher when it's not shown!
2214 
2215 	if (ui.extended) // extended pattern editor
2216 	{
2217 		//INSTRUMENTS
2218 
2219 		clearRect(388, 5, 116, 43); // left box
2220 		clearRect(511, 5, 116, 43); // right box
2221 
2222 		// draw source instrument selection
2223 		if (editor.srcInstr >= editor.instrBankOffset && editor.srcInstr <= editor.instrBankOffset+8)
2224 		{
2225 			y = 5 + ((editor.srcInstr - editor.instrBankOffset - 1) * 11);
2226 			if (y >= 5 && y <= 82)
2227 			{
2228 				if (y <= 47)
2229 					fillRect(388, y, 15, 10, PAL_BUTTONS); // left box
2230 				else
2231 					fillRect(511, y - 44, 15, 10, PAL_BUTTONS); // right box
2232 			}
2233 		}
2234 
2235 		// draw destination instrument selection
2236 		if (editor.curInstr >= editor.instrBankOffset && editor.curInstr <= editor.instrBankOffset+8)
2237 		{
2238 			y = 5 + ((editor.curInstr - editor.instrBankOffset - 1) * 11);
2239 			y = 5 + ((editor.curInstr - editor.instrBankOffset - 1) * 11);
2240 			if (y >= 5 && y <= 82)
2241 			{
2242 				if (y <= 47)
2243 					fillRect(406, y, 98, 10, PAL_BUTTONS); // left box
2244 				else
2245 					fillRect(529, y - 44, 98, 10, PAL_BUTTONS); // right box
2246 			}
2247 		}
2248 
2249 		// draw numbers and texts
2250 		for (int16_t i = 0; i < 4; i++)
2251 		{
2252 			hexOut(388, 5 + (i * 11), PAL_FORGRND, 1 + editor.instrBankOffset + i, 2);
2253 			hexOut(511, 5 + (i * 11), PAL_FORGRND, 5 + editor.instrBankOffset + i, 2);
2254 			drawTextBox(TB_INST1 + i);
2255 			drawTextBox(TB_INST5 + i);
2256 		}
2257 	}
2258 	else // normal pattern editor
2259 	{
2260 		// INSTRUMENTS
2261 
2262 		clearRect(424, 5,  15, 87); // src instrument
2263 		clearRect(446, 5, 139, 87); // main instrument
2264 
2265 		// draw source instrument selection
2266 		if (editor.srcInstr >= editor.instrBankOffset && editor.srcInstr <= editor.instrBankOffset+8)
2267 		{
2268 			y = 5 + ((editor.srcInstr - editor.instrBankOffset - 1) * 11);
2269 			if (y >= 5 && y <= 82)
2270 				fillRect(424, y, 15, 10, PAL_BUTTONS);
2271 		}
2272 
2273 		// draw destination instrument selection
2274 		if (editor.curInstr >= editor.instrBankOffset && editor.curInstr <= editor.instrBankOffset+8)
2275 		{
2276 			y = 5 + ((editor.curInstr - editor.instrBankOffset - 1) * 11);
2277 			if (y >= 5 && y <= 82)
2278 				fillRect(446, y, 139, 10, PAL_BUTTONS);
2279 		}
2280 
2281 		// draw numbers and texts
2282 		for (int16_t i = 0; i < 8; i++)
2283 		{
2284 			hexOut(424, 5 + (i * 11), PAL_FORGRND, 1 + editor.instrBankOffset + i, 2);
2285 			drawTextBox(TB_INST1 + i);
2286 		}
2287 
2288 		// SAMPLES
2289 
2290 		clearRect(424, 99,  15, 54); // src sample
2291 		clearRect(446, 99, 115, 54); // main sample
2292 
2293 		// draw source sample selection
2294 		if (editor.srcSmp >= editor.sampleBankOffset && editor.srcSmp <= editor.sampleBankOffset+4)
2295 		{
2296 			y = 99 + ((editor.srcSmp - editor.sampleBankOffset) * 11);
2297 			if (y >= 36 && y <= 143)
2298 				fillRect(424, y, 15, 10, PAL_BUTTONS);
2299 		}
2300 
2301 		// draw destination sample selection
2302 		if (editor.curSmp >= editor.sampleBankOffset && editor.curSmp <= editor.sampleBankOffset+4)
2303 		{
2304 			y = 99 + ((editor.curSmp - editor.sampleBankOffset) * 11);
2305 			if (y >= 36 && y <= 143)
2306 				fillRect(446, y, 115, 10, PAL_BUTTONS);
2307 		}
2308 
2309 		// draw numbers and texts
2310 		for (int16_t i = 0; i < 5; i++)
2311 		{
2312 			hexOut(424, 99 + (i * 11), PAL_FORGRND, editor.sampleBankOffset + i, 2);
2313 			drawTextBox(TB_SAMP1 + i);
2314 		}
2315 	}
2316 }
2317 
showInstrumentSwitcher(void)2318 void showInstrumentSwitcher(void)
2319 {
2320 	if (!ui.instrSwitcherShown)
2321 		return;
2322 
2323 	for (uint16_t i = 0; i < 8; i++)
2324 		showTextBox(TB_INST1 + i);
2325 
2326 	if (ui.extended)
2327 	{
2328 		hidePushButton(PB_SAMPLE_LIST_UP);
2329 		hidePushButton(PB_SAMPLE_LIST_DOWN);
2330 		hideScrollBar(SB_SAMPLE_LIST);
2331 
2332 		drawFramework(386,  0, 246,   3, FRAMEWORK_TYPE1);
2333 		drawFramework(506,  3,   3,  47, FRAMEWORK_TYPE1);
2334 		drawFramework(386, 50, 246,   3, FRAMEWORK_TYPE1);
2335 		drawFramework(629,  3,   3,  47, FRAMEWORK_TYPE1);
2336 
2337 		clearRect(386, 3, 120, 47);
2338 		clearRect(509, 3, 120, 47);
2339 	}
2340 	else
2341 	{
2342 		drawFramework(421,   0, 166,   3, FRAMEWORK_TYPE1);
2343 		drawFramework(442,   3,   3,  91, FRAMEWORK_TYPE1);
2344 		drawFramework(421,  94, 166,   3, FRAMEWORK_TYPE1);
2345 		drawFramework(442,  97,   3,  58, FRAMEWORK_TYPE1);
2346 		drawFramework(563,  97,  24,  58, FRAMEWORK_TYPE1);
2347 		drawFramework(587,   0,  45,  71, FRAMEWORK_TYPE1);
2348 		drawFramework(587,  71,  45,  71, FRAMEWORK_TYPE1);
2349 		drawFramework(587, 142,  45,  31, FRAMEWORK_TYPE1);
2350 
2351 		fillRect(421,  3,  21, 91, PAL_BCKGRND);
2352 		fillRect(445,  3, 142, 91, PAL_BCKGRND);
2353 		fillRect(421, 97,  21, 58, PAL_BCKGRND);
2354 		fillRect(445, 97, 118, 58, PAL_BCKGRND);
2355 
2356 		showPushButton(PB_SAMPLE_LIST_UP);
2357 		showPushButton(PB_SAMPLE_LIST_DOWN);
2358 		showScrollBar(SB_SAMPLE_LIST);
2359 
2360 		for (uint16_t i = 0; i < 5; i++)
2361 			showTextBox(TB_SAMP1 + i);
2362 	}
2363 
2364 	updateInstrumentSwitcher();
2365 
2366 	for (uint16_t i = 0; i < 8; i++)
2367 		showPushButton(PB_RANGE1 + i + (editor.instrBankSwapped * 8));
2368 
2369 	showPushButton(PB_SWAP_BANK);
2370 }
2371 
hideInstrumentSwitcher(void)2372 void hideInstrumentSwitcher(void)
2373 {
2374 	for (uint16_t i = 0; i < 16; i++)
2375 		hidePushButton(PB_RANGE1 + i);
2376 
2377 	hidePushButton(PB_SWAP_BANK);
2378 	hidePushButton(PB_SAMPLE_LIST_UP);
2379 	hidePushButton(PB_SAMPLE_LIST_DOWN);
2380 	hideScrollBar(SB_SAMPLE_LIST);
2381 
2382 	for (uint16_t i = 0; i < 8; i++)
2383 		hideTextBox(TB_INST1 + i);
2384 
2385 	for (uint16_t i = 0; i < 5; i++)
2386 		hideTextBox(TB_SAMP1 + i);
2387 }
2388 
pbSwapInstrBank(void)2389 void pbSwapInstrBank(void)
2390 {
2391 	editor.instrBankSwapped ^= 1;
2392 
2393 	if (editor.instrBankSwapped)
2394 		editor.instrBankOffset += 8*8;
2395 	else
2396 		editor.instrBankOffset -= 8*8;
2397 
2398 	updateTextBoxPointers();
2399 
2400 	if (ui.instrSwitcherShown)
2401 	{
2402 		updateInstrumentSwitcher();
2403 		for (uint16_t i = 0; i < 8; i++)
2404 		{
2405 			hidePushButton(PB_RANGE1 + i + (!editor.instrBankSwapped * 8));
2406 			showPushButton(PB_RANGE1 + i + ( editor.instrBankSwapped * 8));
2407 		}
2408 	}
2409 }
2410 
pbSetInstrBank1(void)2411 void pbSetInstrBank1(void)
2412 {
2413 	editor.instrBankOffset = 0 * 8;
2414 
2415 	updateTextBoxPointers();
2416 	updateInstrumentSwitcher();
2417 }
2418 
pbSetInstrBank2(void)2419 void pbSetInstrBank2(void)
2420 {
2421 	editor.instrBankOffset = 1 * 8;
2422 
2423 	updateTextBoxPointers();
2424 	updateInstrumentSwitcher();
2425 }
2426 
pbSetInstrBank3(void)2427 void pbSetInstrBank3(void)
2428 {
2429 	editor.instrBankOffset = 2 * 8;
2430 
2431 	updateTextBoxPointers();
2432 	updateInstrumentSwitcher();
2433 }
2434 
pbSetInstrBank4(void)2435 void pbSetInstrBank4(void)
2436 {
2437 	editor.instrBankOffset = 3 * 8;
2438 
2439 	updateTextBoxPointers();
2440 	updateInstrumentSwitcher();
2441 }
2442 
pbSetInstrBank5(void)2443 void pbSetInstrBank5(void)
2444 {
2445 	editor.instrBankOffset = 4 * 8;
2446 
2447 	updateTextBoxPointers();
2448 	updateInstrumentSwitcher();
2449 }
2450 
pbSetInstrBank6(void)2451 void pbSetInstrBank6(void)
2452 {
2453 	editor.instrBankOffset = 5 * 8;
2454 
2455 	updateTextBoxPointers();
2456 	updateInstrumentSwitcher();
2457 }
2458 
pbSetInstrBank7(void)2459 void pbSetInstrBank7(void)
2460 {
2461 	editor.instrBankOffset = 6 * 8;
2462 
2463 	updateTextBoxPointers();
2464 	updateInstrumentSwitcher();
2465 }
2466 
pbSetInstrBank8(void)2467 void pbSetInstrBank8(void)
2468 {
2469 	editor.instrBankOffset = 7 * 8;
2470 
2471 	updateTextBoxPointers();
2472 	updateInstrumentSwitcher();
2473 }
2474 
pbSetInstrBank9(void)2475 void pbSetInstrBank9(void)
2476 {
2477 	editor.instrBankOffset = 8 * 8;
2478 
2479 	updateTextBoxPointers();
2480 	updateInstrumentSwitcher();
2481 }
2482 
pbSetInstrBank10(void)2483 void pbSetInstrBank10(void)
2484 {
2485 	editor.instrBankOffset = 9 * 8;
2486 
2487 	updateTextBoxPointers();
2488 	updateInstrumentSwitcher();
2489 }
2490 
pbSetInstrBank11(void)2491 void pbSetInstrBank11(void)
2492 {
2493 	editor.instrBankOffset = 10 * 8;
2494 
2495 	updateTextBoxPointers();
2496 	updateInstrumentSwitcher();
2497 }
2498 
pbSetInstrBank12(void)2499 void pbSetInstrBank12(void)
2500 {
2501 	editor.instrBankOffset = 11 * 8;
2502 
2503 	updateTextBoxPointers();
2504 	updateInstrumentSwitcher();
2505 }
2506 
pbSetInstrBank13(void)2507 void pbSetInstrBank13(void)
2508 {
2509 	editor.instrBankOffset = 12 * 8;
2510 
2511 	updateTextBoxPointers();
2512 	updateInstrumentSwitcher();
2513 }
2514 
pbSetInstrBank14(void)2515 void pbSetInstrBank14(void)
2516 {
2517 	editor.instrBankOffset = 13 * 8;
2518 
2519 	updateTextBoxPointers();
2520 	updateInstrumentSwitcher();
2521 }
2522 
pbSetInstrBank15(void)2523 void pbSetInstrBank15(void)
2524 {
2525 	editor.instrBankOffset = 14 * 8;
2526 
2527 	updateTextBoxPointers();
2528 	updateInstrumentSwitcher();
2529 }
2530 
pbSetInstrBank16(void)2531 void pbSetInstrBank16(void)
2532 {
2533 	editor.instrBankOffset = 15 * 8;
2534 
2535 	updateTextBoxPointers();
2536 	updateInstrumentSwitcher();
2537 }
2538 
setNewInstr(int16_t ins)2539 void setNewInstr(int16_t ins)
2540 {
2541 	if (ins <= MAX_INST)
2542 	{
2543 		editor.curInstr = (uint8_t)ins;
2544 		updateTextBoxPointers();
2545 		updateInstrumentSwitcher();
2546 		updateNewInstrument();
2547 	}
2548 }
2549 
sampleListScrollUp(void)2550 void sampleListScrollUp(void)
2551 {
2552 	scrollBarScrollUp(SB_SAMPLE_LIST, 1);
2553 }
2554 
sampleListScrollDown(void)2555 void sampleListScrollDown(void)
2556 {
2557 	scrollBarScrollDown(SB_SAMPLE_LIST, 1);
2558 }
2559 
zapSong(void)2560 static void zapSong(void)
2561 {
2562 	lockMixerCallback();
2563 
2564 	song.songLength = 1;
2565 	song.songLoopStart = 0; // FT2 doesn't do this!
2566 	song.BPM = 125;
2567 	song.speed = 6;
2568 	song.songPos = 0;
2569 	song.globalVolume = 64;
2570 
2571 	memset(song.name, 0, sizeof (song.name));
2572 	memset(song.orders, 0, sizeof (song.orders));
2573 
2574 	// zero all pattern data and reset pattern lengths
2575 
2576 	freeAllPatterns();
2577 	for (int32_t i = 0; i < MAX_PATTERNS; i++)
2578 		patternNumRows[i] = 64;
2579 	song.currNumRows = patternNumRows[song.pattNum];
2580 
2581 	resetMusic();
2582 	setMixerBPM(song.BPM);
2583 
2584 	editor.songPos = song.songPos;
2585 	editor.editPattern = song.pattNum;
2586 	editor.BPM = song.BPM;
2587 	editor.speed = song.speed;
2588 	editor.globalVolume = song.globalVolume;
2589 	editor.tick = 1;
2590 
2591 	resetPlaybackTime();
2592 
2593 	if (!audio.linearPeriodsFlag)
2594 		setFrequencyTable(true);
2595 
2596 	clearPattMark();
2597 	resetWavRenderer();
2598 	resetChannels();
2599 	unlockMixerCallback();
2600 
2601 	setScrollBarPos(SB_POS_ED, 0, false);
2602 	setScrollBarEnd(SB_POS_ED, (song.songLength - 1) + 5);
2603 
2604 	updateWindowTitle(true);
2605 }
2606 
zapInstrs(void)2607 static void zapInstrs(void)
2608 {
2609 	lockMixerCallback();
2610 
2611 	for (int16_t i = 1; i <= MAX_INST; i++)
2612 	{
2613 		freeInstr(i);
2614 		memset(song.instrName[i], 0, 22+1);
2615 	}
2616 
2617 	updateNewInstrument();
2618 
2619 	editor.currVolEnvPoint = 0;
2620 	editor.currPanEnvPoint = 0;
2621 
2622 	updateSampleEditorSample();
2623 
2624 	if (ui.sampleEditorShown)
2625 		updateSampleEditor();
2626 	else if (ui.instEditorShown || ui.instEditorExtShown)
2627 		updateInstEditor();
2628 
2629 	unlockMixerCallback();
2630 }
2631 
pbZap(void)2632 void pbZap(void)
2633 {
2634 	const int16_t choice = okBox(4, "System request", "Total devastation of the...");
2635 
2636 	if (choice == 1) // zap all
2637 	{
2638 		zapSong();
2639 		zapInstrs();
2640 	}
2641 	else if (choice == 2) // zap song
2642 	{
2643 		zapSong();
2644 	}
2645 	else if (choice == 3) // zap instruments
2646 	{
2647 		zapInstrs();
2648 	}
2649 
2650 	if (choice >= 1 && choice <= 3)
2651 	{
2652 		// redraw top screens
2653 		hideTopScreen();
2654 		showTopScreen(true);
2655 
2656 		setSongModifiedFlag();
2657 	}
2658 }
2659 
sbSmpBankPos(uint32_t pos)2660 void sbSmpBankPos(uint32_t pos)
2661 {
2662 	if (editor.sampleBankOffset != pos)
2663 	{
2664 		editor.sampleBankOffset = (uint8_t)pos;
2665 
2666 		updateTextBoxPointers();
2667 		updateInstrumentSwitcher();
2668 	}
2669 }
2670 
pbToggleLogo(void)2671 void pbToggleLogo(void)
2672 {
2673 	config.id_FastLogo ^= 1;
2674 	changeLogoType(config.id_FastLogo);
2675 }
2676 
pbToggleBadge(void)2677 void pbToggleBadge(void)
2678 {
2679 	config.id_TritonProd ^= 1;
2680 	changeBadgeType(config.id_TritonProd);
2681 }
2682 
resetChannelOffset(void)2683 void resetChannelOffset(void)
2684 {
2685 	ui.pattChanScrollShown = song.numChannels > getMaxVisibleChannels();
2686 	cursor.object = CURSOR_NOTE;
2687 	cursor.ch = 0;
2688 	setScrollBarPos(SB_CHAN_SCROLL, 0, true);
2689 	ui.channelOffset = 0;
2690 }
2691 
shrinkPattern(void)2692 void shrinkPattern(void)
2693 {
2694 	if (okBox(2, "System request", "Shrink pattern?") != 1)
2695 		return;
2696 
2697 	int16_t numRows = patternNumRows[editor.editPattern];
2698 	if (numRows <= 1)
2699 		return;
2700 
2701 	lockMixerCallback();
2702 
2703 	note_t *p = pattern[editor.editPattern];
2704 	if (p != NULL)
2705 	{
2706 		const int32_t length = numRows >> 1;
2707 		for (int32_t i = 0; i < length; i++)
2708 		{
2709 			for (int32_t j = 0; j < MAX_CHANNELS; j++)
2710 				p[(i * MAX_CHANNELS) + j] = p[((i*2) * MAX_CHANNELS) + j];
2711 		}
2712 	}
2713 
2714 	patternNumRows[editor.editPattern] >>= 1;
2715 	numRows = patternNumRows[editor.editPattern];
2716 
2717 	if (song.pattNum == editor.editPattern)
2718 		song.currNumRows = numRows;
2719 
2720 	song.row >>= 1;
2721 	if (song.row >= numRows)
2722 		song.row = numRows-1;
2723 
2724 	editor.row = song.row;
2725 
2726 	ui.updatePatternEditor = true;
2727 	ui.updatePosSections = true;
2728 
2729 	unlockMixerCallback();
2730 	setSongModifiedFlag();
2731 }
2732 
expandPattern(void)2733 void expandPattern(void)
2734 {
2735 	int16_t numRows = patternNumRows[editor.editPattern];
2736 	if (numRows > 128)
2737 	{
2738 		okBox(0, "System message", "Pattern is too long to be expanded.");
2739 	}
2740 	else
2741 	{
2742 		lockMixerCallback();
2743 
2744 		if (pattern[editor.editPattern] != NULL)
2745 		{
2746 			note_t *tmpPtn = (note_t *)malloc((numRows * 2) * TRACK_WIDTH);
2747 			if (tmpPtn == NULL)
2748 			{
2749 				unlockMixerCallback();
2750 				okBox(0, "System message", "Not enough memory!");
2751 				return;
2752 			}
2753 
2754 			for (int32_t i = 0; i < numRows; i++)
2755 			{
2756 				for (int32_t j = 0; j < MAX_CHANNELS; j++)
2757 					tmpPtn[((i * 2) * MAX_CHANNELS) + j] = pattern[editor.editPattern][(i * MAX_CHANNELS) + j];
2758 
2759 				memset(&tmpPtn[((i * 2) + 1) * MAX_CHANNELS], 0, TRACK_WIDTH);
2760 			}
2761 
2762 			free(pattern[editor.editPattern]);
2763 			pattern[editor.editPattern] = tmpPtn;
2764 		}
2765 
2766 		patternNumRows[editor.editPattern] *= 2;
2767 		numRows = patternNumRows[editor.editPattern];
2768 
2769 		if (song.pattNum == editor.editPattern)
2770 			song.currNumRows = numRows;
2771 
2772 		song.row *= 2;
2773 		if (song.row >= numRows)
2774 			song.row = numRows-1;
2775 
2776 		editor.row = song.row;
2777 
2778 		ui.updatePatternEditor = true;
2779 		ui.updatePosSections = true;
2780 
2781 		unlockMixerCallback();
2782 		setSongModifiedFlag();
2783 	}
2784 }
2785