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