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