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
13static 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
291bool 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
505const char * S9xGetCrosshair (int idx)
506{
507 if (idx < 0 || idx > 31)
508 return (NULL);
509
510 return (crosshairs[idx]);
511}
512