1 /*
2  * screen.c - Atari screen handling
3  *
4  * Copyright (c) 2001 Robert Golias and Piotr Fusik
5  * Copyright (C) 2001-2008 Atari800 development team (see DOC/CREDITS)
6  *
7  * This file is part of the Atari800 emulator project which emulates
8  * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
9  *
10  * Atari800 is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * Atari800 is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Atari800; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
23 */
24 
25 #define _POSIX_C_SOURCE 200112L /* for snprintf */
26 
27 #include "config.h"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 
32 #ifdef HAVE_LIBPNG
33 #include <png.h>
34 #endif
35 
36 #include "antic.h"
37 #include "atari.h"
38 #include "cassette.h"
39 #include "colours.h"
40 #include "log.h"
41 #include "pia.h"
42 #include "screen.h"
43 #include "sio.h"
44 #include "util.h"
45 
46 #define ATARI_VISIBLE_WIDTH 336
47 #define ATARI_LEFT_MARGIN 24
48 
49 ULONG *Screen_atari = NULL;
50 #ifdef DIRTYRECT
51 UBYTE *Screen_dirty = NULL;
52 #endif
53 #ifdef BITPL_SCR
54 ULONG *Screen_atari_b = NULL;
55 ULONG *Screen_atari1 = NULL;
56 ULONG *Screen_atari2 = NULL;
57 #endif
58 
59 /* The area that can been seen is Screen_visible_x1 <= x < Screen_visible_x2,
60    Screen_visible_y1 <= y < Screen_visible_y2.
61    Full Atari screen is 336x240. Screen_WIDTH is 384 only because
62    the code in antic.c sometimes draws more than 336 bytes in a line.
63    Currently Screen_visible variables are used only to place
64    disk led and snailmeter in the corners of the screen.
65 */
66 int Screen_visible_x1 = 24;				/* 0 .. Screen_WIDTH */
67 int Screen_visible_y1 = 0;				/* 0 .. Screen_HEIGHT */
68 int Screen_visible_x2 = 360;			/* 0 .. Screen_WIDTH */
69 int Screen_visible_y2 = Screen_HEIGHT;	/* 0 .. Screen_HEIGHT */
70 
71 int Screen_show_atari_speed = FALSE;
72 int Screen_show_disk_led = TRUE;
73 int Screen_show_sector_counter = FALSE;
74 int Screen_show_1200_leds = TRUE;
75 
76 #ifdef HAVE_LIBPNG
77 #define DEFAULT_SCREENSHOT_FILENAME_FORMAT "atari%03d.png"
78 #else
79 #define DEFAULT_SCREENSHOT_FILENAME_FORMAT "atari%03d.pcx"
80 #endif
81 
82 static char screenshot_filename_format[FILENAME_MAX] = DEFAULT_SCREENSHOT_FILENAME_FORMAT;
83 static int screenshot_no_max = 1000;
84 
85 /* converts "foo%bar##.pcx" to "foo%%bar%02d.pcx" */
Screen_SetScreenshotFilenamePattern(const char * p)86 static void Screen_SetScreenshotFilenamePattern(const char *p)
87 {
88 	char *f = screenshot_filename_format;
89 	char no_width = '0';
90 	screenshot_no_max = 1;
91 	/* 9 because sprintf'ed "no" can be 9 digits */
92 	while (f < screenshot_filename_format + FILENAME_MAX - 9) {
93 		/* replace a sequence of hashes with e.g. "%05d" */
94 		if (*p == '#') {
95 			if (no_width > '0') /* already seen a sequence of hashes */
96 				break;          /* invalid */
97 			/* count hashes */
98 			do {
99 				screenshot_no_max *= 10;
100 				p++;
101 				no_width++;
102 				/* now no_width is the number of hashes seen so far
103 				   and p points after the counted hashes */
104 			} while (no_width < '9' && *p == '#'); /* no more than 9 hashes */
105 			*f++ = '%';
106 			*f++ = '0';
107 			*f++ = no_width;
108 			*f++ = 'd';
109 			continue;
110 		}
111 		if (*p == '%')
112 			*f++ = '%'; /* double the percents */
113 		*f++ = *p;
114 		if (*p == '\0')
115 			return; /* ok */
116 		p++;
117 	}
118 	Log_print("Invalid filename pattern for screenshots, using default.");
119 	strcpy(screenshot_filename_format, DEFAULT_SCREENSHOT_FILENAME_FORMAT);
120 	screenshot_no_max = 1000;
121 }
122 
Screen_Initialise(int * argc,char * argv[])123 int Screen_Initialise(int *argc, char *argv[])
124 {
125 	int i;
126 	int j;
127 	int help_only = FALSE;
128 
129 	for (i = j = 1; i < *argc; i++) {
130 		int i_a = (i + 1 < *argc);		/* is argument available? */
131 		int a_m = FALSE;			/* error, argument missing! */
132 
133 		if (strcmp(argv[i], "-screenshots") == 0) {
134 			if (i_a)
135 				Screen_SetScreenshotFilenamePattern(argv[++i]);
136 			else a_m = TRUE;
137 		}
138 		else if (strcmp(argv[i], "-showspeed") == 0) {
139 			Screen_show_atari_speed = TRUE;
140 		}
141 		else {
142 			if (strcmp(argv[i], "-help") == 0) {
143 				help_only = TRUE;
144 				Log_print("\t-screenshots <p> Set filename pattern for screenshots");
145 				Log_print("\t-showspeed       Show percentage of actual speed");
146 			}
147 			argv[j++] = argv[i];
148 		}
149 
150 		if (a_m) {
151 			Log_print("Missing argument for '%s'", argv[i]);
152 			return FALSE;
153 		}
154 	}
155 	*argc = j;
156 
157 	/* don't bother mallocing Screen_atari with just "-help" */
158 	if (help_only)
159 		return TRUE;
160 
161 	if (Screen_atari == NULL) { /* platform-specific code can initialize it in theory */
162 		Screen_atari = (ULONG *) Util_malloc(Screen_HEIGHT * Screen_WIDTH);
163 		/* Clear the screen. */
164 		memset(Screen_atari, 0, Screen_HEIGHT * Screen_WIDTH);
165 #ifdef DIRTYRECT
166 		Screen_dirty = (UBYTE *) Util_malloc(Screen_HEIGHT * Screen_WIDTH / 8);
167 		Screen_EntireDirty();
168 #endif
169 #ifdef BITPL_SCR
170 		Screen_atari_b = (ULONG *) Util_malloc(Screen_HEIGHT * Screen_WIDTH);
171 		memset(Screen_atari_b, 0, Screen_HEIGHT * Screen_WIDTH);
172 		Screen_atari1 = Screen_atari;
173 		Screen_atari2 = Screen_atari_b;
174 #endif
175 	}
176 
177 	return TRUE;
178 }
179 
Screen_ReadConfig(char * string,char * ptr)180 int Screen_ReadConfig(char *string, char *ptr)
181 {
182 	if (strcmp(string, "SCREEN_SHOW_SPEED") == 0)
183 		return (Screen_show_atari_speed = Util_sscanbool(ptr)) != -1;
184 	else if (strcmp(string, "SCREEN_SHOW_IO_ACTIVITY") == 0)
185 		return (Screen_show_disk_led = Util_sscanbool(ptr)) != -1;
186 	else if (strcmp(string, "SCREEN_SHOW_IO_COUNTER") == 0)
187 		return (Screen_show_sector_counter = Util_sscanbool(ptr)) != -1;
188 	else if (strcmp(string, "SCREEN_SHOW_1200XL_LEDS") == 0)
189 		return (Screen_show_1200_leds = Util_sscanbool(ptr)) != -1;
190 	else return FALSE;
191 	return TRUE;
192 }
193 
Screen_WriteConfig(FILE * fp)194 void Screen_WriteConfig(FILE *fp)
195 {
196 	fprintf(fp, "SCREEN_SHOW_SPEED=%d\n", Screen_show_atari_speed);
197 	fprintf(fp, "SCREEN_SHOW_IO_ACTIVITY=%d\n", Screen_show_disk_led);
198 	fprintf(fp, "SCREEN_SHOW_IO_COUNTER=%d\n", Screen_show_sector_counter);
199 	fprintf(fp, "SCREEN_SHOW_1200XL_LEDS=%d\n", Screen_show_1200_leds);
200 }
201 
202 #define SMALLFONT_WIDTH    5
203 #define SMALLFONT_HEIGHT   7
204 #define SMALLFONT_PERCENT  10
205 #define SMALLFONT_C        11
206 #define SMALLFONT_D        12
207 #define SMALLFONT_L        13
208 #define SMALLFONT_SLASH    14
209 #define SMALLFONT_____ 0x00
210 #define SMALLFONT___X_ 0x02
211 #define SMALLFONT__X__ 0x04
212 #define SMALLFONT__XX_ 0x06
213 #define SMALLFONT_X___ 0x08
214 #define SMALLFONT_X_X_ 0x0A
215 #define SMALLFONT_XX__ 0x0C
216 #define SMALLFONT_XXX_ 0x0E
217 
SmallFont_DrawChar(UBYTE * screen,int ch,UBYTE color1,UBYTE color2)218 static void SmallFont_DrawChar(UBYTE *screen, int ch, UBYTE color1, UBYTE color2)
219 {
220 	static const UBYTE font[15][SMALLFONT_HEIGHT] = {
221 		{
222 			SMALLFONT_____,
223 			SMALLFONT__X__,
224 			SMALLFONT_X_X_,
225 			SMALLFONT_X_X_,
226 			SMALLFONT_X_X_,
227 			SMALLFONT__X__,
228 			SMALLFONT_____
229 		},
230 		{
231 			SMALLFONT_____,
232 			SMALLFONT__X__,
233 			SMALLFONT_XX__,
234 			SMALLFONT__X__,
235 			SMALLFONT__X__,
236 			SMALLFONT__X__,
237 			SMALLFONT_____
238 		},
239 		{
240 			SMALLFONT_____,
241 			SMALLFONT_XX__,
242 			SMALLFONT___X_,
243 			SMALLFONT__X__,
244 			SMALLFONT_X___,
245 			SMALLFONT_XXX_,
246 			SMALLFONT_____
247 		},
248 		{
249 			SMALLFONT_____,
250 			SMALLFONT_XX__,
251 			SMALLFONT___X_,
252 			SMALLFONT__X__,
253 			SMALLFONT___X_,
254 			SMALLFONT_XX__,
255 			SMALLFONT_____
256 		},
257 		{
258 			SMALLFONT_____,
259 			SMALLFONT___X_,
260 			SMALLFONT__XX_,
261 			SMALLFONT_X_X_,
262 			SMALLFONT_XXX_,
263 			SMALLFONT___X_,
264 			SMALLFONT_____
265 		},
266 		{
267 			SMALLFONT_____,
268 			SMALLFONT_XXX_,
269 			SMALLFONT_X___,
270 			SMALLFONT_XXX_,
271 			SMALLFONT___X_,
272 			SMALLFONT_XX__,
273 			SMALLFONT_____
274 		},
275 		{
276 			SMALLFONT_____,
277 			SMALLFONT__X__,
278 			SMALLFONT_X___,
279 			SMALLFONT_XX__,
280 			SMALLFONT_X_X_,
281 			SMALLFONT__X__,
282 			SMALLFONT_____
283 		},
284 		{
285 			SMALLFONT_____,
286 			SMALLFONT_XXX_,
287 			SMALLFONT___X_,
288 			SMALLFONT__X__,
289 			SMALLFONT__X__,
290 			SMALLFONT__X__,
291 			SMALLFONT_____
292 		},
293 		{
294 			SMALLFONT_____,
295 			SMALLFONT__X__,
296 			SMALLFONT_X_X_,
297 			SMALLFONT__X__,
298 			SMALLFONT_X_X_,
299 			SMALLFONT__X__,
300 			SMALLFONT_____
301 		},
302 		{
303 			SMALLFONT_____,
304 			SMALLFONT__X__,
305 			SMALLFONT_X_X_,
306 			SMALLFONT__XX_,
307 			SMALLFONT___X_,
308 			SMALLFONT__X__,
309 			SMALLFONT_____
310 		},
311 		{
312 			SMALLFONT_____,
313 			SMALLFONT_X_X_,
314 			SMALLFONT___X_,
315 			SMALLFONT__X__,
316 			SMALLFONT_X___,
317 			SMALLFONT_X_X_,
318 			SMALLFONT_____
319 		},
320 		{
321 			SMALLFONT_____,
322 			SMALLFONT__X__,
323 			SMALLFONT_X_X_,
324 			SMALLFONT_X___,
325 			SMALLFONT_X_X_,
326 			SMALLFONT__X__,
327 			SMALLFONT_____
328 		},
329 		{
330 			SMALLFONT_____,
331 			SMALLFONT_XX__,
332 			SMALLFONT_X_X_,
333 			SMALLFONT_X_X_,
334 			SMALLFONT_X_X_,
335 			SMALLFONT_XX__,
336 			SMALLFONT_____
337 		},
338 		{
339 			SMALLFONT_____,
340 			SMALLFONT_X___,
341 			SMALLFONT_X___,
342 			SMALLFONT_X___,
343 			SMALLFONT_X___,
344 			SMALLFONT_XXX_,
345 			SMALLFONT_____
346 		},
347 		{
348 			SMALLFONT_____,
349 			SMALLFONT___X_,
350 			SMALLFONT___X_,
351 			SMALLFONT__X__,
352 			SMALLFONT__X__,
353 			SMALLFONT_X___,
354 			SMALLFONT_____
355 		}
356 	};
357 	int y;
358 	for (y = 0; y < SMALLFONT_HEIGHT; y++) {
359 		int src;
360 		int mask;
361 		src = font[ch][y];
362 		for (mask = 1 << (SMALLFONT_WIDTH - 1); mask != 0; mask >>= 1) {
363 			ANTIC_VideoPutByte(screen, (UBYTE) ((src & mask) != 0 ? color1 : color2));
364 			screen++;
365 		}
366 		screen += Screen_WIDTH - SMALLFONT_WIDTH;
367 	}
368 }
369 
370 /* Returns screen address for placing the next character on the left of the
371    drawn number. */
SmallFont_DrawInt(UBYTE * screen,int n,UBYTE color1,UBYTE color2)372 static UBYTE *SmallFont_DrawInt(UBYTE *screen, int n, UBYTE color1, UBYTE color2)
373 {
374 	do {
375 		SmallFont_DrawChar(screen, n % 10, color1, color2);
376 		screen -= SMALLFONT_WIDTH;
377 		n /= 10;
378 	} while (n > 0);
379 	return screen;
380 }
381 
Screen_DrawAtariSpeed(double cur_time)382 void Screen_DrawAtariSpeed(double cur_time)
383 {
384 	if (Screen_show_atari_speed) {
385 		static int percent_display = 100;
386 		static int last_updated = 0;
387 		static double last_time = 0;
388 		if ((cur_time - last_time) >= 0.5) {
389 			percent_display = (int) (100 * (Atari800_nframes - last_updated) / (cur_time - last_time) / (Atari800_tv_mode == Atari800_TV_PAL ? 50 : 60));
390 			last_updated = Atari800_nframes;
391 			last_time = cur_time;
392 		}
393 		/* if (percent_display < 99 || percent_display > 101) */
394 		{
395 			/* space for 5 digits - up to 99999% Atari speed */
396 			UBYTE *screen = (UBYTE *) Screen_atari + Screen_visible_x1 + 5 * SMALLFONT_WIDTH
397 			          	+ (Screen_visible_y2 - SMALLFONT_HEIGHT) * Screen_WIDTH;
398 			SmallFont_DrawChar(screen, SMALLFONT_PERCENT, 0x0c, 0x00);
399 			SmallFont_DrawInt(screen - SMALLFONT_WIDTH, percent_display, 0x0c, 0x00);
400 		}
401 	}
402 }
403 
Screen_DrawDiskLED(void)404 void Screen_DrawDiskLED(void)
405 {
406 	if (SIO_last_op_time > 0) {
407 		UBYTE *screen;
408 		if (SIO_last_drive != 0x60)
409 			SIO_last_op_time--;
410 		screen = (UBYTE *) Screen_atari + Screen_visible_x2 - SMALLFONT_WIDTH
411 			+ (Screen_visible_y2 - SMALLFONT_HEIGHT) * Screen_WIDTH;
412 		if (SIO_last_drive == 0x60 || SIO_last_drive == 0x61) {
413 			if (CASSETTE_status != CASSETTE_STATUS_NONE) {
414 				if (Screen_show_disk_led)
415 					SmallFont_DrawChar(screen, SMALLFONT_C, 0x00, (UBYTE) (CASSETTE_record ? 0x2b : 0xac));
416 
417 				if (Screen_show_sector_counter) {
418 					/* Displaying tape length during saving is pointless since it would equal the number
419 					of the currently-written block, which is already displayed. */
420 					if (!CASSETTE_record) {
421 						screen = SmallFont_DrawInt(screen - SMALLFONT_WIDTH, CASSETTE_GetSize(), 0x00, 0x88);
422 						SmallFont_DrawChar(screen, SMALLFONT_SLASH, 0x00, 0x88);
423 					}
424 					SmallFont_DrawInt(screen - SMALLFONT_WIDTH, CASSETTE_GetPosition(), 0x00, 0x88);
425 				}
426 			}
427 		}
428 		else {
429 			if (Screen_show_disk_led) {
430 				SmallFont_DrawChar(screen, SIO_last_drive, 0x00, (UBYTE) (SIO_last_op == SIO_LAST_READ ? 0xac : 0x2b));
431 				SmallFont_DrawChar(screen -= SMALLFONT_WIDTH, SMALLFONT_D, 0x00, (UBYTE) (SIO_last_op == SIO_LAST_READ ? 0xac : 0x2b));
432 			}
433 
434 			if (Screen_show_sector_counter)
435 				SmallFont_DrawInt(screen - SMALLFONT_WIDTH, SIO_last_sector, 0x00, 0x88);
436 		}
437 	}
438 }
439 
Screen_Draw1200LED(void)440 void Screen_Draw1200LED(void)
441 {
442 	if (Screen_show_1200_leds && Atari800_keyboard_leds) {
443 		UBYTE *screen = (UBYTE *) Screen_atari + Screen_visible_x1 + SMALLFONT_WIDTH * 10
444 			+ (Screen_visible_y2 - SMALLFONT_HEIGHT) * Screen_WIDTH;
445 		UBYTE portb = PIA_PORTB | PIA_PORTB_mask;
446 		if ((portb & 0x04) == 0) {
447 			SmallFont_DrawChar(screen, SMALLFONT_L, 0x00, 0x36);
448 			SmallFont_DrawChar(screen + SMALLFONT_WIDTH, 1, 0x00, 0x36);
449 		}
450 		screen += SMALLFONT_WIDTH * 3;
451 		if ((portb & 0x08) == 0) {
452 			SmallFont_DrawChar(screen, SMALLFONT_L, 0x00, 0x36);
453 			SmallFont_DrawChar(screen + SMALLFONT_WIDTH, 2, 0x00, 0x36);
454 		}
455 	}
456 }
457 
Screen_FindScreenshotFilename(char * buffer,unsigned bufsize)458 void Screen_FindScreenshotFilename(char *buffer, unsigned bufsize)
459 {
460 	static int no = -1;
461 	static int overwrite = FALSE;
462 
463 	for (;;) {
464 		if (++no >= screenshot_no_max) {
465 			no = 0;
466 			overwrite = TRUE;
467 		}
468 		snprintf(buffer, bufsize, screenshot_filename_format, no);
469 		if (overwrite)
470 			break;
471 		if (!Util_fileexists(buffer))
472 			break; /* file does not exist - we can create it */
473 	}
474 }
475 
fputw(int x,FILE * fp)476 static void fputw(int x, FILE *fp)
477 {
478 	fputc(x & 0xff, fp);
479 	fputc(x >> 8, fp);
480 }
481 
Screen_SavePCX(FILE * fp,UBYTE * ptr1,UBYTE * ptr2)482 static void Screen_SavePCX(FILE *fp, UBYTE *ptr1, UBYTE *ptr2)
483 {
484 	int i;
485 	int x;
486 	int y;
487 	UBYTE plane = 16;	/* 16 = Red, 8 = Green, 0 = Blue */
488 	UBYTE last;
489 	UBYTE count;
490 
491 	fputc(0xa, fp);   /* pcx signature */
492 	fputc(0x5, fp);   /* version 5 */
493 	fputc(0x1, fp);   /* RLE encoding */
494 	fputc(0x8, fp);   /* bits per pixel */
495 	fputw(0, fp);     /* XMin */
496 	fputw(0, fp);     /* YMin */
497 	fputw(ATARI_VISIBLE_WIDTH - 1, fp); /* XMax */
498 	fputw(Screen_HEIGHT - 1, fp);        /* YMax */
499 	fputw(0, fp);     /* HRes */
500 	fputw(0, fp);     /* VRes */
501 	for (i = 0; i < 48; i++)
502 		fputc(0, fp); /* EGA color palette */
503 	fputc(0, fp);     /* reserved */
504 	fputc(ptr2 != NULL ? 3 : 1, fp); /* number of bit planes */
505 	fputw(ATARI_VISIBLE_WIDTH, fp);  /* number of bytes per scan line per color plane */
506 	fputw(1, fp);     /* palette info */
507 	fputw(ATARI_VISIBLE_WIDTH, fp); /* screen resolution */
508 	fputw(Screen_HEIGHT, fp);
509 	for (i = 0; i < 54; i++)
510 		fputc(0, fp);  /* unused */
511 
512 	for (y = 0; y < Screen_HEIGHT; ) {
513 		x = 0;
514 		do {
515 			last = ptr2 != NULL ? (((Colours_table[*ptr1] >> plane) & 0xff) + ((Colours_table[*ptr2] >> plane) & 0xff)) >> 1 : *ptr1;
516 			count = 0xc0;
517 			do {
518 				ptr1++;
519 				if (ptr2 != NULL)
520 					ptr2++;
521 				count++;
522 				x++;
523 			} while (last == (ptr2 != NULL ? (((Colours_table[*ptr1] >> plane) & 0xff) + ((Colours_table[*ptr2] >> plane) & 0xff)) >> 1 : *ptr1)
524 						&& count < 0xff && x < ATARI_VISIBLE_WIDTH);
525 			if (count > 0xc1 || last >= 0xc0)
526 				fputc(count, fp);
527 			fputc(last, fp);
528 		} while (x < ATARI_VISIBLE_WIDTH);
529 
530 		if (ptr2 != NULL && plane) {
531 			ptr1 -= ATARI_VISIBLE_WIDTH;
532 			ptr2 -= ATARI_VISIBLE_WIDTH;
533 			plane -= 8;
534 		}
535 		else {
536 			ptr1 += Screen_WIDTH - ATARI_VISIBLE_WIDTH;
537 			if (ptr2 != NULL) {
538 				ptr2 += Screen_WIDTH - ATARI_VISIBLE_WIDTH;
539 				plane = 16;
540 			}
541 			y++;
542 		}
543 	}
544 
545 	if (ptr2 == NULL) {
546 		/* write palette */
547 		fputc(0xc, fp);
548 		for (i = 0; i < 256; i++) {
549 			fputc(Colours_GetR(i), fp);
550 			fputc(Colours_GetG(i), fp);
551 			fputc(Colours_GetB(i), fp);
552 		}
553 	}
554 }
555 
striendswith(const char * s1,const char * s2)556 static int striendswith(const char *s1, const char *s2)
557 {
558 	int pos;
559 	pos = strlen(s1) - strlen(s2);
560 	if (pos < 0)
561 		return 0;
562 	return Util_stricmp(s1 + pos, s2) == 0;
563 }
564 
565 #ifdef HAVE_LIBPNG
Screen_SavePNG(FILE * fp,UBYTE * ptr1,UBYTE * ptr2)566 static void Screen_SavePNG(FILE *fp, UBYTE *ptr1, UBYTE *ptr2)
567 {
568 	png_structp png_ptr;
569 	png_infop info_ptr;
570 	png_bytep rows[Screen_HEIGHT];
571 
572 	png_ptr = png_create_write_struct(
573 		PNG_LIBPNG_VER_STRING,
574 		NULL, NULL, NULL
575 	);
576 	if (png_ptr == NULL)
577 		return;
578 	info_ptr = png_create_info_struct(png_ptr);
579 	if (info_ptr == NULL)
580 		return;
581 	png_init_io(png_ptr, fp);
582 	png_set_IHDR(
583 		png_ptr, info_ptr, ATARI_VISIBLE_WIDTH, Screen_HEIGHT,
584 		8, ptr2 == NULL ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_RGB,
585 		PNG_INTERLACE_NONE,
586 		PNG_COMPRESSION_TYPE_DEFAULT,
587 		PNG_FILTER_TYPE_DEFAULT
588 	);
589 	if (ptr2 == NULL) {
590 		int i;
591 		png_color palette[256];
592 		for (i = 0; i < 256; i++) {
593 			palette[i].red = Colours_GetR(i);
594 			palette[i].green = Colours_GetG(i);
595 			palette[i].blue = Colours_GetB(i);
596 		}
597 		png_set_PLTE(png_ptr, info_ptr, palette, 256);
598 		for (i = 0; i < Screen_HEIGHT; i++) {
599 			rows[i] = ptr1;
600 			ptr1 += Screen_WIDTH;
601 		}
602 	}
603 	else {
604 		png_bytep ptr3;
605 		int x;
606 		int y;
607 		ptr3 = (png_bytep) Util_malloc(3 * ATARI_VISIBLE_WIDTH * Screen_HEIGHT);
608 		for (y = 0; y < Screen_HEIGHT; y++) {
609 			rows[y] = ptr3;
610 			for (x = 0; x < ATARI_VISIBLE_WIDTH; x++) {
611 				*ptr3++ = (png_byte) ((Colours_GetR(*ptr1) + Colours_GetR(*ptr2)) >> 1);
612 				*ptr3++ = (png_byte) ((Colours_GetG(*ptr1) + Colours_GetG(*ptr2)) >> 1);
613 				*ptr3++ = (png_byte) ((Colours_GetB(*ptr1) + Colours_GetB(*ptr2)) >> 1);
614 				ptr1++;
615 				ptr2++;
616 			}
617 			ptr1 += Screen_WIDTH - ATARI_VISIBLE_WIDTH;
618 			ptr2 += Screen_WIDTH - ATARI_VISIBLE_WIDTH;
619 		}
620 	}
621 	png_set_rows(png_ptr, info_ptr, rows);
622 	png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
623 	png_destroy_write_struct(&png_ptr, &info_ptr);
624 	if (ptr2 != NULL)
625 		free(rows[0]);
626 }
627 #endif /* HAVE_LIBPNG */
628 
Screen_SaveScreenshot(const char * filename,int interlaced)629 int Screen_SaveScreenshot(const char *filename, int interlaced)
630 {
631 	int is_png;
632 	FILE *fp;
633 	ULONG *main_screen_atari;
634 	UBYTE *ptr1;
635 	UBYTE *ptr2;
636 	if (striendswith(filename, ".pcx"))
637 		is_png = 0;
638 #ifdef HAVE_LIBPNG
639 	else if (striendswith(filename, ".png"))
640 		is_png = 1;
641 #endif
642 	else
643 		return FALSE;
644 	fp = fopen(filename, "wb");
645 	if (fp == NULL)
646 		return FALSE;
647 	main_screen_atari = Screen_atari;
648 	ptr1 = (UBYTE *) Screen_atari + ATARI_LEFT_MARGIN;
649 	if (interlaced) {
650 		Screen_atari = (ULONG *) Util_malloc(Screen_WIDTH * Screen_HEIGHT);
651 		ptr2 = (UBYTE *) Screen_atari + ATARI_LEFT_MARGIN;
652 		ANTIC_Frame(TRUE); /* draw on Screen_atari */
653 	}
654 	else {
655 		ptr2 = NULL;
656 	}
657 #ifdef HAVE_LIBPNG
658 	if (is_png)
659 		Screen_SavePNG(fp, ptr1, ptr2);
660 	else
661 #endif
662 		Screen_SavePCX(fp, ptr1, ptr2);
663 	fclose(fp);
664 	if (interlaced) {
665 		free(Screen_atari);
666 		Screen_atari = main_screen_atari;
667 	}
668 	return TRUE;
669 }
670 
Screen_SaveNextScreenshot(int interlaced)671 void Screen_SaveNextScreenshot(int interlaced)
672 {
673 	char filename[FILENAME_MAX];
674 	Screen_FindScreenshotFilename(filename, sizeof(filename));
675 	Screen_SaveScreenshot(filename, interlaced);
676 }
677 
Screen_EntireDirty(void)678 void Screen_EntireDirty(void)
679 {
680 #ifdef DIRTYRECT
681 	if (Screen_dirty)
682 		memset(Screen_dirty, 1, Screen_WIDTH * Screen_HEIGHT / 8);
683 #endif /* DIRTYRECT */
684 }
685