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 <stdlib.h>
8 #include <stdint.h>
9 #include <stdbool.h>
10 #include <string.h>
11 #include <ctype.h>
12 #ifndef _WIN32
13 #include <unistd.h>
14 #include <limits.h>
15 #endif
16 #include "pt2_header.h"
17 #include "pt2_helpers.h"
18 #include "pt2_config.h"
19 #include "pt2_tables.h"
20 #include "pt2_audio.h"
21 #include "pt2_diskop.h"
22 #include "pt2_textout.h"
23 #include "pt2_sampler.h"
24
25 #ifndef _WIN32
26 static char oldCwd[PATH_MAX];
27 #endif
28
29 config_t config; // globalized
30
31 static bool loadProTrackerDotIni(FILE *f);
32 static FILE *openPTDotConfig(void);
33 static bool loadPTDotConfig(FILE *f);
34 static bool loadColorsDotIni(void);
35
loadConfig(void)36 void loadConfig(void)
37 {
38 bool proTrackerDotIniFound, ptDotConfigFound;
39 #ifndef _WIN32
40 bool colorsDotIniFound;
41 #endif
42 FILE *f;
43
44 // set default config values first
45 config.noDownsampleOnSmpLoad = false;
46 config.disableE8xEffect = false;
47 config.fullScreenStretch = false;
48 config.pattDots = false;
49 config.waveformCenterLine = true;
50 config.filterModel = FILTERMODEL_A1200;
51 config.soundFrequency = 48000;
52 config.rememberPlayMode = false;
53 config.stereoSeparation = 20;
54 config.videoScaleFactor = 2;
55 config.realVuMeters = false;
56 config.modDot = false;
57 config.accidental = 0; // sharp
58 config.quantizeValue = 1;
59 config.transDel = false;
60 config.blankZeroFlag = false;
61 config.compoMode = false;
62 config.soundBufferSize = 1024;
63 config.autoCloseDiskOp = true;
64 config.vsyncOff = false;
65 config.hwMouse = true;
66 config.startInFullscreen = false;
67 config.pixelFilter = PIXELFILTER_NEAREST;
68 config.integerScaling = true;
69 config.audioInputFrequency = 44100;
70
71 #ifndef _WIN32
72 getcwd(oldCwd, PATH_MAX);
73 #endif
74
75 // load protracker.ini
76 proTrackerDotIniFound = false;
77
78 #ifdef _WIN32
79 f = fopen("protracker.ini", "r");
80 if (f != NULL)
81 proTrackerDotIniFound = true;
82 #else
83 // check in program directory
84 f = fopen("protracker.ini", "r");
85 if (f != NULL)
86 proTrackerDotIniFound = true;
87
88 // check in ~/.protracker/
89 if (!proTrackerDotIniFound && changePathToHome() && chdir(".protracker") == 0)
90 {
91 f = fopen("protracker.ini", "r");
92 if (f != NULL)
93 proTrackerDotIniFound = true;
94 }
95
96 chdir(oldCwd);
97 #endif
98
99 if (proTrackerDotIniFound)
100 loadProTrackerDotIni(f);
101
102 editor.oldTempo = editor.initialTempo;
103
104 // load PT.Config (if available)
105 ptDotConfigFound = false;
106
107 #ifdef _WIN32
108 f = openPTDotConfig();
109 if (f != NULL)
110 ptDotConfigFound = true;
111 #else
112 // check in program directory
113 f = openPTDotConfig();
114 if (f != NULL)
115 ptDotConfigFound = true;
116
117 // check in ~/.protracker/
118 if (!ptDotConfigFound && changePathToHome() && chdir(".protracker") == 0)
119 {
120 f = openPTDotConfig();
121 if (f != NULL)
122 ptDotConfigFound = true;
123 }
124
125 chdir(oldCwd);
126 #endif
127
128 if (ptDotConfigFound)
129 loadPTDotConfig(f);
130
131 if (proTrackerDotIniFound || ptDotConfigFound)
132 editor.configFound = true;
133
134 // load colors.ini (if available)
135 #ifdef _WIN32
136 loadColorsDotIni();
137 #else
138 // check in program directory
139 colorsDotIniFound = loadColorsDotIni();
140
141 // check in ~/.protracker/
142 if (!colorsDotIniFound && changePathToHome() && chdir(".protracker") == 0)
143 loadColorsDotIni();
144 #endif
145
146 #ifndef _WIN32
147 chdir(oldCwd);
148 #endif
149
150 // use palette for generating sample data mark (invert) table
151 createSampleMarkTable();
152 }
153
loadProTrackerDotIni(FILE * f)154 static bool loadProTrackerDotIni(FILE *f)
155 {
156 char *configBuffer, *configLine;
157 uint32_t configFileSize, lineLen, i;
158
159 fseek(f, 0, SEEK_END);
160 configFileSize = ftell(f);
161 rewind(f);
162
163 configBuffer = (char *)malloc(configFileSize + 1);
164 if (configBuffer == NULL)
165 {
166 fclose(f);
167 showErrorMsgBox("Couldn't parse protracker.ini: Out of memory!");
168 return false;
169 }
170
171 fread(configBuffer, 1, configFileSize, f);
172 configBuffer[configFileSize] = '\0';
173 fclose(f);
174
175 configLine = strtok(configBuffer, "\n");
176 while (configLine != NULL)
177 {
178 lineLen = (uint32_t)strlen(configLine);
179
180 // remove CR in CRLF linefeed (if present)
181 if (lineLen > 1)
182 {
183 if (configLine[lineLen-1] == '\r')
184 {
185 configLine[lineLen-1] = '\0';
186 lineLen--;
187 }
188 }
189
190 // COMMENT OR CATEGORY
191 if (*configLine == ';' || *configLine == '[')
192 {
193 configLine = strtok(NULL, "\n");
194 continue;
195 }
196
197 // NO_DWNSMP_ON_SMP_LOAD (no dialog for 2x downsample after >22kHz sample load)
198 else if (!_strnicmp(configLine, "NO_DWNSMP_ON_SMP_LOAD=", 22))
199 {
200 if (!_strnicmp(&configLine[22], "TRUE", 4)) config.noDownsampleOnSmpLoad = true;
201 else if (!_strnicmp(&configLine[22], "FALSE", 5)) config.noDownsampleOnSmpLoad = false;
202 }
203
204 // DISABLE_E8X (Karplus-Strong command)
205 else if (!_strnicmp(configLine, "DISABLE_E8X=", 12))
206 {
207 if (!_strnicmp(&configLine[12], "TRUE", 4)) config.disableE8xEffect = true;
208 else if (!_strnicmp(&configLine[12], "FALSE", 5)) config.disableE8xEffect = false;
209 }
210
211 // HWMOUSE
212 else if (!_strnicmp(configLine, "HWMOUSE=", 8))
213 {
214 if (!_strnicmp(&configLine[8], "TRUE", 4)) config.hwMouse = true;
215 else if (!_strnicmp(&configLine[8], "FALSE", 5)) config.hwMouse = false;
216 }
217
218 // VSYNCOFF
219 else if (!_strnicmp(configLine, "VSYNCOFF=", 9))
220 {
221 if (!_strnicmp(&configLine[9], "TRUE", 4)) config.vsyncOff = true;
222 else if (!_strnicmp(&configLine[9], "FALSE", 5)) config.vsyncOff = false;
223 }
224
225 // INTEGERSCALING
226 else if (!_strnicmp(configLine, "INTEGERSCALING=", 15))
227 {
228 if (!_strnicmp(&configLine[15], "TRUE", 4)) config.integerScaling = true;
229 else if (!_strnicmp(&configLine[15], "FALSE", 5)) config.integerScaling = false;
230 }
231
232 // FULLSCREENSTRETCH
233 else if (!_strnicmp(configLine, "FULLSCREENSTRETCH=", 18))
234 {
235 if (!_strnicmp(&configLine[18], "TRUE", 4)) config.fullScreenStretch = true;
236 else if (!_strnicmp(&configLine[18], "FALSE", 5)) config.fullScreenStretch = false;
237 }
238
239 // HIDEDISKOPDATES
240 else if (!_strnicmp(configLine, "HIDEDISKOPDATES=", 16))
241 {
242 if (!_strnicmp(&configLine[16], "TRUE", 4)) config.hideDiskOpDates = true;
243 else if (!_strnicmp(&configLine[16], "FALSE", 5)) config.hideDiskOpDates = false;
244 }
245
246 // AUTOCLOSEDISKOP
247 else if (!_strnicmp(configLine, "AUTOCLOSEDISKOP=", 16))
248 {
249 if (!_strnicmp(&configLine[16], "TRUE", 4)) config.autoCloseDiskOp = true;
250 else if (!_strnicmp(&configLine[16], "FALSE", 5)) config.autoCloseDiskOp = false;
251 }
252
253 // FULLSCREEN
254 else if (!_strnicmp(configLine, "FULLSCREEN=", 11))
255 {
256 if (!_strnicmp(&configLine[11], "TRUE", 4)) config.startInFullscreen = true;
257 else if (!_strnicmp(&configLine[11], "FALSE", 5)) config.startInFullscreen = false;
258 }
259
260 // PIXELFILTER
261 else if (!_strnicmp(configLine, "PIXELFILTER=", 12))
262 {
263 if (!_strnicmp(&configLine[12], "NEAREST", 7)) config.pixelFilter = PIXELFILTER_NEAREST;
264 else if (!_strnicmp(&configLine[12], "LINEAR", 6)) config.pixelFilter = PIXELFILTER_LINEAR;
265 else if (!_strnicmp(&configLine[12], "BEST", 4)) config.pixelFilter = PIXELFILTER_BEST;
266 }
267
268 // COMPOMODE
269 else if (!_strnicmp(configLine, "COMPOMODE=", 10))
270 {
271 if (!_strnicmp(&configLine[10], "TRUE", 4)) config.compoMode = true;
272 else if (!_strnicmp(&configLine[10], "FALSE", 5)) config.compoMode = false;
273 }
274
275 // PATTDOTS
276 else if (!_strnicmp(configLine, "PATTDOTS=", 9))
277 {
278 if (!_strnicmp(&configLine[9], "TRUE", 4)) config.pattDots = true;
279 else if (!_strnicmp(&configLine[9], "FALSE", 5)) config.pattDots = false;
280 }
281
282 // BLANKZERO
283 else if (!_strnicmp(configLine, "BLANKZERO=", 10))
284 {
285 if (!_strnicmp(&configLine[10], "TRUE", 4)) config.blankZeroFlag = true;
286 else if (!_strnicmp(&configLine[10], "FALSE", 5)) config.blankZeroFlag = false;
287 }
288
289 // REALVUMETERS
290 else if (!_strnicmp(configLine, "REALVUMETERS=", 13))
291 {
292 if (!_strnicmp(&configLine[13], "TRUE", 4)) config.realVuMeters = true;
293 else if (!_strnicmp(&configLine[13], "FALSE", 5)) config.realVuMeters = false;
294 }
295
296 // ACCIDENTAL
297 else if (!_strnicmp(configLine, "ACCIDENTAL=", 11))
298 {
299 if (!_strnicmp(&configLine[11], "SHARP", 4)) config.accidental = 0;
300 else if (!_strnicmp(&configLine[11], "FLAT", 5)) config.accidental = 1;
301 }
302
303 // QUANTIZE
304 else if (!_strnicmp(configLine, "QUANTIZE=", 9))
305 {
306 if (configLine[9] != '\0')
307 {
308 const int32_t num = atoi(&configLine[9]);
309 config.quantizeValue = (int16_t)(CLAMP(num, 0, 63));
310 }
311 }
312
313 // TRANSDEL
314 else if (!_strnicmp(configLine, "TRANSDEL=", 9))
315 {
316 if (!_strnicmp(&configLine[9], "TRUE", 4)) config.transDel = true;
317 else if (!_strnicmp(&configLine[9], "FALSE", 5)) config.transDel = false;
318 }
319
320 // DOTTEDCENTER
321 else if (!_strnicmp(configLine, "DOTTEDCENTER=", 13))
322 {
323 if (!_strnicmp(&configLine[13], "TRUE", 4)) config.waveformCenterLine = true;
324 else if (!_strnicmp(&configLine[13], "FALSE", 5)) config.waveformCenterLine = false;
325 }
326
327 // MODDOT
328 else if (!_strnicmp(configLine, "MODDOT=", 7))
329 {
330 if (!_strnicmp(&configLine[7], "TRUE", 4)) config.modDot = true;
331 else if (!_strnicmp(&configLine[7], "FALSE", 5)) config.modDot = false;
332 }
333
334 // SCALE3X (deprecated)
335 else if (!_strnicmp(configLine, "SCALE3X=", 8))
336 {
337 if (!_strnicmp(&configLine[8], "TRUE", 4)) config.videoScaleFactor = 3;
338 else if (!_strnicmp(&configLine[8], "FALSE", 5)) config.videoScaleFactor = 2;
339 }
340
341 // VIDEOSCALE
342 else if (!_strnicmp(configLine, "VIDEOSCALE=", 11))
343 {
344 if (lineLen >= 13 && configLine[12] == 'X' && isdigit(configLine[11]))
345 config.videoScaleFactor = configLine[11] - '0';
346 }
347
348 // REMEMBERPLAYMODE
349 else if (!_strnicmp(configLine, "REMEMBERPLAYMODE=", 17))
350 {
351 if (!_strnicmp(&configLine[17], "TRUE", 4)) config.rememberPlayMode = true;
352 else if (!_strnicmp(&configLine[17], "FALSE", 5)) config.rememberPlayMode = false;
353 }
354
355 // DEFAULTDIR
356 else if (!_strnicmp(configLine, "DEFAULTDIR=", 11))
357 {
358 if (lineLen > 11)
359 {
360 i = 11;
361 while (configLine[i] == ' ') i++; // remove spaces before string (if present)
362 while (configLine[lineLen-1] == ' ') lineLen--; // remove spaces after string (if present)
363
364 lineLen -= i;
365 if (lineLen > 0)
366 strncpy(config.defModulesDir, &configLine[i], (lineLen > PATH_MAX) ? PATH_MAX : lineLen);
367 }
368 }
369
370 // DEFAULTSMPDIR
371 else if (!_strnicmp(configLine, "DEFAULTSMPDIR=", 14))
372 {
373 if (lineLen > 14)
374 {
375 i = 14;
376 while (configLine[i] == ' ') i++; // remove spaces before string (if present)
377 while (configLine[lineLen-1] == ' ') lineLen--; // remove spaces after string (if present)
378
379 lineLen -= i;
380 if (lineLen > 0)
381 strncpy(config.defSamplesDir, &configLine[i], (lineLen > PATH_MAX) ? PATH_MAX : lineLen);
382 }
383 }
384
385 // FILTERMODEL
386 else if (!_strnicmp(configLine, "FILTERMODEL=", 12))
387 {
388 if (!_strnicmp(&configLine[12], "A500", 4)) config.filterModel = FILTERMODEL_A500;
389 else if (!_strnicmp(&configLine[12], "A1200", 5)) config.filterModel = FILTERMODEL_A1200;
390 }
391
392 // A500LOWPASSFILTER (deprecated, same as A4000LOWPASSFILTER)
393 else if (!_strnicmp(configLine, "A500LOWPASSFILTER=", 18))
394 {
395 if (!_strnicmp(&configLine[18], "TRUE", 4)) config.filterModel = FILTERMODEL_A500;
396 else if (!_strnicmp(&configLine[18], "FALSE", 5)) config.filterModel = FILTERMODEL_A1200;
397 }
398
399 // A4000LOWPASSFILTER (deprecated)
400 else if (!_strnicmp(configLine, "A4000LOWPASSFILTER=", 19))
401 {
402 if (!_strnicmp(&configLine[19], "TRUE", 4)) config.filterModel = FILTERMODEL_A500;
403 else if (!_strnicmp(&configLine[19], "FALSE", 5)) config.filterModel = FILTERMODEL_A1200;
404 }
405
406 // SAMPLINGFREQ
407 else if (!_strnicmp(configLine, "SAMPLINGFREQ=", 13))
408 {
409 if (configLine[10] != '\0')
410 {
411 const int32_t num = atoi(&configLine[13]);
412 config.audioInputFrequency = CLAMP(num, 44100, 192000);
413 }
414 }
415
416 // FREQUENCY
417 else if (!_strnicmp(configLine, "FREQUENCY=", 10))
418 {
419 if (configLine[10] != '\0')
420 {
421 const int32_t num = atoi(&configLine[10]);
422 config.soundFrequency = CLAMP(num, 44100, 192000);
423 }
424 }
425
426 // BUFFERSIZE
427 else if (!_strnicmp(configLine, "BUFFERSIZE=", 11))
428 {
429 if (configLine[11] != '\0')
430 {
431 const int32_t num = atoi(&configLine[11]);
432 config.soundBufferSize = CLAMP(num, 128, 8192);
433 }
434 }
435
436 // STEREOSEPARATION
437 else if (!_strnicmp(configLine, "STEREOSEPARATION=", 17))
438 {
439 if (configLine[17] != '\0')
440 {
441 const int32_t num = atoi(&configLine[17]);
442 config.stereoSeparation = (int8_t)(CLAMP(num, 0, 100));
443 }
444 }
445
446 configLine = strtok(NULL, "\n");
447 }
448
449 free(configBuffer);
450 return true;
451 }
452
openPTDotConfig(void)453 static FILE *openPTDotConfig(void)
454 {
455 char tmpFilename[16];
456 uint8_t i;
457 FILE *f;
458
459 f = fopen("PT.Config", "rb"); // PT didn't read PT.Config with no number, but let's support it
460 if (f == NULL)
461 {
462 for (i = 0; i < 100; i++)
463 {
464 sprintf(tmpFilename, "PT.Config-%02d", i);
465 f = fopen(tmpFilename, "rb");
466 if (f != NULL)
467 break;
468 }
469
470 if (i == 100)
471 return NULL;
472 }
473
474 return f;
475 }
476
loadPTDotConfig(FILE * f)477 static bool loadPTDotConfig(FILE *f)
478 {
479 char cfgString[24];
480 uint8_t tmp8;
481 uint16_t tmp16;
482 int32_t i;
483 uint32_t configFileSize;
484
485 // get filesize
486 fseek(f, 0, SEEK_END);
487 configFileSize = ftell(f);
488 if (configFileSize != 1024)
489 {
490 // not a valid PT.Config file
491 fclose(f);
492 return false;
493 }
494 rewind(f);
495
496 // check if file is a PT.Config file
497 fread(cfgString, 1, 24, f);
498
499 /* force version string to 2.3 so that we'll accept all versions.
500 ** AFAIK we're only loading values that were present since 1.0,
501 ** so it should be safe. */
502 cfgString[2] = '2';
503 cfgString[4] = '3';
504
505 if (strncmp(cfgString, "PT2.3 Configuration File", 24) != 0)
506 {
507 fclose(f);
508 return false;
509 }
510
511 // Palette
512 fseek(f, 154, SEEK_SET);
513 for (i = 0; i < 8; i++)
514 {
515 fread(&tmp16, 2, 1, f); // stored as Big-Endian
516 tmp16 = SWAP16(tmp16);
517 video.palette[i] = RGB12_to_RGB24(tmp16);
518 }
519
520 // Transpose Delete (delete out of range notes on transposing)
521 fseek(f, 174, SEEK_SET);
522 fread(&tmp8, 1, 1, f);
523 config.transDel = tmp8 ? true : false;
524 config.transDel = config.transDel;
525
526 // Note style (sharps/flats)
527 fseek(f, 200, SEEK_SET);
528 fread(&tmp8, 1, 1, f);
529 config.accidental = tmp8 ? 1 : 0;
530 config.accidental = config.accidental;
531
532 // Multi Mode Next
533 fseek(f, 462, SEEK_SET);
534 fread(&editor.multiModeNext[0], 1, 1, f);
535 fread(&editor.multiModeNext[1], 1, 1, f);
536 fread(&editor.multiModeNext[2], 1, 1, f);
537 fread(&editor.multiModeNext[3], 1, 1, f);
538
539 // Effect Macros
540 fseek(f, 466, SEEK_SET);
541 for (i = 0; i < 10; i++)
542 {
543 fread(&tmp16, 2, 1, f); // stored as Big-Endian
544 tmp16 = SWAP16(tmp16);
545 editor.effectMacros[i] = tmp16;
546 }
547
548 // Timing Mode (CIA/VBLANK)
549 fseek(f, 487, SEEK_SET);
550 fread(&tmp8, 1, 1, f);
551 editor.timingMode = tmp8 ? TEMPO_MODE_CIA : TEMPO_MODE_VBLANK;
552
553 // Blank Zeroes
554 fseek(f, 490, SEEK_SET);
555 fread(&tmp8, 1, 1, f);
556 config.blankZeroFlag = tmp8 ? true : false;
557 config.blankZeroFlag = config.blankZeroFlag;
558
559 // Initial Tempo (don't load if timing is set to VBLANK)
560 if (editor.timingMode == TEMPO_MODE_CIA)
561 {
562 fseek(f, 497, SEEK_SET);
563 fread(&tmp8, 1, 1, f);
564 if (tmp8 < 32) tmp8 = 32;
565 editor.initialTempo = tmp8;
566 editor.oldTempo = tmp8;
567 }
568
569 // Tuning Tone Note
570 fseek(f, 501, SEEK_SET);
571 fread(&tmp8, 1, 1, f);
572 if (tmp8 > 35) tmp8 = 35;
573 editor.tuningNote = tmp8;
574
575 // Tuning Tone Volume
576 fseek(f, 503, SEEK_SET);
577 fread(&tmp8, 1, 1, f);
578 if (tmp8 > 64) tmp8 = 64;
579 editor.tuningVol = tmp8;
580
581 // Initial Speed
582 fseek(f, 545, SEEK_SET);
583 fread(&tmp8, 1, 1, f);
584 if (editor.timingMode == TEMPO_MODE_VBLANK)
585 {
586 editor.initialSpeed = tmp8;
587 }
588 else
589 {
590 if (tmp8 > 0x20) tmp8 = 0x20;
591 editor.initialSpeed = tmp8;
592 }
593
594 // VU-Meter Colors
595 fseek(f, 546, SEEK_SET);
596 for (i = 0; i < 48; i++)
597 {
598 fread(&vuMeterColors[i], 2, 1, f); // stored as Big-Endian
599 vuMeterColors[i] = SWAP16(vuMeterColors[i]);
600 }
601
602 // Spectrum Analyzer Colors
603 fseek(f, 642, SEEK_SET);
604 for (i = 0; i < 36; i++)
605 {
606 fread(&analyzerColors[i], 2, 1, f); // stored as Big-Endian
607 analyzerColors[i] = SWAP16(analyzerColors[i]);
608 }
609
610 fclose(f);
611 return true;
612 }
613
hex2int(char ch)614 static uint8_t hex2int(char ch)
615 {
616 ch = (char)toupper(ch);
617
618 if (ch >= 'A' && ch <= 'F') return 10 + (ch - 'A');
619 else if (ch >= '0' && ch <= '9') return ch - '0';
620
621 return 0; // not a hex
622 }
623
loadColorsDotIni(void)624 static bool loadColorsDotIni(void)
625 {
626 char *configBuffer, *configLine;
627 uint16_t color;
628 uint32_t line, fileSize, lineLen;
629 FILE *f;
630
631 f = fopen("colors.ini", "r");
632 if (f == NULL)
633 return false;
634
635 // get filesize
636 fseek(f, 0, SEEK_END);
637 fileSize = ftell(f);
638 rewind(f);
639
640 configBuffer = (char *)malloc(fileSize + 1);
641 if (configBuffer == NULL)
642 {
643 fclose(f);
644 showErrorMsgBox("Couldn't parse colors.ini: Out of memory!");
645 return false;
646 }
647
648 fread(configBuffer, 1, fileSize, f);
649 configBuffer[fileSize] = '\0';
650 fclose(f);
651
652 // do parsing
653 configLine = strtok(configBuffer, "\n");
654 while (configLine != NULL)
655 {
656 lineLen = (uint32_t)strlen(configLine);
657
658 // read palette
659 if (lineLen >= (sizeof ("[Palette]")-1))
660 {
661 if (!_strnicmp("[Palette]", configLine, sizeof ("[Palette]")-1))
662 {
663 configLine = strtok(NULL, "\n");
664
665 line = 0;
666 while (configLine != NULL && line < 8)
667 {
668 color = (hex2int(configLine[0]) << 8) | (hex2int(configLine[1]) << 4) | hex2int(configLine[2]);
669 color &= 0xFFF;
670 video.palette[line] = RGB12_to_RGB24(color);
671
672 configLine = strtok(NULL, "\n");
673 line++;
674 }
675 }
676
677 if (configLine == NULL)
678 break;
679
680 lineLen = (uint32_t)strlen(configLine);
681 }
682
683 // read VU-meter colors
684 if (lineLen >= sizeof ("[VU-meter]")-1)
685 {
686 if (!_strnicmp("[VU-meter]", configLine, sizeof ("[VU-meter]")-1))
687 {
688 configLine = strtok(NULL, "\n");
689
690 line = 0;
691 while (configLine != NULL && line < 48)
692 {
693 color = (hex2int(configLine[0]) << 8) | (hex2int(configLine[1]) << 4) | hex2int(configLine[2]);
694 vuMeterColors[line] = color & 0xFFF;
695
696 configLine = strtok(NULL, "\n");
697 line++;
698 }
699 }
700
701 if (configLine == NULL)
702 break;
703
704 lineLen = (uint32_t)strlen(configLine);
705 }
706
707 // read spectrum analyzer colors
708 if (lineLen >= sizeof ("[SpectrumAnalyzer]")-1)
709 {
710 if (!_strnicmp("[SpectrumAnalyzer]", configLine, sizeof ("[SpectrumAnalyzer]")-1))
711 {
712 configLine = strtok(NULL, "\n");
713
714 line = 0;
715 while (configLine != NULL && line < 36)
716 {
717 color = (hex2int(configLine[0]) << 8) | (hex2int(configLine[1]) << 4) | hex2int(configLine[2]);
718 analyzerColors[line] = color & 0xFFF;
719
720 configLine = strtok(NULL, "\n");
721 line++;
722 }
723 }
724
725 if (configLine == NULL)
726 break;
727 }
728
729 configLine = strtok(NULL, "\n");
730 }
731
732 free(configBuffer);
733 return true;
734 }
735