1 /*****************************************************************************\
2      Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3                 This file is licensed under the Snes9x License.
4    For further information, consult the LICENSE file in the root directory.
5 \*****************************************************************************/
6 
7 #ifdef HAVE_LIBPNG
8 #include <png.h>
9 #endif
10 #include "port.h"
11 #include "crosshairs.h"
12 
13 static const char	*crosshairs[32] =
14 {
15 	"`              "  // Crosshair 0 (no image)
16 	"               "
17 	"               "
18 	"               "
19 	"               "
20 	"               "
21 	"               "
22 	"               "
23 	"               "
24 	"               "
25 	"               "
26 	"               "
27 	"               "
28 	"               "
29 	"               ",
30 
31 	"`              "  // Crosshair 1 (the classic small dot)
32 	"               "
33 	"               "
34 	"               "
35 	"               "
36 	"               "
37 	"               "
38 	"       #.      "
39 	"               "
40 	"               "
41 	"               "
42 	"               "
43 	"               "
44 	"               "
45 	"               ",
46 
47 	"`              "  // Crosshair 2 (a standard cross)
48 	"               "
49 	"               "
50 	"               "
51 	"      .#.      "
52 	"      .#.      "
53 	"    ...#...    "
54 	"    #######    "
55 	"    ...#...    "
56 	"      .#.      "
57 	"      .#.      "
58 	"               "
59 	"               "
60 	"               "
61 	"               ",
62 
63 	"`     .#.      "  // Crosshair 3 (a standard cross)
64 	"      .#.      "
65 	"      .#.      "
66 	"      .#.      "
67 	"      .#.      "
68 	"      .#.      "
69 	".......#......."
70 	"###############"
71 	".......#......."
72 	"      .#.      "
73 	"      .#.      "
74 	"      .#.      "
75 	"      .#.      "
76 	"      .#.      "
77 	"      .#.      ",
78 
79 	"`              "  // Crosshair 4 (an X)
80 	"               "
81 	"               "
82 	"    .     .    "
83 	"   .#.   .#.   "
84 	"    .#. .#.    "
85 	"     .#.#.     "
86 	"      .#.      "
87 	"     .#.#.     "
88 	"    .#. .#.    "
89 	"   .#.   .#.   "
90 	"    .     .    "
91 	"               "
92 	"               "
93 	"               ",
94 
95 	"`.           . "  // Crosshair 5 (an X)
96 	".#.         .#."
97 	" .#.       .#. "
98 	"  .#.     .#.  "
99 	"   .#.   .#.   "
100 	"    .#. .#.    "
101 	"     .#.#.     "
102 	"      .#.      "
103 	"     .#.#.     "
104 	"    .#. .#.    "
105 	"   .#.   .#.   "
106 	"  .#.     .#.  "
107 	" .#.       .#. "
108 	".#.         .#."
109 	" .           . ",
110 
111 	"`              "  // Crosshair 6 (a combo)
112 	"               "
113 	"               "
114 	"               "
115 	"    #  .  #    "
116 	"     # . #     "
117 	"      #.#      "
118 	"    ...#...    "
119 	"      #.#      "
120 	"     # . #     "
121 	"    #  .  #    "
122 	"               "
123 	"               "
124 	"               "
125 	"               ",
126 
127 	"`      .       "  // Crosshair 7 (a combo)
128 	" #     .     # "
129 	"  #    .    #  "
130 	"   #   .   #   "
131 	"    #  .  #    "
132 	"     # . #     "
133 	"      #.#      "
134 	".......#......."
135 	"      #.#      "
136 	"     # . #     "
137 	"    #  .  #    "
138 	"   #   .   #   "
139 	"  #    .    #  "
140 	" #     .     # "
141 	"       .       ",
142 
143 	"`      #       "  // Crosshair 8 (a diamond cross)
144 	"      #.#      "
145 	"     # . #     "
146 	"    #  .  #    "
147 	"   #   .   #   "
148 	"  #    .    #  "
149 	" #     .     # "
150 	"#......#......#"
151 	" #     .     # "
152 	"  #    .    #  "
153 	"   #   .   #   "
154 	"    #  .  #    "
155 	"     # . #     "
156 	"      #.#      "
157 	"       #       ",
158 
159 	"`     ###      "  // Crosshair 9 (a circle cross)
160 	"    ## . ##    "
161 	"   #   .   #   "
162 	"  #    .    #  "
163 	" #     .     # "
164 	" #     .     # "
165 	"#      .      #"
166 	"#......#......#"
167 	"#      .      #"
168 	" #     .     # "
169 	" #     .     # "
170 	"  #    .    #  "
171 	"   #   .   #   "
172 	"    ## . ##    "
173 	"      ###      ",
174 
175 	"`     .#.      "  // Crosshair 10 (a square cross)
176 	"      .#.      "
177 	"      .#.      "
178 	"   ....#....   "
179 	"   .#######.   "
180 	"   .#     #.   "
181 	"....#     #...."
182 	"#####     #####"
183 	"....#     #...."
184 	"   .#     #.   "
185 	"   .#######.   "
186 	"   ....#....   "
187 	"      .#.      "
188 	"      .#.      "
189 	"      .#.      ",
190 
191 	"`     .#.      "  // Crosshair 11 (an interrupted cross)
192 	"      .#.      "
193 	"      .#.      "
194 	"      .#.      "
195 	"      .#.      "
196 	"               "
197 	".....     ....."
198 	"#####     #####"
199 	".....     ....."
200 	"               "
201 	"      .#.      "
202 	"      .#.      "
203 	"      .#.      "
204 	"      .#.      "
205 	"      .#.      ",
206 
207 	"`.           . "  // Crosshair 12 (an interrupted X)
208 	".#.         .#."
209 	" .#.       .#. "
210 	"  .#.     .#.  "
211 	"   .#.   .#.   "
212 	"               "
213 	"               "
214 	"               "
215 	"               "
216 	"               "
217 	"   .#.   .#.   "
218 	"  .#.     .#.  "
219 	" .#.       .#. "
220 	".#.         .#."
221 	" .           . ",
222 
223 	"`      .       "  // Crosshair 13 (an interrupted combo)
224 	" #     .     # "
225 	"  #    .    #  "
226 	"   #   .   #   "
227 	"    #  .  #    "
228 	"               "
229 	"               "
230 	".....     ....."
231 	"               "
232 	"               "
233 	"    #  .  #    "
234 	"   #   .   #   "
235 	"  #    .    #  "
236 	" #     .     # "
237 	"       .       ",
238 
239 	"`####     #### "  // Crosshair 14
240 	"#....     ....#"
241 	"#.           .#"
242 	"#.           .#"
243 	"#.           .#"
244 	"       #       "
245 	"       #       "
246 	"     #####     "
247 	"       #       "
248 	"       #       "
249 	"#.           .#"
250 	"#.           .#"
251 	"#.           .#"
252 	"#....     ....#"
253 	" ####     #### ",
254 
255 	"`  .#     #.   "  // Crosshair 15
256 	"   .#     #.   "
257 	"   .#     #.   "
258 	"....#     #...."
259 	"#####     #####"
260 	"               "
261 	"               "
262 	"               "
263 	"               "
264 	"               "
265 	"#####     #####"
266 	"....#     #...."
267 	"   .#     #.   "
268 	"   .#     #.   "
269 	"   .#     #.   ",
270 
271 	"`      #       "  // Crosshair 16
272 	"       #       "
273 	"       #       "
274 	"   ....#....   "
275 	"   .   #   .   "
276 	"   .   #   .   "
277 	"   .   #   .   "
278 	"###############"
279 	"   .   #   .   "
280 	"   .   #   .   "
281 	"   .   #   .   "
282 	"   ....#....   "
283 	"       #       "
284 	"       #       "
285 	"       #       ",
286 
287 	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
288 };
289 
290 
S9xLoadCrosshairFile(int idx,const char * filename)291 bool S9xLoadCrosshairFile (int idx, const char *filename)
292 {
293 	if (idx < 1 || idx > 31)
294 		return (false);
295 
296 	char	*s = (char *) calloc(15 * 15 + 1, sizeof(char));
297 	if (s == NULL)
298 	{
299 		fprintf(stderr, "S9xLoadCrosshairFile: malloc error while reading ");
300 		perror(filename);
301 		return (false);
302 	}
303 
304 	FILE	*fp = fopen(filename, "rb");
305 	if (fp == NULL)
306 	{
307 		fprintf(stderr, "S9xLoadCrosshairFile: Couldn't open ");
308 		perror(filename);
309 		free(s);
310 		return (false);
311 	}
312 
313 	size_t	l = fread(s, 1, 8, fp);
314 	if (l != 8)
315 	{
316 		fprintf(stderr, "S9xLoadCrosshairFile: File is too short!\n");
317 		free(s);
318 		fclose(fp);
319 		return (false);
320 	}
321 
322 #ifdef HAVE_LIBPNG
323 	png_structp	png_ptr;
324 	png_infop	info_ptr;
325 
326 	if (!png_sig_cmp((png_byte *) s, 0, 8))
327 	{
328 		png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
329 		if (!png_ptr)
330 		{
331 			free(s);
332 			fclose(fp);
333 			return (false);
334 		}
335 
336 		info_ptr = png_create_info_struct(png_ptr);
337 		if (!info_ptr)
338 		{
339 			png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
340 			free(s);
341 			fclose(fp);
342 			return (false);
343 		}
344 
345 		png_init_io(png_ptr, fp);
346 		png_set_sig_bytes(png_ptr, 8);
347 		png_read_info(png_ptr, info_ptr);
348 
349 		png_uint_32	width, height;
350 		int			bit_depth, color_type;
351 
352 		png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
353 		if (color_type != PNG_COLOR_TYPE_PALETTE)
354 		{
355 			fprintf(stderr, "S9xLoadCrosshairFile: Input PNG is not a palettized image!\n");
356 			png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
357 			free(s);
358 			fclose(fp);
359 			return (false);
360 		}
361 
362 		if (bit_depth == 16)
363 			png_set_strip_16(png_ptr);
364 
365 		if (width != 15 || height != 15)
366 		{
367 			fprintf(stderr, "S9xLoadCrosshairFile: Expecting a 15x15 PNG\n");
368 			png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
369 			free(s);
370 			fclose(fp);
371 			return (false);
372 		}
373 
374 		png_color	*pngpal;
375 		png_byte	*trans;
376 		int			num_palette = 0, num_trans = 0;
377 		int			transcol = -1, fgcol = -1, bgcol = -1;
378 
379 		png_get_PLTE(png_ptr, info_ptr, &pngpal, &num_palette);
380 		png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
381 
382 		if (num_palette != 3 || num_trans != 1)
383 		{
384 			fprintf(stderr, "S9xLoadCrosshairFile: Expecting a 3-color PNG with 1 trasnparent color\n");
385 			png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
386 			free(s);
387 			fclose(fp);
388 			return (false);
389 		}
390 
391 		for (int i = 0; i < 3; i++)
392 		{
393 			if (trans[0] == i)
394 				transcol = i;
395 			else
396 			if (pngpal[i].red ==   0 && pngpal[i].green ==   0 && pngpal[i].blue ==   0)
397 				bgcol = i;
398 			else
399 			if (pngpal[i].red == 255 && pngpal[i].green == 255 && pngpal[i].blue == 255)
400 				fgcol = i;
401 		}
402 
403 		if (transcol < 0 || fgcol < 0 || bgcol < 0)
404 		{
405 			fprintf(stderr, "S9xLoadCrosshairFile: PNG must have 3 colors: white (fg), black (bg), and transparent.\n");
406 			png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
407 			free(s);
408 			fclose(fp);
409 			return (false);
410 		}
411 
412 		png_set_packing(png_ptr);
413 		png_read_update_info(png_ptr, info_ptr);
414 		png_byte	*row_pointer = new png_byte[png_get_rowbytes(png_ptr, info_ptr)];
415 
416 		for (int r = 0; r < 15 * 15; r += 15)
417 		{
418 			png_read_row(png_ptr, row_pointer, NULL);
419 
420 			for (int i = 0; i < 15; i++)
421 			{
422 				if (row_pointer[i] == transcol)
423 					s[r + i] = ' ';
424 				else
425 				if (row_pointer[i] == fgcol)
426 					s[r + i] = '#';
427 				else
428 				if (row_pointer[i] == bgcol)
429 					s[r + i] = '.';
430 				else
431 				{
432 					fprintf(stderr, "S9xLoadCrosshairFile: WTF? This was supposed to be a 3-color PNG!\n");
433 					png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
434 					free(s);
435 					fclose(fp);
436 					return (false);
437 				}
438 			}
439 		}
440 
441 		s[15 * 15] = 0;
442 		png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
443 	}
444 	else
445 #endif
446 	{
447 		l = fread(s + 8, 1, 15 - 8, fp);
448 		if (l != 15 - 8)
449 		{
450 			fprintf(stderr, "S9xLoadCrosshairFile: File is too short!\n");
451 			free(s);
452 			fclose(fp);
453 			return (false);
454 		}
455 
456 		if (getc(fp) != '\n')
457 		{
458 			fprintf(stderr, "S9xLoadCrosshairFile: Invalid file format! (note: PNG support is not available)\n");
459 			free(s);
460 			fclose(fp);
461 			return (false);
462 		}
463 
464 		for (int r = 1; r < 15; r++)
465 		{
466 			l = fread(s + r * 15, 1, 15, fp);
467 			if (l != 15)
468 			{
469 				fprintf(stderr, "S9xLoadCrosshairFile: File is too short! (note: PNG support is not available)\n");
470 				free(s);
471 				fclose(fp);
472 				return (false);
473 			}
474 
475 			if (getc(fp) != '\n')
476 			{
477 				fprintf(stderr, "S9xLoadCrosshairFile: Invalid file format! (note: PNG support is not available)\n");
478 				free(s);
479 				fclose(fp);
480 				return (false);
481 			}
482 		}
483 
484 		for (int i = 0; i < 15 * 15; i++)
485 		{
486 			if (s[i] != ' ' && s[i] != '#' && s[i] != '.')
487 			{
488 				fprintf(stderr, "S9xLoadCrosshairFile: Invalid file format! (note: PNG support is not available)\n");
489 				free(s);
490 				fclose(fp);
491 				return (false);
492 			}
493 		}
494 	}
495 
496 	fclose(fp);
497 
498 	if (crosshairs[idx] != NULL && crosshairs[idx][0] != '`')
499 		free((void *) crosshairs[idx]);
500 	crosshairs[idx] = s;
501 
502 	return (true);
503 }
504 
S9xGetCrosshair(int idx)505 const char * S9xGetCrosshair (int idx)
506 {
507 	if (idx < 0 || idx > 31)
508 		return (NULL);
509 
510 	return (crosshairs[idx]);
511 }
512