1 /*
2 * OpenBOR - http://www.chronocrash.com
3 * -----------------------------------------------------------------------
4 * All rights reserved, see LICENSE in OpenBOR root for details.
5 *
6 * Copyright (c) 2004 - 2015 OpenBOR Team
7 *
8 * Video.c - adjunct to the main build's video.c.
9 * Made by UTunnels (utunnels@hotmail.com).
10 * Modifications by CRxTRDude, White Dragon and msmalik681.
11 */
12
13 #include <math.h>
14 #include "types.h"
15 #include "video.h"
16 #include "vga.h"
17 #include "screen.h"
18 #include "sdlport.h"
19 #include "openbor.h"
20 #include "gfxtypes.h"
21 #include "gfx.h"
22 #include "videocommon.h"
23
24 #include "pngdec.h"
25
26 extern int videoMode;
27
28 SDL_Window *window = NULL;
29 SDL_Renderer *renderer = NULL;
30 SDL_Texture *texture = NULL;
31 SDL_Texture *texture_base = NULL;
32
33 //For Android - Textures and a surface for the buttons
34 SDL_Texture *buttons = NULL;
35
36 s_videomodes stored_videomodes;
37 yuv_video_mode stored_yuv_mode;
38
39 char windowTitle[MAX_LABEL_LEN] = {"OpenBOR"};
40
41 int stretch = 1;
42 int opengl = 0;
43
44 int nativeWidth, nativeHeight; // resolution of device screen
45 static int textureWidth, textureHeight; // dimensions of game screen and texture
46
47 int brightness = 0;
48
49 #include "button_png_800x480.h" // default button skin
50
51
initSDL()52 void initSDL()
53 {
54 SDL_DisplayMode mode;
55 int init_flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC;
56 const char *var = SDL_getenv("SDL_VIDEO_FULLSCREEN_DISPLAY");
57 int vm;
58
59
60 #ifdef CUSTOM_SIGNAL_HANDLER
61 init_flags |= SDL_INIT_NOPARACHUTE;
62 #endif
63
64 if(SDL_Init(init_flags) < 0)
65 {
66 printf("SDL Failed to Init!!!! (%s)\n", SDL_GetError());
67 borExit(0);
68 }
69 SDL_ShowCursor(SDL_DISABLE);
70 SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
71 //atexit(SDL_Quit); //White Dragon: use SDL_Quit() into sdlport.c it's best practice!
72
73 if ( !var )
74 {
75 var = SDL_getenv("SDL_VIDEO_FULLSCREEN_HEAD");
76 }
77 if ( var )
78 {
79 vm = SDL_atoi(var);
80 }
81 else
82 {
83 vm = 0;
84 }
85
86 // Store the monitor's current resolution before setting the video mode for the first time
87 if(SDL_GetDesktopDisplayMode(vm, &mode) == 0)
88 {
89 nativeWidth = mode.w;
90 nativeHeight = mode.h;
91 }
92 else
93 {
94 nativeWidth = 640;
95 nativeHeight = 480;
96 }
97
98 //Hardcode full screen mode
99 savedata.fullscreen = 1;
100
101 }
102
103 //Start of touch control UI code
104
105 #define DOCKLEFT 8
106 #define DOCKRIGHT 2
107 #define DOCKTOP 1
108 #define DOCKBOTTOM 4
109 static int screendocking = DOCKTOP; //0 center if possible, 1 top 2 right 4 bottom 8 left
110
111 float bx[MAXTOUCHB];
112 float by[MAXTOUCHB];
113 float br[MAXTOUCHB];
114 unsigned touchstates[MAXTOUCHB];
115 #define _RE(x,y) {144*(x),144*(y),96,96}
116
117 SDL_Rect btnsrc[MAXTOUCHB] =
118 {
119 [SDID_MOVEUP] = _RE(0, 2), [SDID_MOVERIGHT] = _RE(1, 2), [SDID_MOVEDOWN] = _RE(2, 2), [SDID_MOVELEFT] = _RE(3, 2), //dpad
120 [SDID_SPECIAL] = _RE(4, 0), [SDID_ATTACK2] = _RE(1, 0), [SDID_JUMP] = _RE(1, 1), [SDID_ATTACK] = _RE(0, 0), //special a2 jump a1
121 [SDID_START] = _RE(2, 1), [SDID_ESC] = _RE(3, 1), [SDID_ATTACK3] = _RE(2, 0), [SDID_ATTACK4] = _RE(3, 0), //start esc a3 a4
122 [SDID_SCREENSHOT] = _RE(4, 1)
123 };
124 SDL_Rect btndes[MAXTOUCHB];
125
126 /*
127 Default touch UI
128 */
129
setup_touch_default()130 static void setup_touch_default()
131 {
132 float w = nativeWidth;
133 float h = nativeHeight;
134 float hh = w * 480 / 800;
135 float ra = 0.15f, rb = ra / 1.75f;
136 float cx1 = (ra + rb + rb / 5.0f) * hh, cy1 = h - cx1;
137 float cy2 = cy1;
138 //dpad
139 bx[SDID_MOVEUP] = cx1;
140 by[SDID_MOVEUP] = cy1 - ra * hh;
141 br[SDID_MOVEUP] = rb * hh;
142 bx[SDID_MOVERIGHT] = bx[SDID_MOVEUP] + ra * hh;
143 by[SDID_MOVERIGHT] = cy1;
144 br[SDID_MOVERIGHT] = br[SDID_MOVEUP];
145 bx[SDID_MOVEDOWN] = bx[SDID_MOVEUP];
146 by[SDID_MOVEDOWN] = cy1 + ra * hh;
147 br[SDID_MOVEDOWN] = br[SDID_MOVEUP];
148 bx[SDID_MOVELEFT] = bx[SDID_MOVEUP] - ra * hh;
149 by[SDID_MOVELEFT] = cy1;
150 br[SDID_MOVELEFT] = br[SDID_MOVEUP];
151 //special a2 jump a1
152 bx[SDID_SPECIAL] = w - bx[SDID_MOVEUP];
153 by[SDID_SPECIAL] = cy2 - ra * hh;
154 br[SDID_SPECIAL] = rb * hh;
155 bx[SDID_ATTACK2] = bx[SDID_SPECIAL] + ra * hh;
156 by[SDID_ATTACK2] = cy2;
157 br[SDID_ATTACK2] = br[SDID_MOVEUP];
158 bx[SDID_JUMP] = bx[SDID_SPECIAL];
159 by[SDID_JUMP] = cy2 + ra * hh;
160 br[SDID_JUMP] = br[SDID_MOVEUP];
161 bx[SDID_ATTACK] = bx[SDID_SPECIAL] - ra * hh;
162 by[SDID_ATTACK] = cy2;
163 br[SDID_ATTACK] = br[SDID_MOVEUP];
164 //start, esc
165 bx[SDID_START] = bx[SDID_MOVELEFT];
166 by[SDID_START] = h - by[SDID_MOVEDOWN];
167 br[SDID_START] = br[SDID_MOVELEFT];
168 bx[SDID_ESC] = bx[SDID_ATTACK2];
169 by[SDID_ESC] = h - by[SDID_JUMP];
170 br[SDID_ESC] = br[SDID_ATTACK2];
171 //a3 a4
172 bx[SDID_ATTACK3] = w / 2.0f - ra * hh;
173 by[SDID_ATTACK3] = by[SDID_MOVEDOWN];
174 br[SDID_ATTACK3] = br[SDID_MOVEDOWN];
175 bx[SDID_ATTACK4] = w / 2.0f + ra * hh;
176 by[SDID_ATTACK4] = by[SDID_JUMP];
177 br[SDID_ATTACK4] = br[SDID_JUMP];
178 //screenshot
179 bx[SDID_SCREENSHOT] = w / 2.0f;
180 by[SDID_SCREENSHOT] = h / 2.0f;
181 br[SDID_SCREENSHOT] = br[SDID_MOVEDOWN];
182
183
184 screendocking = DOCKTOP;
185 }
186
187 /*
188 Code to use touch.txt to displace buttons and set up skin.
189 */
190
setup_touch_txt()191 static int setup_touch_txt()
192 {
193 int pos, i, sdid, t;
194 static char filename[MAX_FILENAME_LEN];
195 static char pakname[MAX_FILENAME_LEN];
196 static char touchfilename[MAX_FILENAME_LEN];
197 char *buf, *command, *value;
198 size_t size;
199 ArgList arglist;
200 char argbuf[MAX_ARG_LEN + 1] = "";
201 float w = nativeWidth;
202 float h = nativeHeight;
203 float hh = w * 480 / 800;
204 SDL_Surface *ts;
205 char *pngb;
206 size_t pngs;
207
208 getPakName(pakname, -1);
209 sprintf(filename, "%s/%s", savesDir, pakname);
210 dirExists(filename, 1);
211 sprintf(filename, "%s/%s/touch.txt", savesDir, pakname);
212 sprintf(touchfilename, "%s/touch.txt", savesDir);
213 // Read file
214 if( buffer_pakfile(filename, &buf, &size) != 1 &&
215 buffer_pakfile("data/touch.txt", &buf, &size) != 1 &&
216 buffer_pakfile(touchfilename, &buf, &size) != 1)
217 {
218 return 0;
219 }
220
221 // Now interpret the contents of buf line by line
222 pos = 0;
223 while(pos < size)
224 {
225 if(ParseArgs(&arglist, buf + pos, argbuf))
226 {
227 command = GET_ARG(0);
228 if(command && command[0])
229 {
230 if(stricmp(command, "button") == 0)
231 {
232 sdid = translate_SDID(GET_ARG(1));
233 if(sdid >= 0)
234 {
235 bx[sdid] = GET_FLOAT_ARG(2) * hh;
236 by[sdid] = GET_FLOAT_ARG(3) * hh;
237 br[sdid] = GET_FLOAT_ARG(4) * hh;
238 t = GET_INT_ARG(5);
239 /*
240 corners:
241 0 1
242
243 3 2
244 */
245 if(t == 1 || t == 2)
246 {
247 bx[sdid] = w - bx[sdid];
248 }
249 if(t == 2 || t == 3)
250 {
251 by[sdid] = h - by[sdid];
252 }
253 }
254 }
255 // CRxTRDude 11/15/14 - Added 'skin' as a replacement to
256 // 'texture' command. Texture is still retained
257 // for backwards compatibility.
258 else if((stricmp(command, "texture") == 0) || (stricmp(command, "skin") == 0))
259 {
260 if(buffer_pakfile(GET_ARG(1), &pngb, &pngs))
261 {
262 ts = pngToSurface(pngb);
263 if(!ts || !(buttons = SDL_CreateTextureFromSurface(renderer, ts)))
264 {
265 printf("error: %s\n", SDL_GetError());
266 }
267 if(ts)
268 {
269 SDL_FreeSurface(ts);
270 }
271 if(pngb)
272 {
273 free(pngb);
274 }
275 }
276 }
277 else if(stricmp(command, "screendocking") == 0)
278 {
279 screendocking = 0;
280 for(i = 1; ; i++)
281 {
282 value = GET_ARG(i);
283 if(!value || !value[0])
284 {
285 break;
286 }
287 if(stricmp(value, "left") == 0)
288 {
289 screendocking |= DOCKLEFT;
290 }
291 else if(stricmp(value, "right") == 0)
292 {
293 screendocking |= DOCKRIGHT;
294 }
295 else if(stricmp(value, "top") == 0)
296 {
297 screendocking |= DOCKTOP;
298 }
299 else if(stricmp(value, "bottom") == 0)
300 {
301 screendocking |= DOCKBOTTOM;
302 }
303 else
304 {
305 screendocking = GET_INT_ARG(i);
306 }
307 }
308 }
309 }
310 }
311
312 // Go to next line
313 pos += getNewLineStart(buf + pos);
314 }
315
316 if(buf != NULL)
317 {
318 free(buf);
319 buf = NULL;
320 }
321
322 return 1;
323 }
324
setup_touch()325 static void setup_touch()
326 {
327 int i;
328 setup_touch_default();
329 setup_touch_txt();
330
331 for(i = 0; i < MAXTOUCHB; i++)
332 {
333 btndes[i].x = bx[i] - br[i];
334 btndes[i].y = by[i] - br[i];
335 btndes[i].h = btndes[i].w = 2 * br[i];
336 }
337 }
338 //End of touch control UI code
339
340 /*
341 Start of video code. Unlike the original code for video, everything is
342 incorporated in video_set_mode, since this is isolated from the main
343 code.
344 */
345
346 static unsigned pixelformats[4] = {SDL_PIXELFORMAT_INDEX8, SDL_PIXELFORMAT_BGR565, SDL_PIXELFORMAT_BGR888, SDL_PIXELFORMAT_ABGR8888};
347
video_set_mode(s_videomodes videomodes)348 int video_set_mode(s_videomodes videomodes)
349 {
350 stored_videomodes = videomodes;
351
352 //hardcode flags
353 int flags = SDL_WINDOW_SHOWN | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL;
354
355 savedata.fullscreen = 1;
356
357 videomodes = setupPreBlitProcessing(videomodes);
358
359 // 8-bit color should be transparently converted to 32-bit
360 assert(videomodes.pixel == 2 || videomodes.pixel == 4);
361
362 //destroy all
363 if(texture_base)
364 {
365 SDL_DestroyTexture(texture_base);
366 texture_base = NULL;
367 }
368 if(texture)
369 {
370 SDL_DestroyTexture(texture);
371 texture = NULL;
372 }
373 if(buttons)
374 {
375 SDL_DestroyTexture(buttons);
376 buttons = NULL;
377 }
378
379 if(videomodes.hRes == 0 && videomodes.vRes == 0)
380 {
381 Term_Gfx();
382 return 0;
383 }
384
385 if(!window && !(window = SDL_CreateWindow(windowTitle, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, nativeWidth, nativeHeight, flags)))
386 {
387 printf("error: %s\n", SDL_GetError());
388 return 0;
389 }
390
391 if(!renderer && !(renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED)))
392 {
393 printf("error: %s\n", SDL_GetError());
394 return 0;
395 }
396
397 //For status
398 SDL_RendererInfo info;
399 SDL_GetRendererInfo(renderer, &info);
400
401 printf("SDL video Renderer: %s \n", info.name);
402
403 SDL_SetHintWithPriority(SDL_HINT_RENDER_SCALE_QUALITY, savedata.hwfilter ? "nearest" : "linear", SDL_HINT_DEFAULT);
404
405 // now create a texture
406
407 textureWidth = videomodes.hRes;
408 textureHeight = videomodes.vRes;
409
410 if(!texture_base)
411 {
412 SDL_Surface *tmp_surface = SDL_CreateRGBSurface(0, 800, 480, 32, 0, 0, 0, 0);
413 if(!tmp_surface)
414 {
415 printf("error: %s\n", SDL_GetError());
416 return 0;
417 }
418 SDL_FillRect(tmp_surface, NULL, SDL_MapRGB(tmp_surface->format, 0, 0, 0));
419 SDL_SetSurfaceBlendMode(tmp_surface, SDL_BLENDMODE_NONE);
420 if(!(texture_base = SDL_CreateTextureFromSurface(renderer, tmp_surface)))
421 {
422 printf("error: %s\n", SDL_GetError());
423 return 0;
424 }
425 SDL_FreeSurface(tmp_surface);
426 tmp_surface = NULL;
427 }
428
429 if(!(texture = SDL_CreateTexture(renderer, pixelformats[videomodes.pixel-1], SDL_TEXTUREACCESS_STREAMING, textureWidth, textureHeight)))
430 {
431 printf("error: %s\n", SDL_GetError());
432 return 0;
433 }
434
435 setup_touch();
436
437 if(!buttons)
438 {
439 SDL_Surface *btn_screen = pngToSurface(buttonpng);
440 if(!btn_screen || !(buttons = SDL_CreateTextureFromSurface(renderer, btn_screen)))
441 {
442 printf("error: %s\n", SDL_GetError());
443 return 0;
444 }
445 SDL_FreeSurface(btn_screen);
446 btn_screen = NULL;
447 }
448
449 SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
450 video_clearscreen();
451
452 return 1;
453 }
454
video_fullscreen_flip()455 void video_fullscreen_flip()
456 {
457 }
458
blit()459 void blit()
460 {
461 int i, h;
462 int hide_touch;
463 extern int hide_t;
464
465 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
466 SDL_RenderClear(renderer);
467
468 //SDL_SetRenderTarget(renderer, NULL);
469 //SDL_RenderSetLogicalSize(renderer, 0, 0);
470 SDL_SetTextureBlendMode(texture_base, SDL_BLENDMODE_NONE);
471 SDL_RenderCopy(renderer, texture_base, NULL, NULL);
472
473 if(stretch)
474 {
475 //SDL_RenderSetLogicalSize(renderer, 0, 0);
476 SDL_RenderCopy(renderer, texture, NULL, NULL);
477 }
478 else
479 {
480 //SDL_RenderSetLogicalSize(renderer, textureWidth, textureHeight);
481 float aspectRatio = (float)textureWidth / (float)textureHeight;
482 float newWidth = nativeHeight * aspectRatio;
483
484 //SDL_Log("aspect: from %d/%d con %f, orig: %d/%d -> %d",textureWidth,textureHeight,aspectRatio,nativeWidth,nativeHeight,(int)newWidth);
485 SDL_Rect d_rect = {(int)(nativeWidth/2.0f - newWidth/2.0f), 0, (int)newWidth, nativeHeight};
486 SDL_RenderCopy(renderer, texture, NULL, &d_rect);
487 }
488
489 if(brightness > 0)
490 {
491 SDL_SetRenderDrawColor(renderer, 255, 255, 255, brightness-1);
492 }
493 else if(brightness < 0)
494 {
495 SDL_SetRenderDrawColor(renderer, 0, 0, 0, (-brightness)-1);
496 }
497 SDL_RenderFillRect(renderer, NULL);
498
499 //SDL_RenderSetLogicalSize(renderer, 0, 0);
500 hide_touch = (timer_gettick() > hide_t);
501 for(i = 0, h = 0; i < MAXTOUCHB; i++)
502 {
503 h += touchstates[i];
504 if(!hide_touch && (i != SDID_SCREENSHOT || touchstates[i]))
505 {
506 SDL_SetTextureAlphaMod(buttons, touchstates[i] ? 128 : 64);
507 SDL_RenderCopy(renderer, buttons, &btnsrc[i], &btndes[i]);
508 }
509 }
510 if(h)
511 {
512 hide_t = timer_gettick() + 5000;
513 }
514 SDL_RenderPresent(renderer);
515 }
516
video_copy_screen(s_screen * src)517 int video_copy_screen(s_screen *src)
518 {
519 s_videosurface *surface = getVideoSurface(src);
520 SDL_UpdateTexture(texture, NULL, surface->data, surface->pitch);
521 blit();
522
523 SDL_Delay(1);
524 return 1;
525 }
526
video_clearscreen()527 void video_clearscreen()
528 {
529 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
530 SDL_RenderClear(renderer);
531 //SDL_RenderPresent(renderer);
532 }
533
video_stretch(int enable)534 void video_stretch(int enable)
535 {
536 stretch = enable;
537 }
538
vga_vwait(void)539 void vga_vwait(void)
540 {
541 static int prevtick = 0;
542 int now = SDL_GetTicks();
543 int wait = 1000/60 - (now - prevtick);
544 if (wait>0)
545 {
546 SDL_Delay(wait);
547 }
548 else SDL_Delay(1);
549 prevtick = now;
550 }
551
video_set_color_correction(int gm,int br)552 void video_set_color_correction(int gm, int br)
553 {
554 brightness = br;
555 }
556
video_setup_yuv_overlay(const yuv_video_mode * mode)557 int video_setup_yuv_overlay(const yuv_video_mode *mode)
558 {
559 stored_yuv_mode = *mode;
560
561 SDL_DestroyTexture(texture);
562 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
563 texture = SDL_CreateTexture(renderer,
564 SDL_PIXELFORMAT_YV12,
565 SDL_TEXTUREACCESS_STREAMING,
566 mode->width, mode->height);
567 textureWidth = mode->display_width;
568 textureHeight = mode->display_height;
569 return texture ? 1 : 0;
570 }
571
video_prepare_yuv_frame(yuv_frame * src)572 int video_prepare_yuv_frame(yuv_frame *src)
573 {
574 SDL_UpdateYUVTexture(texture, NULL, src->lum, stored_yuv_mode.width,
575 src->cr, stored_yuv_mode.width/2, src->cb, stored_yuv_mode.width/2);
576 return 1;
577 }
578
video_display_yuv_frame(void)579 int video_display_yuv_frame(void)
580 {
581 blit();
582 return 1;
583 }
584