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