ft2_help.c (9938B)
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 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 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 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 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 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 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!", NULL); 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!", NULL); 255 break; 256 } 257 258 memcpy(subjPtrArr[subj], tempText, HELP_SIZE * textLine); 259 subjLen[subj] = textLine; 260 } 261 262 free(tempText); 263 } 264 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 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 345 void helpScrollUp(void) 346 { 347 if (fHlp_Line > 0) 348 { 349 scrollBarScrollUp(SB_HELP_SCROLL, 1); 350 writeHelp(); 351 } 352 } 353 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 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 372 void showHelpScreen(void) 373 { 374 uint16_t tmpID; 375 376 if (ui.extendedPatternEditor) 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 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 430 void exitHelpScreen(void) 431 { 432 hideHelpScreen(); 433 showTopScreen(true); 434 } 435 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 445 void rbHelpFeatures(void) 446 { 447 checkRadioButton(RB_HELP_FEATURES); 448 setHelpSubject(0); 449 writeHelp(); 450 } 451 452 void rbHelpEffects(void) 453 { 454 checkRadioButton(RB_HELP_EFFECTS); 455 setHelpSubject(1); 456 writeHelp(); 457 } 458 459 void rbHelpKeybindings(void) 460 { 461 checkRadioButton(RB_HELP_KEYBINDINGS); 462 setHelpSubject(2); 463 writeHelp(); 464 } 465 466 void rbHelpHowToUseFT2(void) 467 { 468 checkRadioButton(RB_HELP_HOW_TO_USE_FT2); 469 setHelpSubject(3); 470 writeHelp(); 471 } 472 473 void rbHelpFAQ(void) 474 { 475 checkRadioButton(RB_HELP_FAQ); 476 setHelpSubject(4); 477 writeHelp(); 478 } 479 480 void rbHelpKnownBugs(void) 481 { 482 checkRadioButton(RB_HELP_KNOWN_BUGS); 483 setHelpSubject(5); 484 writeHelp(); 485 } 486 487 void initFTHelp(void) 488 { 489 readHelp(); 490 setHelpSubject(0); 491 } 492 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 }