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 <stdio.h>
8 #include "ft2_header.h"
9 #include "ft2_gui.h"
10 #include "ft2_help.h"
11 #include "ft2_video.h"
12 #include "ft2_pattern_ed.h"
13 #include "ft2_bmp.h"
14 #include "ft2_structs.h"
15 #include "helpdata/ft2_help_data.h"
16 
17 typedef struct
18 {
19 	bool bigFont, noLine;
20 	uint8_t color;
21 	int16_t xPos;
22 	char text[100];
23 } helpRec;
24 
25 #define HELP_LINES 15
26 #define MAX_HELP_LINES 768
27 #define HELP_SIZE sizeof (helpRec)
28 #define MAX_SUBJ 10
29 #define HELP_COLUMN 135
30 #define HELP_WIDTH (596 - HELP_COLUMN)
31 
32 static uint8_t fHlp_Num;
33 static int16_t textLine, fHlp_Line, subjLen[MAX_SUBJ];
34 static int32_t helpBufferPos;
35 static helpRec *subjPtrArr[MAX_SUBJ];
36 
addText(helpRec * t,int16_t xPos,uint8_t color,char * text)37 static void addText(helpRec *t, int16_t xPos, uint8_t color, char *text)
38 {
39 	if (*text == '\0')
40 		return;
41 
42 	t->xPos = xPos;
43 	t->color = color;
44 	t->bigFont = false;
45 	t->noLine = false;
46 	strcpy(t->text, text);
47 	*text = '\0'; // empty old string
48 
49 	textLine++;
50 }
51 
getLine(char * output)52 static bool getLine(char *output)
53 {
54 	if (helpBufferPos >= (int32_t)sizeof (helpData))
55 	{
56 		*output = '\0';
57 		return false;
58 	}
59 
60 	const uint8_t strLen = helpData[helpBufferPos++];
61 	memcpy(output, &helpData[helpBufferPos], strLen);
62 	output[strLen] = '\0';
63 
64 	helpBufferPos += strLen;
65 
66 	return true;
67 }
68 
controlCodeToNum(const char * controlCode)69 static int16_t controlCodeToNum(const char *controlCode)
70 {
71 	return (((controlCode[0]-'0')%10)*100) + (((controlCode[1]-'0')%10)*10) + ((controlCode[2]-'0')%10);
72 }
73 
ltrim(char * s)74 static char *ltrim(char *s)
75 {
76 	if (*s == '\0')
77 		return (s);
78 
79 	while (*s == ' ') s++;
80 
81 	return s;
82 }
83 
rtrim(char * s)84 static char *rtrim(char *s)
85 {
86 	if (*s == '\0')
87 		return (s);
88 
89 	int32_t i = (int32_t)strlen(s) - 1;
90 	while (i >= 0)
91 	{
92 		if (s[i] != ' ')
93 		{
94 			s[i+1] = '\0';
95 			break;
96 		}
97 
98 		i--;
99 	}
100 
101 	return s;
102 }
103 
readHelp(void)104 static void readHelp(void) // this is a bit messy...
105 {
106 	char text[256], text2[256], *s, *sEnd, *s3;
107 	int16_t a, b, i, k;
108 
109 	helpRec *tempText = (helpRec *)malloc(HELP_SIZE * MAX_HELP_LINES);
110 	if (tempText == NULL)
111 	{
112 		okBox(0, "System message", "Not enough memory!");
113 		return;
114 	}
115 
116 	text[0] = '\0';
117 	text2[0] = '\0';
118 
119 	char *s2 = text2;
120 
121 	helpBufferPos = 0;
122 	for (int16_t subj = 0; subj < MAX_SUBJ; subj++)
123 	{
124 		textLine = 0;
125 		int16_t currColumn = 0;
126 		uint8_t currColor = PAL_FORGRND;
127 
128 		getLine(text); s = text;
129 		while (strncmp(s, "END", 3) != 0)
130 		{
131 			if (*s == ';')
132 			{
133 				if (!getLine(text))
134 					break;
135 
136 				s = text;
137 				continue;
138 			}
139 
140 			if (*(uint16_t *)s == 0x4C40) // @L - "big font"
141 			{
142 				addText(&tempText[textLine], currColumn, currColor, s2);
143 				s += 2;
144 
145 				if (*(uint16_t *)s == 0x5840) // @X - "change X position"
146 				{
147 					currColumn = controlCodeToNum(&s[2]);
148 					s += 5;
149 				}
150 
151 				if (*(uint16_t *)s == 0x4340) // @C - "change color
152 				{
153 					currColor = (uint8_t)controlCodeToNum(&s[2]);
154 					currColor = (currColor < 2) ? PAL_FORGRND : PAL_BUTTONS;
155 					s += 5;
156 				}
157 
158 				helpRec *t = &tempText[textLine];
159 				t->xPos = currColumn;
160 				t->color = currColor;
161 				t->bigFont = true;
162 				t->noLine = false;
163 				strcpy(t->text, s);
164 				textLine++;
165 
166 				t = &tempText[textLine];
167 				t->noLine = true;
168 				textLine++;
169 			}
170 			else
171 			{
172 				if (*s == '>')
173 				{
174 					addText(&tempText[textLine], currColumn, currColor, s2);
175 					s++;
176 				}
177 
178 				if (*(uint16_t *)s == 0x5840) // @X - "set X position (relative to help X start)"
179 				{
180 					currColumn = controlCodeToNum(&s[2]);
181 					s += 5;
182 				}
183 
184 				if (*(uint16_t *)s == 0x4340) // @C - "change color"
185 				{
186 					currColor = (uint8_t)controlCodeToNum(&s[2]);
187 					currColor = (currColor < 2) ? PAL_FORGRND : PAL_BUTTONS;
188 					s += 5;
189 				}
190 
191 				s = ltrim(rtrim(s));
192 				if (*s == '\0')
193 				{
194 					addText(&tempText[textLine], currColumn, currColor, s2);
195 					strcpy(s2, " ");
196 					addText(&tempText[textLine], currColumn, currColor, s2);
197 				}
198 
199 				int16_t sLen = (int16_t)strlen(s);
200 
201 				sEnd = &s[sLen];
202 				while (s < sEnd)
203 				{
204 					if (sLen < 0)
205 						sLen = 0;
206 
207 					i = 0;
208 					while (s[i] != ' ' && i < sLen) i++;
209 					i++;
210 
211 					if (*(uint16_t *)s == 0x5440) // @T - "set absolute X position (in the middle of text)"
212 					{
213 						k = controlCodeToNum(&s[2]);
214 						s += 5; sLen -= 5;
215 
216 						s3 = &s2[strlen(s2)];
217 						while (textWidth(s2) + charWidth(' ') + 1 < k-currColumn)
218 						{
219 							s3[0] = ' ';
220 							s3[1] = '\0';
221 							s3++;
222 						}
223 
224 						b = textWidth(s2) + 1;
225 						if (b < k-currColumn)
226 						{
227 							s3 = &s2[strlen(s2)];
228 							for (a = 0; a < k-b-currColumn; a++)
229 								s3[a] = 127; // one-pixel spacer glyph
230 							s3[a] = '\0';
231 						}
232 					}
233 
234 					if (textWidth(s2)+textNWidth(s,i)+2 > HELP_WIDTH-currColumn)
235 						addText(&tempText[textLine], currColumn, currColor, s2);
236 
237 					strncat(s2, s, i);
238 
239 					s += i; sLen -= i;
240 					if ((*s == '\0') || (s >= sEnd))
241 						strcat(s2, " ");
242 				}
243 			}
244 
245 			if (textLine >= MAX_HELP_LINES || !getLine(text))
246 				break;
247 
248 			s = text;
249 		}
250 
251 		subjPtrArr[subj] = (helpRec *)malloc(HELP_SIZE * textLine);
252 		if (subjPtrArr[subj] == NULL)
253 		{
254 			okBox(0, "System message", "Not enough memory!");
255 			break;
256 		}
257 
258 		memcpy(subjPtrArr[subj], tempText, HELP_SIZE * textLine);
259 		subjLen[subj] = textLine;
260 	}
261 
262 	free(tempText);
263 }
264 
bigTextOutHalf(uint16_t xPos,uint16_t yPos,uint8_t paletteIndex,bool lowerHalf,const char * textPtr)265 static void bigTextOutHalf(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, bool lowerHalf, const char *textPtr)
266 {
267 	assert(textPtr != NULL);
268 
269 	uint16_t currX = xPos;
270 	while (true)
271 	{
272 		const char chr = *textPtr++ & 0x7F;
273 		if (chr == '\0')
274 			break;
275 
276 		if (chr != ' ')
277 		{
278 			const uint8_t *srcPtr = &bmp.font2[chr * FONT2_CHAR_W];
279 			if (!lowerHalf)
280 				srcPtr += (FONT2_CHAR_H / 2) * FONT2_WIDTH;
281 
282 			uint32_t *dstPtr = &video.frameBuffer[(yPos * SCREEN_W) + currX];
283 			const uint32_t pixVal = video.palette[paletteIndex];
284 
285 			for (uint32_t y = 0; y < FONT2_CHAR_H/2; y++)
286 			{
287 				for (uint32_t x = 0; x < FONT2_CHAR_W; x++)
288 				{
289 					if (srcPtr[x])
290 						dstPtr[x] = pixVal;
291 				}
292 
293 				srcPtr += FONT2_WIDTH;
294 				dstPtr += SCREEN_W;
295 			}
296 		}
297 
298 		currX += charWidth16(chr);
299 	}
300 }
301 
writeHelp(void)302 static void writeHelp(void)
303 {
304 	helpRec *ptr = subjPtrArr[fHlp_Num];
305 	if (ptr == NULL)
306 		return;
307 
308 	for (int16_t i = 0; i < HELP_LINES; i++)
309 	{
310 		const int16_t k = i + fHlp_Line;
311 		if (k >= subjLen[fHlp_Num])
312 			break;
313 
314 		clearRect(HELP_COLUMN, 5 + (i * 11), HELP_WIDTH, 11);
315 
316 		if (ptr[k].noLine)
317 		{
318 			if (i == 0)
319 				bigTextOutHalf(HELP_COLUMN + ptr[k-1].xPos, 5 + (i * 11), PAL_FORGRND, false, ptr[k-1].text);
320 		}
321 		else
322 		{
323 			if (ptr[k].bigFont)
324 			{
325 				if (i == HELP_LINES-1)
326 				{
327 					bigTextOutHalf(HELP_COLUMN + ptr[k].xPos, 5 + (i * 11), PAL_FORGRND, true, ptr[k].text);
328 					return;
329 				}
330 				else
331 				{
332 					clearRect(HELP_COLUMN, 5 + ((i + 1) * 11), HELP_WIDTH, 11);
333 					bigTextOut(HELP_COLUMN + ptr[k].xPos, 5 + (i * 11), PAL_FORGRND, ptr[k].text);
334 					i++;
335 				}
336 			}
337 			else
338 			{
339 				textOut(HELP_COLUMN + ptr[k].xPos, 5 + (i * 11), ptr[k].color, ptr[k].text);
340 			}
341 		}
342 	}
343 }
344 
helpScrollUp(void)345 void helpScrollUp(void)
346 {
347 	if (fHlp_Line > 0)
348 	{
349 		scrollBarScrollUp(SB_HELP_SCROLL, 1);
350 		writeHelp();
351 	}
352 }
353 
helpScrollDown(void)354 void helpScrollDown(void)
355 {
356 	if (fHlp_Line < subjLen[fHlp_Num]-1)
357 	{
358 		scrollBarScrollDown(SB_HELP_SCROLL, 1);
359 		writeHelp();
360 	}
361 }
362 
helpScrollSetPos(uint32_t pos)363 void helpScrollSetPos(uint32_t pos)
364 {
365 	if (fHlp_Line != (int16_t)pos)
366 	{
367 		fHlp_Line = (int16_t)pos;
368 		writeHelp();
369 	}
370 }
371 
showHelpScreen(void)372 void showHelpScreen(void)
373 {
374 	uint16_t tmpID;
375 
376 	if (ui.extended)
377 		exitPatternEditorExtended();
378 
379 	hideTopScreen();
380 	ui.helpScreenShown = true;
381 
382 	drawFramework(0,   0, 128, 173, FRAMEWORK_TYPE1);
383 	drawFramework(128, 0, 504, 173, FRAMEWORK_TYPE1);
384 	drawFramework(130, 2, 479, 169, FRAMEWORK_TYPE2);
385 
386 	showPushButton(PB_HELP_EXIT);
387 	showPushButton(PB_HELP_SCROLL_UP);
388 	showPushButton(PB_HELP_SCROLL_DOWN);
389 
390 	uncheckRadioButtonGroup(RB_GROUP_HELP);
391 	switch (fHlp_Num)
392 	{
393 		default:
394 		case 0: tmpID = RB_HELP_FEATURES;       break;
395 		case 1: tmpID = RB_HELP_EFFECTS;        break;
396 		case 2: tmpID = RB_HELP_KEYBINDINGS;       break;
397 		case 3: tmpID = RB_HELP_HOW_TO_USE_FT2; break;
398 		case 4: tmpID = RB_HELP_FAQ;            break;
399 		case 5: tmpID = RB_HELP_KNOWN_BUGS;     break;
400 	}
401 	radioButtons[tmpID].state = RADIOBUTTON_CHECKED;
402 
403 	showRadioButtonGroup(RB_GROUP_HELP);
404 
405 	showScrollBar(SB_HELP_SCROLL);
406 
407 	textOutShadow(4,   4, PAL_FORGRND, PAL_DSKTOP2, "Subjects:");
408 	textOutShadow(21, 19, PAL_FORGRND, PAL_DSKTOP2, "Features");
409 	textOutShadow(21, 35, PAL_FORGRND, PAL_DSKTOP2, "Effects");
410 	textOutShadow(21, 51, PAL_FORGRND, PAL_DSKTOP2, "Keybindings");
411 	textOutShadow(21, 67, PAL_FORGRND, PAL_DSKTOP2, "How to use FT2");
412 	textOutShadow(21, 83, PAL_FORGRND, PAL_DSKTOP2, "Problems/FAQ");
413 	textOutShadow(21, 99, PAL_FORGRND, PAL_DSKTOP2, "Known bugs");
414 
415 	writeHelp();
416 }
417 
hideHelpScreen(void)418 void hideHelpScreen(void)
419 {
420 	hidePushButton(PB_HELP_EXIT);
421 	hidePushButton(PB_HELP_SCROLL_UP);
422 	hidePushButton(PB_HELP_SCROLL_DOWN);
423 
424 	hideRadioButtonGroup(RB_GROUP_HELP);
425 	hideScrollBar(SB_HELP_SCROLL);
426 
427 	ui.helpScreenShown = false;
428 }
429 
exitHelpScreen(void)430 void exitHelpScreen(void)
431 {
432 	hideHelpScreen();
433 	showTopScreen(true);
434 }
435 
setHelpSubject(uint8_t Nr)436 static void setHelpSubject(uint8_t Nr)
437 {
438 	fHlp_Num = Nr;
439 	fHlp_Line = 0;
440 
441 	setScrollBarEnd(SB_HELP_SCROLL, subjLen[fHlp_Num]);
442 	setScrollBarPos(SB_HELP_SCROLL, 0, false);
443 }
444 
rbHelpFeatures(void)445 void rbHelpFeatures(void)
446 {
447 	checkRadioButton(RB_HELP_FEATURES);
448 	setHelpSubject(0);
449 	writeHelp();
450 }
451 
rbHelpEffects(void)452 void rbHelpEffects(void)
453 {
454 	checkRadioButton(RB_HELP_EFFECTS);
455 	setHelpSubject(1);
456 	writeHelp();
457 }
458 
rbHelpKeybindings(void)459 void rbHelpKeybindings(void)
460 {
461 	checkRadioButton(RB_HELP_KEYBINDINGS);
462 	setHelpSubject(2);
463 	writeHelp();
464 }
465 
rbHelpHowToUseFT2(void)466 void rbHelpHowToUseFT2(void)
467 {
468 	checkRadioButton(RB_HELP_HOW_TO_USE_FT2);
469 	setHelpSubject(3);
470 	writeHelp();
471 }
472 
rbHelpFAQ(void)473 void rbHelpFAQ(void)
474 {
475 	checkRadioButton(RB_HELP_FAQ);
476 	setHelpSubject(4);
477 	writeHelp();
478 }
479 
rbHelpKnownBugs(void)480 void rbHelpKnownBugs(void)
481 {
482 	checkRadioButton(RB_HELP_KNOWN_BUGS);
483 	setHelpSubject(5);
484 	writeHelp();
485 }
486 
initFTHelp(void)487 void initFTHelp(void)
488 {
489 	readHelp();
490 	setHelpSubject(0);
491 }
492 
windUpFTHelp(void)493 void windUpFTHelp(void)
494 {
495 	for (int16_t i = 0; i < MAX_SUBJ; i++)
496 	{
497 		if (subjPtrArr[i] != NULL)
498 		{
499 			free(subjPtrArr[i]);
500 			subjPtrArr[i] = NULL;
501 		}
502 	}
503 }
504