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 <stdbool.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include "gui.h"
12 #include "palette.h"
13 #include "unicode.h"
14 #include "tinyfiledialogs/tinyfiledialogs.h"
15 
16 #ifndef _WIN32
17 #define _stricmp strcasecmp
18 #define _strnicmp strncasecmp
19 #endif
20 
21 #define SWAP16(value) \
22 ( \
23 	(((uint16_t)((value) & 0x00FF)) << 8) | \
24 	(((uint16_t)((value) & 0xFF00)) >> 8)   \
25 )
26 
27 UNICHAR *loadedFile = NULL;
28 bool configIsSaved = true;
29 
30 static uint8_t hex2int(char ch)
31 {
32 	ch = (char)toupper(ch);
33 
34 	     if (ch >= 'A' && ch <= 'F') return 10 + (ch - 'A');
35 	else if (ch >= '0' && ch <= '9') return ch - '0';
36 
37 	return 0; // not a hex
38 }
39 
40 static bool _loadPaletteFromColorsIni(FILE *f)
41 {
42 	char *configBuffer, *configLine;
43 	uint16_t color;
44 	uint32_t line, fileSize, lineLen;
45 
46 	// get filesize
47 	fseek(f, 0, SEEK_END);
48 	fileSize = ftell(f);
49 	rewind(f);
50 
51 	configBuffer = (char *)malloc(fileSize + 1);
52 	if (configBuffer == NULL)
53 	{
54 		fclose(f);
55 		showErrorMsgBox("Couldn't parse colors.ini: Out of memory!");
56 		return false;
57 	}
58 
59 	fread(configBuffer, 1, fileSize, f);
60 	configBuffer[fileSize] = '\0';
61 	fclose(f);
62 
63 	// do parsing
64 	configLine = strtok(configBuffer, "\n");
65 	while (configLine != NULL)
66 	{
67 		lineLen = (uint32_t)strlen(configLine);
68 
69 		// read palette
70 		if (lineLen >= sizeof ("[Palette]")-1)
71 		{
72 			if (!_strnicmp("[Palette]", configLine, sizeof ("[Palette]")-1))
73 			{
74 				configLine = strtok(NULL, "\n");
75 
76 				line = 0;
77 				while (configLine != NULL && line < 8)
78 				{
79 					color = (hex2int(configLine[0]) << 8) | (hex2int(configLine[1]) << 4) | hex2int(configLine[2]);
80 					palette[line] = color & 0xFFF;
81 
82 					configLine = strtok(NULL, "\n");
83 					line++;
84 				}
85 			}
86 
87 			if (configLine == NULL)
88 				break;
89 
90 			lineLen = (uint32_t)strlen(configLine);
91 		}
92 
93 		// read VU-meter colors
94 		if (lineLen >= sizeof ("[VU-meter]")-1)
95 		{
96 			if (!_strnicmp("[VU-meter]", configLine, sizeof ("[VU-meter]")-1))
97 			{
98 				configLine = strtok(NULL, "\n");
99 
100 				line = 0;
101 				while (configLine != NULL && line < 48)
102 				{
103 					color = (hex2int(configLine[0]) << 8) | (hex2int(configLine[1]) << 4) | hex2int(configLine[2]);
104 					vuColors[line] = color & 0xFFF;
105 
106 					configLine = strtok(NULL, "\n");
107 					line++;
108 				}
109 			}
110 
111 			if (configLine == NULL)
112 				break;
113 
114 			lineLen = (uint32_t)strlen(configLine);
115 		}
116 
117 		// read spectrum analyzer colors
118 		if (lineLen >= sizeof ("[SpectrumAnalyzer]")-1)
119 		{
120 			if (!_strnicmp("[SpectrumAnalyzer]", configLine, sizeof ("[SpectrumAnalyzer]")-1))
121 			{
122 				configLine = strtok(NULL, "\n");
123 
124 				line = 0;
125 				while (configLine != NULL && line < 36)
126 				{
127 					color = (hex2int(configLine[0]) << 8) | (hex2int(configLine[1]) << 4) | hex2int(configLine[2]);
128 					analyzerColors[line] = color & 0xFFF;
129 
130 					configLine = strtok(NULL, "\n");
131 					line++;
132 				}
133 			}
134 
135 			if (configLine == NULL)
136 				break;
137 		}
138 
139 		configLine = strtok(NULL, "\n");
140 	}
141 
142 	free(configBuffer);
143 	return true;
144 }
145 
146 static void _loadPaletteFromPtConfig(FILE *f)
147 {
148 	uint16_t tmp16;
149 	int32_t i;
150 
151 	// read palette
152 	fseek(f, 154, SEEK_SET);
153 	for (i = 0; i < 8; i++)
154 	{
155 		fread(&tmp16, 2, 1, f); // stored as Big-Endian
156 		tmp16 = SWAP16(tmp16);
157 
158 		palette[i] = tmp16 & 0xFFF;
159 	}
160 
161 	// read vu colors
162 	fseek(f, 546, SEEK_SET);
163 	for (i = 0; i < 48; i++)
164 	{
165 		fread(&tmp16, 2, 1, f); // stored as Big-Endian
166 		tmp16 = SWAP16(tmp16);
167 
168 		vuColors[i] = tmp16 & 0xFFF;
169 	}
170 
171 	// read spectrum analyzer colors
172 	fseek(f, 642, SEEK_SET);
173 	for (i = 0; i < 36; i++)
174 	{
175 		fread(&tmp16, 2, 1, f); // stored as Big-Endian
176 		tmp16 = SWAP16(tmp16);
177 
178 		analyzerColors[i] = tmp16 & 0xFFF;
179 	}
180 }
181 
182 static bool loadPaletteFromConfig(const UNICHAR *file, uint8_t ptDotConfig)
183 {
184 	char cfgString[26];
185 	uint32_t filesize;
186 	FILE *f;
187 
188 	f = UNICHAR_FOPEN(file, "rb");
189 	if (f == NULL)
190 	{
191 		showErrorMsgBox("Couldn't open file for reading!");
192 		return false;
193 	}
194 
195 	fseek(f, 0, SEEK_END);
196 	filesize = ftell(f);
197 	rewind(f);
198 
199 	if (ptDotConfig)
200 	{
201 		if (filesize != 1024)
202 		{
203 			fclose(f);
204 			showErrorMsgBox("This is not valid a PT.Config file!");
205 			return false;
206 		}
207 
208 		// check if file is a PT.Config file
209 		fread(cfgString, 1, 25, f);
210 		rewind(f);
211 		cfgString[25] = '\0'; // add null terminator
212 
213 		cfgString[2] = '1'; // force version to 1.0 so that we can compare string easily
214 		cfgString[4] = '0';
215 
216 		if (!strcmp(cfgString, "PT1.0 Configuration File\012"))
217 		{
218 			_loadPaletteFromPtConfig(f);
219 		}
220 		else
221 		{
222 			fclose(f);
223 			showErrorMsgBox("This is not a valid PT.Config file!");
224 			return false;
225 		}
226 	}
227 	else
228 	{
229 		_loadPaletteFromColorsIni(f);
230 	}
231 
232 	fclose(f);
233 
234 	if (!ptDotConfig)
235 	{
236 		if (loadedFile != NULL)
237 			free(loadedFile);
238 
239 		loadedFile = UNICHAR_STRDUP(file);
240 	}
241 
242 	fillCancel1Colors();
243 	fillCancel2Colors();
244 	updateBMPs();
245 	drawTracker();
246 	drawColorPicker1();
247 	drawColorPicker2();
248 	redrawScreen = true;
249 
250 	configIsSaved = false;
251 	return true;
252 }
253 
254 void loadColorsDotIni(void)
255 {
256 #ifdef _WIN32
257 	const UNICHAR *aFilterPatterns[] = { L"colors.ini" };
258 #else
259 	const UNICHAR *aFilterPatterns[] = { "colors.ini" };
260 #endif
261 	const UNICHAR *file;
262 
263 #ifdef _WIN32
264 	file = tinyfd_openFileDialogW(L"Please select a colors.ini to load...", L"colors.ini", 1,
265 		aFilterPatterns, NULL, 0);
266 #else
267 #ifdef __APPLE__
268 	file = tinyfd_openFileDialog("Please select a colors.ini to load...", "colors.ini", 0,
269 		NULL, NULL, 0);
270 #else
271 	file = tinyfd_openFileDialog("Please select a colors.ini to load...", "colors.ini", 1,
272 		aFilterPatterns, NULL, 0);
273 #endif
274 #endif
275 
276 	if (file != NULL)
277 		loadPaletteFromConfig(file, 0);
278 }
279 
280 void loadPTDotConfig(void)
281 {
282 	const UNICHAR *file;
283 
284 #ifdef _WIN32
285 	file = tinyfd_openFileDialogW(L"Please select a PT.Config file to load...", NULL, 0, NULL, NULL, 0);
286 #else
287 	file = tinyfd_openFileDialog("Please select a PT.Config file to load...", NULL, 0, NULL, NULL, 0);
288 #endif
289 
290 	if (file != NULL)
291 		loadPaletteFromConfig(file, 1);
292 }
293 
294 bool savePalette(bool showNotes)
295 {
296 	int32_t i;
297 	FILE *f;
298 	UNICHAR *folder;
299 
300 	if (loadedFile == NULL)
301 	{
302 		loadedFile = (UNICHAR *)calloc(4096+2, sizeof (UNICHAR));
303 		if (loadedFile == NULL)
304 		{
305 			configIsSaved = 0;
306 			showErrorMsgBox("Out of memory!");
307 			return false;
308 		}
309 	}
310 
311 	if (loadedFile[0] == '\0' && loadedFile[1] == '\0')
312 	{
313 #ifdef _WIN32
314 		folder = (UNICHAR *)tinyfd_selectFolderDialogW(L"Please select ProTracker clone folder...", NULL);
315 		if (folder == NULL || UNICHAR_STRLEN(folder) < 1)
316 			return false;
317 
318 		UNICHAR_STRCPY(loadedFile, folder);
319 		UNICHAR_STRCAT(loadedFile, L"\\");
320 		UNICHAR_STRCAT(loadedFile, L"colors.ini");
321 #else
322 		folder = (UNICHAR *)tinyfd_selectFolderDialog("Please select ProTracker clone folder...", NULL);
323 		if (folder == NULL || UNICHAR_STRLEN(folder) < 1)
324 			return false;
325 
326 		UNICHAR_STRCPY(loadedFile, folder);
327 		UNICHAR_STRCAT(loadedFile, "/");
328 		UNICHAR_STRCAT(loadedFile, "colors.ini");
329 #endif
330 	}
331 
332 	f = UNICHAR_FOPEN(loadedFile, "w");
333 	if (f == NULL)
334 	{
335 		showErrorMsgBox("Sorry, couldn't write to config.ini! Is the file in use?\n");
336 		return false;
337 	}
338 
339 	fprintf(f, "; WARNING: DO NOT TOUCH ANYTHING EXCEPT THE HEX VALUES!\n");
340 	fprintf(f, "; I recommend that you use the palette editor tool instead.\n");
341 	fprintf(f, "; You can find it at www.16-bits.org/pt.php\n");
342 	fprintf(f, ";\n");
343 	fprintf(f, "; Info:\n");
344 	fprintf(f, "; - Colors are stored as 12-bit RGB in hex (4096 total colors).\n");
345 	fprintf(f, ";   The palette order is the same as ProTracker on Amiga.\n");
346 	fprintf(f, ";\n");
347 	fprintf(f, "; To convert a 24-bit RGB hex value to 12-bit RGB hex, just\n");
348 	fprintf(f, "; skip every other digit. F.ex. 89ABCD -> 8AC\n");
349 	fprintf(f, ";\n");
350 	fprintf(f, "; To convert the other way around, repeat the digits.\n");
351 	fprintf(f, "; F.ex. 8AC -> 88AACC\n");
352 	fprintf(f, "\n");
353 	fprintf(f, "[Palette]\n");
354 	fprintf(f, "%03X ; Background\n", palette[0]);
355 	fprintf(f, "%03X ; GUI box light border / static text\n", palette[1]);
356 	fprintf(f, "%03X ; GUI box background color\n", palette[2]);
357 	fprintf(f, "%03X ; GUI box dark border / static text shadow\n", palette[3]);
358 	fprintf(f, "%03X ; Quadrascope / disk op. text / pos. ed. text\n", palette[4]);
359 	fprintf(f, "%03X ; Pattern cursor\n", palette[5]);
360 	fprintf(f, "%03X ; GUI box (non-static) text\n", palette[6]);
361 	fprintf(f, "%03X ; Pattern text\n", palette[7]);
362 	fprintf(f, "\n");
363 
364 	fprintf(f, "[VU-meter]\n");
365 	for (i = 0; i < 48; i++)
366 		fprintf(f, "%03X\n", vuColors[i]);
367 	fprintf(f, "\n");
368 
369 	fprintf(f, "[SpectrumAnalyzer]\n");
370 	for (i = 0; i < 36; i++)
371 		fprintf(f, "%03X\n", analyzerColors[i]);
372 
373 	fclose(f);
374 
375 	configIsSaved = true;
376 
377 	if (showNotes)
378 		SDL_ShowSimpleMessageBox(0, "Note", "Colors were successfully saved to colors.ini!", NULL);
379 
380 	return true;
381 }
382