1 #pragma once
2 
3 #include <SDL.h>
4 #include <unistd.h>
5 #include <errno.h>
6 #include <stdio.h>
7 #include <sys/stat.h>
8 #include <sys/types.h>
9 #include <math.h>
10 #include <cstddef>
11 
12 #include "console.h"
13 #include "../SourceX/stubs.h"
14 
15 #define WINDOW_ICON_NAME 0
16 
17 //== Utility
18 
19 #define SDL_zero(x) SDL_memset(&(x), 0, sizeof((x)))
20 #define SDL_InvalidParamError(param) SDL_SetError("Parameter '%s' is invalid", (param))
21 #define SDL_floor floor
22 
23 #define SDL_MAX_UINT32 ((Uint32)0xFFFFFFFFu)
24 
25 //== Events handling
26 
27 #define SDL_threadID Uint32
28 
29 #define SDL_Keysym SDL_keysym
30 #define SDL_Keycode SDLKey
31 
32 #define SDLK_PRINTSCREEN SDLK_PRINT
33 #define SDLK_SCROLLLOCK SDLK_SCROLLOCK
34 #define SDLK_NUMLOCKCLEAR SDLK_NUMLOCK
35 #define SDLK_KP_1 SDLK_KP1
36 #define SDLK_KP_2 SDLK_KP2
37 #define SDLK_KP_3 SDLK_KP3
38 #define SDLK_KP_4 SDLK_KP4
39 #define SDLK_KP_5 SDLK_KP5
40 #define SDLK_KP_6 SDLK_KP6
41 #define SDLK_KP_7 SDLK_KP7
42 #define SDLK_KP_8 SDLK_KP8
43 #define SDLK_KP_9 SDLK_KP9
44 #define SDLK_KP_0 SDLK_KP0
45 #define SDLK_LGUI SDLK_LSUPER
46 #define SDLK_RGUI SDLK_RSUPER
47 
48 // Haptic events are not supported in SDL1.
49 #define SDL_INIT_HAPTIC 0
50 
51 // For now we only process ASCII input when using SDL1.
52 #define SDL_TEXTINPUTEVENT_TEXT_SIZE 2
53 
54 #define SDL_JoystickID Sint32
55 #define SDL_JoystickNameForIndex SDL_JoystickName
56 
SDL_Log(const char * fmt,...)57 inline void SDL_Log(const char *fmt, ...)
58 {
59 	char message[256];
60 	va_list ap;
61 	va_start(ap, fmt);
62 	vsprintf(message, fmt, ap);
63 	va_end(ap);
64 
65 	printInConsole("INFO: %s\n", message);
66 }
67 
SDL_StartTextInput()68 inline void SDL_StartTextInput()
69 {
70 }
71 
SDL_StopTextInput()72 inline void SDL_StopTextInput()
73 {
74 }
75 
SDL_SetTextInputRect(const SDL_Rect * r)76 inline void SDL_SetTextInputRect(const SDL_Rect *r)
77 {
78 }
79 
80 //== Graphics helpers
81 
82 typedef struct SDL_Point {
83 	int x;
84 	int y;
85 } SDL_Point;
86 
SDL_PointInRect(const SDL_Point * p,const SDL_Rect * r)87 inline SDL_bool SDL_PointInRect(const SDL_Point *p, const SDL_Rect *r)
88 {
89 	return ((p->x >= r->x) && (p->x < (r->x + r->w)) && (p->y >= r->y) && (p->y < (r->y + r->h))) ? SDL_TRUE : SDL_FALSE;
90 }
91 
SDL_DisableScreenSaver()92 inline void SDL_DisableScreenSaver()
93 {
94 	DUMMY();
95 }
96 
97 //= Messagebox (simply logged to stderr for now)
98 
99 typedef enum {
100 	SDL_MESSAGEBOX_ERROR = 0x00000010,      /**< error dialog */
101 	SDL_MESSAGEBOX_WARNING = 0x00000020,    /**< warning dialog */
102 	SDL_MESSAGEBOX_INFORMATION = 0x00000040 /**< informational dialog */
103 } SDL_MessageBoxFlags;
104 
SDL_ShowSimpleMessageBox(Uint32 flags,const char * title,const char * message,SDL_Surface * window)105 inline int SDL_ShowSimpleMessageBox(Uint32 flags,
106     const char *title,
107     const char *message,
108     SDL_Surface *window)
109 {
110 	SDL_Log("MSGBOX: %s\n%s", title, message);
111 	return 0;
112 }
113 
114 //= Window handling
115 
116 #define SDL_Window SDL_Surface
117 
SDL_GetWindowPosition(SDL_Window * window,int * x,int * y)118 inline void SDL_GetWindowPosition(SDL_Window *window, int *x, int *y)
119 {
120 	*x = window->clip_rect.x;
121 	*y = window->clip_rect.x;
122 	SDL_Log("SDL_GetWindowPosition %d %d", *x, *y);
123 }
124 
SDL_SetWindowPosition(SDL_Window * window,int x,int y)125 inline void SDL_SetWindowPosition(SDL_Window *window, int x, int y)
126 {
127 	DUMMY();
128 }
129 
SDL_GetWindowSize(SDL_Window * window,int * w,int * h)130 inline void SDL_GetWindowSize(SDL_Window *window, int *w, int *h)
131 {
132 	*w = window->clip_rect.w;
133 	*h = window->clip_rect.h;
134 	SDL_Log("SDL_GetWindowSize %d %d", *w, *h);
135 }
136 
SDL_ShowWindow(SDL_Window * window)137 inline void SDL_ShowWindow(SDL_Window *window)
138 {
139 	DUMMY();
140 }
141 
SDL_HideWindow(SDL_Window * window)142 inline void SDL_HideWindow(SDL_Window *window)
143 {
144 	DUMMY();
145 }
146 
SDL_RaiseWindow(SDL_Window * window)147 inline void SDL_RaiseWindow(SDL_Window *window)
148 {
149 	DUMMY();
150 }
151 
SDL_DestroyWindow(SDL_Window * window)152 inline void SDL_DestroyWindow(SDL_Window *window)
153 {
154 	SDL_FreeSurface(window);
155 }
156 
157 inline void
SDL_WarpMouseInWindow(SDL_Window * window,int x,int y)158 SDL_WarpMouseInWindow(SDL_Window *window, int x, int y)
159 {
160 	SDL_WarpMouse(x, y);
161 }
162 
163 //= Renderer stubs
164 
165 #define SDL_Renderer void
166 
SDL_DestroyRenderer(SDL_Renderer * renderer)167 inline void SDL_DestroyRenderer(SDL_Renderer *renderer)
168 {
169 	if (renderer != NULL)
170 		UNIMPLEMENTED();
171 }
172 
173 //= Texture stubs
174 
175 #define SDL_Texture void
176 
SDL_DestroyTexture(SDL_Texture * texture)177 inline void SDL_DestroyTexture(SDL_Texture *texture)
178 {
179 	if (texture != NULL)
180 		UNIMPLEMENTED();
181 }
182 
183 //= Palette handling
184 
185 inline SDL_Palette *
SDL_AllocPalette(int ncolors)186 SDL_AllocPalette(int ncolors)
187 {
188 	SDL_Palette *palette;
189 
190 	/* Input validation */
191 	if (ncolors < 1) {
192 		SDL_InvalidParamError("ncolors");
193 		return NULL;
194 	}
195 
196 	palette = (SDL_Palette *)SDL_malloc(sizeof(*palette));
197 	if (!palette) {
198 		SDL_OutOfMemory();
199 		return NULL;
200 	}
201 	palette->colors = (SDL_Color *)SDL_malloc(ncolors * sizeof(*palette->colors));
202 	if (!palette->colors) {
203 		SDL_free(palette);
204 		return NULL;
205 	}
206 	palette->ncolors = ncolors;
207 	SDL_memset(palette->colors, 0xFF, ncolors * sizeof(*palette->colors));
208 	return palette;
209 }
210 
211 inline void
SDL_FreePalette(SDL_Palette * palette)212 SDL_FreePalette(SDL_Palette *palette)
213 {
214 	if (!palette) {
215 		SDL_InvalidParamError("palette");
216 		return;
217 	}
218 	SDL_free(palette->colors);
219 	SDL_free(palette);
220 }
221 
SDL_HasColorKey(SDL_Surface * surface)222 inline bool SDL_HasColorKey(SDL_Surface *surface)
223 {
224 	return (surface->flags & SDL_SRCCOLORKEY) != 0;
225 }
226 
227 //= Pixel formats
228 
229 #define SDL_PIXELFORMAT_INDEX8 1
230 #define SDL_PIXELFORMAT_RGB888 2
231 #define SDL_PIXELFORMAT_RGBA8888 3
232 
SDLBackport_PixelformatToMask(int pixelformat,Uint32 * flags,Uint32 * rmask,Uint32 * gmask,Uint32 * bmask,Uint32 * amask)233 inline void SDLBackport_PixelformatToMask(int pixelformat, Uint32 *flags, Uint32 *rmask,
234     Uint32 *gmask,
235     Uint32 *bmask,
236     Uint32 *amask)
237 {
238 	if (pixelformat == SDL_PIXELFORMAT_RGBA8888) {
239 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
240 		*rmask = 0xff000000;
241 		*gmask = 0x00ff0000;
242 		*bmask = 0x0000ff00;
243 		*amask = 0x000000ff;
244 #else
245 		*rmask = 0x000000ff;
246 		*gmask = 0x0000ff00;
247 		*bmask = 0x00ff0000;
248 		*amask = 0xff000000;
249 #endif
250 	} else if (pixelformat == SDL_PIXELFORMAT_RGB888) {
251 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
252 		*rmask = 0xff000000;
253 		*gmask = 0x00ff0000;
254 		*bmask = 0x0000ff00;
255 #else
256 		*rmask = 0x000000ff;
257 		*gmask = 0x0000ff00;
258 		*bmask = 0x00ff0000;
259 #endif
260 		*amask = 0;
261 	} else {
262 		*rmask = *gmask = *bmask = *amask = 0;
263 	}
264 }
265 
266 /**
267  * A limited implementation of `a.format` == `b.format` from SDL2.
268  */
SDLBackport_PixelFormatFormatEq(const SDL_PixelFormat * a,const SDL_PixelFormat * b)269 inline bool SDLBackport_PixelFormatFormatEq(const SDL_PixelFormat *a, const SDL_PixelFormat *b)
270 {
271 	return a->BitsPerPixel == b->BitsPerPixel && (a->palette != NULL) == (b->palette != NULL)
272 	    && a->Rmask == b->Rmask && a->Gmask == b->Gmask && a->Bmask == b->Bmask;
273 }
274 
275 /**
276  * Similar to `SDL_ISPIXELFORMAT_INDEXED` from SDL2.
277  */
SDLBackport_IsPixelFormatIndexed(const SDL_PixelFormat * pf)278 inline bool SDLBackport_IsPixelFormatIndexed(const SDL_PixelFormat *pf)
279 {
280 	return pf->BitsPerPixel == 8 && pf->palette != NULL;
281 }
282 
283 //= Surface creation
284 
285 inline SDL_Surface *
SDL_CreateRGBSurfaceWithFormat(Uint32 flags,int width,int height,int depth,Uint32 format)286 SDL_CreateRGBSurfaceWithFormat(Uint32 flags, int width, int height, int depth,
287     Uint32 format)
288 {
289 	Uint32 rmask, gmask, bmask, amask;
290 	SDLBackport_PixelformatToMask(format, &flags, &rmask, &gmask, &bmask, &amask);
291 	return SDL_CreateRGBSurface(flags, width, height, depth, rmask, gmask, bmask, amask);
292 }
293 
294 inline SDL_Surface *
SDL_CreateRGBSurfaceWithFormatFrom(void * pixels,Uint32 flags,int width,int height,int depth,Uint32 format)295 SDL_CreateRGBSurfaceWithFormatFrom(void *pixels, Uint32 flags, int width, int height, int depth,
296     Uint32 format)
297 {
298 	Uint32 rmask, gmask, bmask, amask;
299 	SDLBackport_PixelformatToMask(format, &flags, &rmask, &gmask, &bmask, &amask);
300 	return SDL_CreateRGBSurfaceFrom(pixels, flags, width, height, depth, rmask, gmask, bmask, amask);
301 }
302 
303 //= BlitScaled backport from SDL 2.0.9.
304 
305 #define SDL_BlitScaled SDL_UpperBlitScaled
306 
307 #define DEFINE_COPY_ROW(name, type)                              \
308 	static void name(type *src, int src_w, type *dst, int dst_w) \
309 	{                                                            \
310 		int i;                                                   \
311 		int pos, inc;                                            \
312 		type pixel = 0;                                          \
313                                                                  \
314 		pos = 0x10000;                                           \
315 		inc = (src_w << 16) / dst_w;                             \
316 		for (i = dst_w; i > 0; --i) {                            \
317 			while (pos >= 0x10000L) {                            \
318 				pixel = *src++;                                  \
319 				pos -= 0x10000L;                                 \
320 			}                                                    \
321 			*dst++ = pixel;                                      \
322 			pos += inc;                                          \
323 		}                                                        \
324 	}
DEFINE_COPY_ROW(copy_row1,Uint8)325 DEFINE_COPY_ROW(copy_row1, Uint8)
326 DEFINE_COPY_ROW(copy_row2, Uint16)
327 DEFINE_COPY_ROW(copy_row4, Uint32)
328 
329 static void
330 copy_row3(Uint8 *src, int src_w, Uint8 *dst, int dst_w)
331 {
332 	int i;
333 	int pos, inc;
334 	Uint8 pixel[3] = { 0, 0, 0 };
335 
336 	pos = 0x10000;
337 	inc = (src_w << 16) / dst_w;
338 	for (i = dst_w; i > 0; --i) {
339 		while (pos >= 0x10000L) {
340 			pixel[0] = *src++;
341 			pixel[1] = *src++;
342 			pixel[2] = *src++;
343 			pos -= 0x10000L;
344 		}
345 		*dst++ = pixel[0];
346 		*dst++ = pixel[1];
347 		*dst++ = pixel[2];
348 		pos += inc;
349 	}
350 }
351 
352 // NOTE: Not thread-safe
353 inline int
SDL_SoftStretch(SDL_Surface * src,const SDL_Rect * srcrect,SDL_Surface * dst,const SDL_Rect * dstrect)354 SDL_SoftStretch(SDL_Surface *src, const SDL_Rect *srcrect,
355     SDL_Surface *dst, const SDL_Rect *dstrect)
356 {
357 	// All the ASM support has been removed, as the platforms that the ASM
358 	// implementation exists for support SDL2 anyway.
359 	int src_locked;
360 	int dst_locked;
361 	int pos, inc;
362 	int dst_maxrow;
363 	int src_row, dst_row;
364 	Uint8 *srcp = NULL;
365 	Uint8 *dstp;
366 	SDL_Rect full_src;
367 	SDL_Rect full_dst;
368 	const int bpp = dst->format->BytesPerPixel;
369 
370 	if (!SDLBackport_PixelFormatFormatEq(src->format, dst->format)) {
371 		SDL_SetError("Only works with same format surfaces");
372 		return -1;
373 	}
374 
375 	/* Verify the blit rectangles */
376 	if (srcrect) {
377 		if ((srcrect->x < 0) || (srcrect->y < 0) || ((srcrect->x + srcrect->w) > src->w) || ((srcrect->y + srcrect->h) > src->h)) {
378 			SDL_SetError("Invalid source blit rectangle");
379 			return -1;
380 		}
381 	} else {
382 		full_src.x = 0;
383 		full_src.y = 0;
384 		full_src.w = src->w;
385 		full_src.h = src->h;
386 		srcrect = &full_src;
387 	}
388 	if (dstrect) {
389 		if ((dstrect->x < 0) || (dstrect->y < 0) || ((dstrect->x + dstrect->w) > dst->w) || ((dstrect->y + dstrect->h) > dst->h)) {
390 			SDL_SetError("Invalid destination blit rectangle");
391 			return -1;
392 		}
393 	} else {
394 		full_dst.x = 0;
395 		full_dst.y = 0;
396 		full_dst.w = dst->w;
397 		full_dst.h = dst->h;
398 		dstrect = &full_dst;
399 	}
400 
401 	/* Lock the destination if it's in hardware */
402 	dst_locked = 0;
403 	if (SDL_MUSTLOCK(dst)) {
404 		if (SDL_LockSurface(dst) < 0) {
405 			SDL_SetError("Unable to lock destination surface");
406 			return -1;
407 		}
408 		dst_locked = 1;
409 	}
410 	/* Lock the source if it's in hardware */
411 	src_locked = 0;
412 	if (SDL_MUSTLOCK(src)) {
413 		if (SDL_LockSurface(src) < 0) {
414 			if (dst_locked) {
415 				SDL_UnlockSurface(dst);
416 			}
417 			SDL_SetError("Unable to lock source surface");
418 			return -1;
419 		}
420 		src_locked = 1;
421 	}
422 
423 	/* Set up the data... */
424 	pos = 0x10000;
425 	inc = (srcrect->h << 16) / dstrect->h;
426 	src_row = srcrect->y;
427 	dst_row = dstrect->y;
428 
429 	/* Perform the stretch blit */
430 	for (dst_maxrow = dst_row + dstrect->h; dst_row < dst_maxrow; ++dst_row) {
431 		dstp = (Uint8 *)dst->pixels + (dst_row * dst->pitch)
432 		    + (dstrect->x * bpp);
433 		while (pos >= 0x10000L) {
434 			srcp = (Uint8 *)src->pixels + (src_row * src->pitch)
435 			    + (srcrect->x * bpp);
436 			++src_row;
437 			pos -= 0x10000L;
438 		}
439 		switch (bpp) {
440 		case 1:
441 			copy_row1(srcp, srcrect->w, dstp, dstrect->w);
442 			break;
443 		case 2:
444 			copy_row2((Uint16 *)srcp, srcrect->w,
445 			    (Uint16 *)dstp, dstrect->w);
446 			break;
447 		case 3:
448 			copy_row3(srcp, srcrect->w, dstp, dstrect->w);
449 			break;
450 		case 4:
451 			copy_row4((Uint32 *)srcp, srcrect->w,
452 			    (Uint32 *)dstp, dstrect->w);
453 			break;
454 		}
455 		pos += inc;
456 	}
457 
458 	/* We need to unlock the surfaces if they're locked */
459 	if (dst_locked) {
460 		SDL_UnlockSurface(dst);
461 	}
462 	if (src_locked) {
463 		SDL_UnlockSurface(src);
464 	}
465 	return (0);
466 }
467 
468 inline int
SDL_LowerBlitScaled(SDL_Surface * src,SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect)469 SDL_LowerBlitScaled(SDL_Surface *src, SDL_Rect *srcrect,
470     SDL_Surface *dst, SDL_Rect *dstrect)
471 {
472 	if (SDLBackport_PixelFormatFormatEq(src->format, dst->format) && !SDLBackport_IsPixelFormatIndexed(src->format)) {
473 		return SDL_SoftStretch(src, srcrect, dst, dstrect);
474 	} else {
475 		return SDL_LowerBlit(src, srcrect, dst, dstrect);
476 	}
477 }
478 
479 // NOTE: The second argument is const in SDL2 but not here.
480 inline int
SDL_UpperBlitScaled(SDL_Surface * src,SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect)481 SDL_UpperBlitScaled(SDL_Surface *src, SDL_Rect *srcrect,
482     SDL_Surface *dst, SDL_Rect *dstrect)
483 {
484 	double src_x0, src_y0, src_x1, src_y1;
485 	double dst_x0, dst_y0, dst_x1, dst_y1;
486 	SDL_Rect final_src, final_dst;
487 	double scaling_w, scaling_h;
488 	int src_w, src_h;
489 	int dst_w, dst_h;
490 
491 	/* Make sure the surfaces aren't locked */
492 	if (!src || !dst) {
493 		SDL_SetError("SDL_UpperBlitScaled: passed a NULL surface");
494 		return -1;
495 	}
496 	if (src->locked || dst->locked) {
497 		SDL_SetError("Surfaces must not be locked during blit");
498 		return -1;
499 	}
500 
501 	if (NULL == srcrect) {
502 		src_w = src->w;
503 		src_h = src->h;
504 	} else {
505 		src_w = srcrect->w;
506 		src_h = srcrect->h;
507 	}
508 
509 	if (NULL == dstrect) {
510 		dst_w = dst->w;
511 		dst_h = dst->h;
512 	} else {
513 		dst_w = dstrect->w;
514 		dst_h = dstrect->h;
515 	}
516 
517 	if (dst_w == src_w && dst_h == src_h) {
518 		/* No scaling, defer to regular blit */
519 		return SDL_BlitSurface(src, srcrect, dst, dstrect);
520 	}
521 
522 	scaling_w = (double)dst_w / src_w;
523 	scaling_h = (double)dst_h / src_h;
524 
525 	if (NULL == dstrect) {
526 		dst_x0 = 0;
527 		dst_y0 = 0;
528 		dst_x1 = dst_w - 1;
529 		dst_y1 = dst_h - 1;
530 	} else {
531 		dst_x0 = dstrect->x;
532 		dst_y0 = dstrect->y;
533 		dst_x1 = dst_x0 + dst_w - 1;
534 		dst_y1 = dst_y0 + dst_h - 1;
535 	}
536 
537 	if (NULL == srcrect) {
538 		src_x0 = 0;
539 		src_y0 = 0;
540 		src_x1 = src_w - 1;
541 		src_y1 = src_h - 1;
542 	} else {
543 		src_x0 = srcrect->x;
544 		src_y0 = srcrect->y;
545 		src_x1 = src_x0 + src_w - 1;
546 		src_y1 = src_y0 + src_h - 1;
547 
548 		/* Clip source rectangle to the source surface */
549 
550 		if (src_x0 < 0) {
551 			dst_x0 -= src_x0 * scaling_w;
552 			src_x0 = 0;
553 		}
554 
555 		if (src_x1 >= src->w) {
556 			dst_x1 -= (src_x1 - src->w + 1) * scaling_w;
557 			src_x1 = src->w - 1;
558 		}
559 
560 		if (src_y0 < 0) {
561 			dst_y0 -= src_y0 * scaling_h;
562 			src_y0 = 0;
563 		}
564 
565 		if (src_y1 >= src->h) {
566 			dst_y1 -= (src_y1 - src->h + 1) * scaling_h;
567 			src_y1 = src->h - 1;
568 		}
569 	}
570 
571 	/* Clip destination rectangle to the clip rectangle */
572 
573 	/* Translate to clip space for easier calculations */
574 	dst_x0 -= dst->clip_rect.x;
575 	dst_x1 -= dst->clip_rect.x;
576 	dst_y0 -= dst->clip_rect.y;
577 	dst_y1 -= dst->clip_rect.y;
578 
579 	if (dst_x0 < 0) {
580 		src_x0 -= dst_x0 / scaling_w;
581 		dst_x0 = 0;
582 	}
583 
584 	if (dst_x1 >= dst->clip_rect.w) {
585 		src_x1 -= (dst_x1 - dst->clip_rect.w + 1) / scaling_w;
586 		dst_x1 = dst->clip_rect.w - 1;
587 	}
588 
589 	if (dst_y0 < 0) {
590 		src_y0 -= dst_y0 / scaling_h;
591 		dst_y0 = 0;
592 	}
593 
594 	if (dst_y1 >= dst->clip_rect.h) {
595 		src_y1 -= (dst_y1 - dst->clip_rect.h + 1) / scaling_h;
596 		dst_y1 = dst->clip_rect.h - 1;
597 	}
598 
599 	/* Translate back to surface coordinates */
600 	dst_x0 += dst->clip_rect.x;
601 	dst_x1 += dst->clip_rect.x;
602 	dst_y0 += dst->clip_rect.y;
603 	dst_y1 += dst->clip_rect.y;
604 
605 	final_src.x = (Sint16)SDL_floor(src_x0 + 0.5);
606 	final_src.y = (Sint16)SDL_floor(src_y0 + 0.5);
607 	src_w = (int)SDL_floor(src_x1 + 1 + 0.5) - (int)SDL_floor(src_x0 + 0.5);
608 	src_h = (int)SDL_floor(src_y1 + 1 + 0.5) - (int)SDL_floor(src_y0 + 0.5);
609 	if (src_w < 0)
610 		src_w = 0;
611 	if (src_h < 0)
612 		src_h = 0;
613 
614 	final_src.w = static_cast<Uint16>(src_w);
615 	final_src.h = static_cast<Uint16>(src_h);
616 
617 	final_dst.x = (Sint16)SDL_floor(dst_x0 + 0.5);
618 	final_dst.y = (Sint16)SDL_floor(dst_y0 + 0.5);
619 	dst_w = (int)SDL_floor(dst_x1 - dst_x0 + 1.5);
620 	dst_h = (int)SDL_floor(dst_y1 - dst_y0 + 1.5);
621 	if (dst_w < 0)
622 		dst_w = 0;
623 	if (dst_h < 0)
624 		dst_h = 0;
625 
626 	final_dst.w = static_cast<Uint16>(dst_w);
627 	final_dst.h = static_cast<Uint16>(dst_h);
628 
629 	if (dstrect)
630 		*dstrect = final_dst;
631 
632 	if (final_dst.w == 0 || final_dst.h == 0 || final_src.w == 0 || final_src.h == 0) {
633 		/* No-op. */
634 		return 0;
635 	}
636 
637 	return SDL_LowerBlitScaled(src, &final_src, dst, &final_dst);
638 }
639 
640 //= Display handling
641 
642 typedef struct
643 {
644 	Uint32 format;    /**< pixel format */
645 	int w;            /**< width, in screen coordinates */
646 	int h;            /**< height, in screen coordinates */
647 	int refresh_rate; /**< refresh rate (or zero for unspecified) */
648 	void *driverdata; /**< driver-specific data, initialize to 0 */
649 } SDL_DisplayMode;
650 
SDL_GetCurrentDisplayMode(int displayIndex,SDL_DisplayMode * mode)651 inline int SDL_GetCurrentDisplayMode(int displayIndex, SDL_DisplayMode *mode)
652 {
653 	if (displayIndex != 0)
654 		UNIMPLEMENTED();
655 
656 	const SDL_VideoInfo *info = SDL_GetVideoInfo();
657 	if (info == NULL)
658 		return 0;
659 
660 	switch (info->vfmt->BitsPerPixel) {
661 	case 8:
662 		mode->format = SDL_PIXELFORMAT_INDEX8;
663 		break;
664 	case 24:
665 		mode->format = SDL_PIXELFORMAT_RGB888;
666 		break;
667 	case 32:
668 		mode->format = SDL_PIXELFORMAT_RGBA8888;
669 		break;
670 	default:
671 		mode->format = 0;
672 		break;
673 	}
674 
675 	mode->w = info->current_w;
676 	mode->h = info->current_h;
677 	mode->refresh_rate = 0;
678 	mode->driverdata = NULL;
679 
680 	return 0;
681 }
682 
683 //== Filesystem
684 
685 #if !defined(__QNXNTO__)
686 inline char *
readSymLink(const char * path)687 readSymLink(const char *path)
688 {
689 	// From sdl2-2.0.9/src/filesystem/unix/SDL_sysfilesystem.c
690 	char *retval = NULL;
691 	ssize_t len = 64;
692 	ssize_t rc = -1;
693 
694 	while (1) {
695 		char *ptr = (char *)SDL_realloc(retval, (size_t)len);
696 		if (ptr == NULL) {
697 			SDL_OutOfMemory();
698 			break;
699 		}
700 
701 		retval = ptr;
702 
703 		rc = readlink(path, retval, len);
704 		if (rc == -1) {
705 			break; /* not a symlink, i/o error, etc. */
706 		} else if (rc < len) {
707 			retval[rc] = '\0'; /* readlink doesn't null-terminate. */
708 			return retval;     /* we're good to go. */
709 		}
710 
711 		len *= 2; /* grow buffer, try again. */
712 	}
713 
714 	SDL_free(retval);
715 	return NULL;
716 }
717 #endif
718 
SDL_GetBasePath()719 inline char *SDL_GetBasePath()
720 {
721 	// From sdl2-2.0.9/src/filesystem/unix/SDL_sysfilesystem.c
722 
723 	char *retval = NULL;
724 
725 #if defined(__FREEBSD__)
726 	char fullpath[PATH_MAX];
727 	size_t buflen = sizeof(fullpath);
728 	const int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
729 	if (sysctl(mib, SDL_arraysize(mib), fullpath, &buflen, NULL, 0) != -1) {
730 		retval = SDL_strdup(fullpath);
731 		if (!retval) {
732 			SDL_OutOfMemory();
733 			return NULL;
734 		}
735 	}
736 #endif
737 #if defined(__OPENBSD__)
738 	char **retvalargs;
739 	size_t len;
740 	const int mib[] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV };
741 	if (sysctl(mib, 4, NULL, &len, NULL, 0) != -1) {
742 		retvalargs = SDL_malloc(len);
743 		if (!retvalargs) {
744 			SDL_OutOfMemory();
745 			return NULL;
746 		}
747 		sysctl(mib, 4, retvalargs, &len, NULL, 0);
748 		retval = SDL_malloc(PATH_MAX + 1);
749 		if (retval)
750 			realpath(retvalargs[0], retval);
751 
752 		SDL_free(retvalargs);
753 	}
754 #endif
755 #if defined(__SOLARIS__)
756 	const char *path = getexecname();
757 	if ((path != NULL) && (path[0] == '/')) { /* must be absolute path... */
758 		retval = SDL_strdup(path);
759 		if (!retval) {
760 			SDL_OutOfMemory();
761 			return NULL;
762 		}
763 	}
764 #endif
765 #if defined(__3DS__)
766 	retval = SDL_strdup("file:sdmc:/3ds/devilutionx/");
767 	return retval;
768 #endif
769 
770 	/* is a Linux-style /proc filesystem available? */
771 	if (!retval && (access("/proc", F_OK) == 0)) {
772 		/* !!! FIXME: after 2.0.6 ships, let's delete this code and just
773                       use the /proc/%llu version. There's no reason to have
774                       two copies of this plus all the #ifdefs. --ryan. */
775 #if defined(__FREEBSD__)
776 		retval = readSymLink("/proc/curproc/file");
777 #elif defined(__NETBSD__)
778 		retval = readSymLink("/proc/curproc/exe");
779 #elif defined(__QNXNTO__)
780 		retval = SDL_LoadFile("/proc/self/exefile", NULL);
781 #else
782 		retval = readSymLink("/proc/self/exe"); /* linux. */
783 		if (retval == NULL) {
784 			/* older kernels don't have /proc/self ... try PID version... */
785 			char path[64];
786 			const int rc = (int)SDL_snprintf(path, sizeof(path),
787 			    "/proc/%llu/exe",
788 			    (unsigned long long)getpid());
789 			if ((rc > 0) && (static_cast<std::size_t>(rc) < sizeof(path))) {
790 				retval = readSymLink(path);
791 			}
792 		}
793 #endif
794 	}
795 
796 	/* If we had access to argv[0] here, we could check it for a path,
797         or troll through $PATH looking for it, too. */
798 
799 	if (retval != NULL) { /* chop off filename. */
800 		char *ptr = SDL_strrchr(retval, '/');
801 		if (ptr != NULL) {
802 			*(ptr + 1) = '\0';
803 		} else { /* shouldn't happen, but just in case... */
804 			SDL_free(retval);
805 			retval = NULL;
806 		}
807 	}
808 
809 	if (retval != NULL) {
810 		/* try to shrink buffer... */
811 		char *ptr = (char *)SDL_realloc(retval, strlen(retval) + 1);
812 		if (ptr != NULL)
813 			retval = ptr; /* oh well if it failed. */
814 	}
815 
816 	return retval;
817 }
818 
SDL_GetPrefPath(const char * org,const char * app)819 inline char *SDL_GetPrefPath(const char *org, const char *app)
820 {
821 	// From sdl2-2.0.9/src/filesystem/unix/SDL_sysfilesystem.c
822 	/*
823      * We use XDG's base directory spec, even if you're not on Linux.
824      *  This isn't strictly correct, but the results are relatively sane
825      *  in any case.
826      *
827      * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
828      */
829 	const char *envr = SDL_getenv("XDG_DATA_HOME");
830 	const char *append;
831 	char *retval = NULL;
832 	char *ptr = NULL;
833 	size_t len = 0;
834 
835 #if defined(__3DS__)
836 	retval = SDL_strdup("sdmc:/3ds/devilutionx/");
837 	return retval;
838 #endif
839 
840 	if (!app) {
841 		SDL_InvalidParamError("app");
842 		return NULL;
843 	}
844 	if (!org) {
845 		org = "";
846 	}
847 
848 	if (!envr) {
849 		/* You end up with "$HOME/.local/share/Game Name 2" */
850 		envr = SDL_getenv("HOME");
851 		if (!envr) {
852 			/* we could take heroic measures with /etc/passwd, but oh well. */
853 			SDL_SetError("neither XDG_DATA_HOME nor HOME environment is set");
854 			return NULL;
855 		}
856 #if defined(__unix__) || defined(__unix)
857 		append = "/.local/share/";
858 #else
859 		append = "/";
860 #endif
861 	} else {
862 		append = "/";
863 	}
864 
865 	len = SDL_strlen(envr);
866 	if (envr[len - 1] == '/')
867 		append += 1;
868 
869 	len += SDL_strlen(append) + SDL_strlen(org) + SDL_strlen(app) + 3;
870 	retval = (char *)SDL_malloc(len);
871 	if (!retval) {
872 		SDL_OutOfMemory();
873 		return NULL;
874 	}
875 
876 	if (*org) {
877 		SDL_snprintf(retval, len, "%s%s%s/%s", envr, append, org, app);
878 	} else {
879 		SDL_snprintf(retval, len, "%s%s%s", envr, append, app);
880 	}
881 
882 	for (ptr = retval + 1; *ptr; ptr++) {
883 		if (*ptr == '/') {
884 			*ptr = '\0';
885 			if (mkdir(retval, 0700) != 0 && errno != EEXIST)
886 				goto error;
887 			*ptr = '/';
888 		}
889 	}
890 	if (mkdir(retval, 0700) != 0 && errno != EEXIST) {
891 	error:
892 		SDL_SetError("Couldn't create directory '%s': '%s'", retval, strerror(errno));
893 		SDL_free(retval);
894 		return NULL;
895 	}
896 
897 	// Append trailing /
898 	size_t final_len = SDL_strlen(retval);
899 	if (final_len + 1 < len) {
900 		retval[final_len++] = '/';
901 		retval[final_len] = '\0';
902 	}
903 
904 	return retval;
905 }
906