1 #include <stdlib.h>
2 #include <stdio.h>
3 #ifdef __EMSCRIPTEN__
4 #include <emscripten/emscripten.h>
5 #endif
6 #include <SDL.h>
7 #include <time.h>
8 #include <unistd.h>
9 #include <getopt.h>
10 #include "mrboom.h"
11 #include "common.hpp"
12 #include "MrboomHelper.hpp"
13 #include "xbrz.h"
14 
15 #define IFTRACES               (traceMask & DEBUG_SDL2)
16 
17 #ifdef DEBUG
18 #define XBRZ_DEFAULT_FACTOR    1
19 #else
20 #define XBRZ_DEFAULT_FACTOR    2
21 #endif
22 #ifdef __ARM_ARCH_6__
23 #define XBRZ_DEFAULT_FACTOR    1
24 #endif
25 #ifdef __ARM_ARCH_7__
26 #define XBRZ_DEFAULT_FACTOR    1
27 #endif
28 
29 int xbrzFactor    = XBRZ_DEFAULT_FACTOR;
30 int widthTexture  = 0;
31 int heightTexture = 0;
32 
33 #define BEEING_PLAYING_BUFFER    60
34 int beeingPlaying = 0;
35 
36 SDL_Renderer *renderer;
37 SDL_Texture * texture;
38 SDL_bool      done  = SDL_FALSE;
39 SDL_bool      noVGA = SDL_FALSE;
40 
41 SDL_Joystick *joysticks[nb_dyna] = { 0 };
42 int           joysticksInstance[nb_dyna];
43 
44 static clock_t begin;
45 unsigned int   nbFrames = 0;
46 int            testAI   = 0;
47 bool           slowMode = false;
48 #define MAX_TURBO    32
49 int turboMode                 = 0;
50 int anyButtonPushedMask       = 0;
51 int anyStartButtonPushedMask  = 0;
52 int anySelectButtonPushedMask = 0;
53 int framesPlayed              = 0;
54 bool checkDemoMode = false;
55 
quit(int rc)56 void quit(int rc)
57 {
58    SDL_Quit();
59    exit(rc);
60 }
61 
printJoystick()62 void printJoystick()
63 {
64    int i;
65 
66    for (i = 0; i < nb_dyna; i++)
67    {
68       if (joysticks[i] != 0)
69       {
70          log_debug("Joystick instance %d: (index:%d)\n", joysticksInstance[i], i);
71       }
72    }
73 }
74 
removeJoystick(int instance)75 void removeJoystick(int instance)
76 {
77    int i;
78 
79    if (IFTRACES)
80    {
81       log_debug("Joystick instance %d removed.\n", (int)instance);
82    }
83    for (i = 0; i < nb_dyna; i++)
84    {
85       if (joysticks[i] != 0)
86       {
87          if (joysticksInstance[i] == instance)
88          {
89             joysticks[i] = 0;
90             if (IFTRACES)
91             {
92                log_debug("Joystick index/player %d removed.\n", i);
93             }
94          }
95       }
96    }
97 }
98 
updateInput(int keyid,int playerNumber,int state,bool isIA)99 void updateInput(int keyid, int playerNumber, int state, bool isIA) {
100    if (!checkDemoMode) {
101       mrboom_update_input(keyid, playerNumber, state, isIA);
102    }
103 }
104 
105 
addJoystick(int index)106 void addJoystick(int index)
107 {
108    if (IFTRACES)
109    {
110       log_debug("Add Joystick index %d \n", index);
111    }
112    SDL_Joystick *joystick = SDL_JoystickOpen(index);
113    if (joystick == NULL)
114    {
115       log_error("SDL_JoystickOpen(%d) failed: %s\n", index,
116                 SDL_GetError());
117    }
118    else
119    {
120       int noFreePlayer = 1;
121       for (int i = 0; i < nb_dyna; i++)
122       {
123          if (joysticks[i] == 0)
124          {
125             joysticks[i]         = joystick;
126             joysticksInstance[i] = SDL_JoystickInstanceID(joystick);
127             if (IFTRACES)
128             {
129                log_debug("Joystick instance %d added for player %d\n", joysticksInstance[i], i);
130             }
131             noFreePlayer = 0;
132             return;
133          }
134       }
135 
136       if (noFreePlayer)
137       {
138          if (IFTRACES)
139          {
140             log_debug("Joystick cant be added\n");
141          }
142       }
143    }
144 }
145 
146 #define NB_SCREENS_BLURRING    8
147 
UpdateTexture(SDL_Texture * texture,bool skipRendering)148 void UpdateTexture(SDL_Texture *texture, bool skipRendering)
149 {
150    static uint32_t matrixPalette[NB_COLORS_PALETTE];
151    static uint32_t previousScreen[WIDTH][HEIGHT][NB_SCREENS_BLURRING];
152    static int      numberOfScreenToMelt = 1;
153 
154    Uint32 *         dst;
155    uint32_t         src[WIDTH * HEIGHT];
156    static uint32_t *trg = NULL;
157 
158    if (trg == NULL)
159    {
160       trg = (uint32_t *)malloc(widthTexture * heightTexture * sizeof(uint32_t));
161    }
162    int   row, col;
163    void *pixels;
164    int   pitch;
165 
166    if (SDL_LockTexture(texture, NULL, &pixels, &pitch) < 0)
167    {
168       log_error("Couldn't lock texture: %s\n", SDL_GetError());
169       quit(5);
170    }
171    int z = 0;
172    do
173    {
174       matrixPalette[z / 3]  = ((m.vgaPalette[z+0] << 2) | (m.vgaPalette[z+0] >> 4)) << 16;
175 	  matrixPalette[z / 3] |= ((m.vgaPalette[z+1] << 2) | (m.vgaPalette[z+1] >> 4)) << 8;
176 	  matrixPalette[z / 3] |= ((m.vgaPalette[z+2] << 2) | (m.vgaPalette[z+2] >> 4)) << 0;
177       z += 3;
178    } while (z != NB_COLORS_PALETTE * 3);
179 
180    for (row = 0; row < HEIGHT; ++row)
181    {
182       dst = (Uint32 *)((Uint8 *)pixels + row * pitch);
183       for (col = 0; col < WIDTH; ++col)
184       {
185          uint32_t color;
186          if (m.affiche_pal != 1)
187          {
188             color = matrixPalette[m.buffer[col + row * WIDTH]];
189             m.vgaRam[col + row * WIDTH] = m.buffer[col + row * WIDTH];
190          }
191          else
192          {
193             color = matrixPalette[m.vgaRam[col + row * WIDTH]];
194          }
195          previousScreen[col][row][frameNumber() % NB_SCREENS_BLURRING] = color;
196          //uint32_t color = matrixPalette[m.vgaRam[col + row * WIDTH]];
197          //previousScreen[col][row][frameNumber() % NB_SCREENS_BLURRING] = color;
198          if (skipRendering)
199          {
200             uint32_t b = 0;
201             uint32_t r = 0;
202             uint32_t g = 0;
203             for (int z = 0; z < numberOfScreenToMelt; z++)
204             {
205                uint32_t prevColor = previousScreen[col][row][(NB_SCREENS_BLURRING - z + frameNumber()) % NB_SCREENS_BLURRING];
206                b += prevColor & 255;
207                r += (prevColor >> 8) & 255;
208                g += (prevColor >> 16) & 255;
209             }
210             b     = b / (numberOfScreenToMelt);
211             r     = r / (numberOfScreenToMelt);
212             g     = g / (numberOfScreenToMelt);
213             color = (g << 16) | (r << 8) | b;
214             if (numberOfScreenToMelt < NB_SCREENS_BLURRING - 1)
215             {
216                numberOfScreenToMelt += 2;
217             }
218          }
219          else
220          {
221             numberOfScreenToMelt = 1;
222          }
223          if (xbrzFactor != 1)
224          {
225             src[col + row * WIDTH] = color;
226          }
227          else
228          {
229             *dst++ = color;
230          }
231       }
232    }
233 
234    if (xbrzFactor != 1)
235    {
236       scale(xbrzFactor,                   //valid range: 2 - 6
237             (uint32_t *)src, trg, WIDTH, HEIGHT,
238             xbrz::ColorFormat::RGB);
239 
240       for (row = 0; row < heightTexture; ++row)
241       {
242          dst = (Uint32 *)((Uint8 *)pixels + row * pitch);
243          for (col = 0; col < widthTexture; ++col)
244          {
245             *dst++ = trg[row * widthTexture + col];
246          }
247       }
248    }
249 
250    SDL_UnlockTexture(texture);
251 }
252 
getPlayerFromJoystickPort(int instance)253 int getPlayerFromJoystickPort(int instance)
254 {
255    int i;
256 
257    for (i = 0; i < nb_dyna; i++)
258    {
259       if (joysticks[i] != 0)
260       {
261          if (joysticksInstance[i] == instance)
262          {
263             return(i);
264          }
265       }
266    }
267    log_error("Error getPlayerFromJoystickPort %d\n", instance);
268    return(0);
269 }
270 
updateKeyboard(Uint8 scancode,int state)271 void  updateKeyboard(Uint8 scancode, int state)
272 {
273    if (IFTRACES)
274    {
275       log_debug("updateKeyboard %d", scancode);
276    }
277    if (state)
278    {
279       anyButtonPushedMask = anyButtonPushedMask | (1 << (scancode & 31));
280    }
281    else
282    {
283       anyButtonPushedMask = anyButtonPushedMask & ~(1 << (scancode & 31));
284    }
285    switch (scancode)
286    {
287    case SDL_SCANCODE_W:
288       updateInput(button_up, nb_dyna - 2, state, false);
289       break;
290 
291    case SDL_SCANCODE_S:
292       updateInput(button_down, nb_dyna - 2, state, false);
293       break;
294 
295    case SDL_SCANCODE_A:
296       updateInput(button_left, nb_dyna - 2, state, false);
297       break;
298 
299    case SDL_SCANCODE_D:
300       updateInput(button_right, nb_dyna - 2, state, false);
301       break;
302 
303    case SDL_SCANCODE_LALT:
304       updateInput(button_a, nb_dyna - 2, state, false);
305       break;
306 
307    case SDL_SCANCODE_LCTRL:
308    case SDL_SCANCODE_LGUI:
309       updateInput(button_b, nb_dyna - 2, state, false);
310       break;
311 
312    case SDL_SCANCODE_LSHIFT:
313       updateInput(button_x, nb_dyna - 2, state, false);
314       break;
315 
316    case SDL_SCANCODE_SPACE:
317       updateInput(button_select, nb_dyna - 2, state, false);
318       break;
319 
320    case SDL_SCANCODE_RETURN:
321       updateInput(button_start, nb_dyna - 2, state, false);
322       updateInput(button_start, nb_dyna - 1, state, false);
323       break;
324 
325    case SDL_SCANCODE_KP_ENTER:
326       updateInput(button_start, nb_dyna - 2, state, false);
327       updateInput(button_start, nb_dyna - 1, state, false);
328       updateInput(button_x, nb_dyna - 1, state, false);      // also jump 1st player
329       break;
330 
331    case SDL_SCANCODE_UP:
332       updateInput(button_up, nb_dyna - 1, state, false);
333       break;
334 
335    case SDL_SCANCODE_DOWN:
336       updateInput(button_down, nb_dyna - 1, state, false);
337       break;
338 
339    case SDL_SCANCODE_LEFT:
340       updateInput(button_left, nb_dyna - 1, state, false);
341       break;
342 
343    case SDL_SCANCODE_RIGHT:
344       updateInput(button_right, nb_dyna - 1, state, false);
345       break;
346    case SDL_SCANCODE_F1:
347       updateInput(button_l, nb_dyna - 1, state, false);
348       break;
349    case SDL_SCANCODE_F2:
350       updateInput(button_r, nb_dyna - 1, state, false);
351       break;
352    case SDL_SCANCODE_PAGEDOWN:
353    case SDL_SCANCODE_RALT:
354    case SDL_SCANCODE_KP_PERIOD:
355       updateInput(button_a, nb_dyna - 1, state, false);
356       break;
357 
358    case SDL_SCANCODE_END:
359    case SDL_SCANCODE_RCTRL:
360    case SDL_SCANCODE_RGUI:
361    case SDL_SCANCODE_KP_0:
362       log_debug("button_b %d i:%d\n", state, nb_dyna - 1);
363       updateInput(button_b, nb_dyna - 1, state, false);
364       break;
365 
366    case SDL_SCANCODE_HOME:
367    case SDL_SCANCODE_RSHIFT:
368       updateInput(button_x, nb_dyna - 1, state, false);
369       break;
370 
371    case SDL_SCANCODE_ESCAPE:
372       if (state)
373       {
374          if ((beeingPlaying > BEEING_PLAYING_BUFFER) || !isGameActive())
375          {
376             pressESC();
377          }
378          if (inTheMenu())
379          {
380             done = SDL_TRUE;
381          }
382       }
383       break;
384 
385    case SDL_SCANCODE_PAUSE:
386    case SDL_SCANCODE_P:
387       if (state)
388       {
389          pauseGameButton();
390       }
391       break;
392 
393    default:
394       if (IFTRACES)
395       {
396          log_debug("updateKeyboard not handled %d %d\n", scancode, state);
397       }
398       break;
399    }
400 }
401 
402 #define DEFAULT_DEAD_ZONE    8000
403 
404 int                joystickDeadZone            = DEFAULT_DEAD_ZONE;
405 int                anyStartButtonPushedCounter = 0;
406 uint32_t           windowID;
407 static const float ASPECT_RATIO = float(WIDTH) / float(HEIGHT);
408 
409 
410 bool        resizeDone = false;
411 SDL_Rect    screen;
412 SDL_Window *window;
413 int         width  = 0;
414 int         height = 0;
415 
416 #define NB_STICKS_MAX_PER_PAD    2
417 
418 int axis[NB_STICKS_MAX_PER_PAD * 2] = { 0, 0, 0, 0 };
419 
420 
pollEvent()421 void pollEvent() {
422    SDL_Event e;
423 
424    while (SDL_PollEvent(&e))
425    {
426       switch (e.type)
427       {
428       case SDL_WINDOWEVENT:
429          if (e.window.windowID == windowID)
430          {
431             switch (e.window.event)
432             {
433             case SDL_WINDOWEVENT_RESIZED: {
434                width  = e.window.data1;
435                height = e.window.data2;
436                float aspectRatio = (float)width / (float)height;
437                if (abs(aspectRatio - ASPECT_RATIO) > 0.1)
438                {
439                   if (aspectRatio < ASPECT_RATIO)
440                   {
441                      height = (1.f / ASPECT_RATIO) * width;
442                   }
443                   else
444                   {
445                      width = ASPECT_RATIO * height;
446                   }
447                   log_debug("Setting window size to %d, %d, aspect ratio: %f\n",
448                             width, height, (float)width / (float)height);
449                }
450                else
451                {
452                   log_debug("skip resize %f %f\n", aspectRatio, ASPECT_RATIO);
453                   break;
454                }
455                screen.w   = width;
456                screen.h   = height;
457                resizeDone = true;
458                break;
459             }
460             }
461          }
462          break;
463 
464       case SDL_JOYDEVICEADDED:
465          addJoystick(e.jdevice.which);
466          break;
467 
468       case SDL_JOYDEVICEREMOVED:
469          removeJoystick(e.jdevice.which);
470          break;
471 
472       case SDL_JOYAXISMOTION:
473 
474          if (e.jaxis.axis >= NB_STICKS_MAX_PER_PAD * 2)
475          {
476             if (IFTRACES)
477             {
478                log_debug("ignoring e.jaxis.axis=%d e.jaxis.value=%d player=%d\n", e.jaxis.axis, e.jaxis.value, getPlayerFromJoystickPort(e.jaxis.which));
479             }
480             break;
481          }
482 
483          if (e.jaxis.value < -joystickDeadZone)
484          {
485             axis[e.jaxis.axis] = -1;
486          }
487          else if (e.jaxis.value > joystickDeadZone)
488          {
489             axis[e.jaxis.axis] = 1;
490          }
491          else
492          {
493             axis[e.jaxis.axis] = 0;
494          }
495 
496          if (IFTRACES)
497          {
498             log_debug("e.jaxis.axis=%d e.jaxis.value=%d player=%d\n", e.jaxis.axis, e.jaxis.value, getPlayerFromJoystickPort(e.jaxis.which));
499          }
500          if ((e.jaxis.axis == 0) || (e.jaxis.axis == 2))
501          {
502             if ((axis[0] == 1) || (axis[2] == 1))
503             {
504                updateInput(button_right, getPlayerFromJoystickPort(e.jaxis.which), 1, false);
505                updateInput(button_left, getPlayerFromJoystickPort(e.jaxis.which), 0, false);
506             }
507             else if ((axis[0] == -1) || (axis[2] == -1))
508             {
509                updateInput(button_left, getPlayerFromJoystickPort(e.jaxis.which), 1, false);
510                updateInput(button_right, getPlayerFromJoystickPort(e.jaxis.which), 0, false);
511             }
512             else
513             {
514                updateInput(button_left, getPlayerFromJoystickPort(e.jaxis.which), 0, false);
515                updateInput(button_right, getPlayerFromJoystickPort(e.jaxis.which), 0, false);
516             }
517          }
518          if ((e.jaxis.axis == 1) || (e.jaxis.axis == 3))
519          {
520             if ((axis[1] == 1) || (axis[3] == 1))
521             {
522                updateInput(button_down, getPlayerFromJoystickPort(e.jaxis.which), 1, false);
523                updateInput(button_up, getPlayerFromJoystickPort(e.jaxis.which), 0, false);
524             }
525             else if ((axis[1] == -1) || (axis[3] == -1))
526             {
527                updateInput(button_up, getPlayerFromJoystickPort(e.jaxis.which), 1, false);
528                updateInput(button_down, getPlayerFromJoystickPort(e.jaxis.which), 0, false);
529             }
530             else
531             {
532                updateInput(button_up, getPlayerFromJoystickPort(e.jaxis.which), 0, false);
533                updateInput(button_down, getPlayerFromJoystickPort(e.jaxis.which), 0, false);
534             }
535          }
536          break;
537 
538       case SDL_JOYHATMOTION:
539       {
540          int player = getPlayerFromJoystickPort(e.jhat.which);
541          if (IFTRACES)
542          {
543             log_debug("SDL_JOYHATMOTION: .jhat=%d which=%d player=%d\n", e.jhat.value, e.jhat.which, getPlayerFromJoystickPort(e.jhat.which));
544          }
545 
546          switch (e.jhat.value)
547          {
548          case SDL_HAT_CENTERED:
549             updateInput(button_up, player, 0, false);
550             updateInput(button_down, player, 0, false);
551             updateInput(button_left, player, 0, false);
552             updateInput(button_right, player, 0, false);
553             break;
554 
555          case SDL_HAT_UP:
556             updateInput(button_up, player, 1, false);
557             updateInput(button_down, player, 0, false);
558             updateInput(button_left, player, 0, false);
559             updateInput(button_right, player, 0, false);
560             break;
561 
562          case SDL_HAT_DOWN:
563             updateInput(button_up, player, 0, false);
564             updateInput(button_down, player, 1, false);
565             updateInput(button_left, player, 0, false);
566             updateInput(button_right, player, 0, false);
567             break;
568 
569          case SDL_HAT_LEFT:
570             updateInput(button_up, player, 0, false);
571             updateInput(button_down, player, 0, false);
572             updateInput(button_left, player, 1, false);
573             updateInput(button_right, player, 0, false);
574             break;
575 
576          case SDL_HAT_RIGHT:
577             updateInput(button_up, player, 0, false);
578             updateInput(button_down, player, 0, false);
579             updateInput(button_left, player, 0, false);
580             updateInput(button_right, player, 1, false);
581             break;
582 
583          case SDL_HAT_RIGHTDOWN:
584             updateInput(button_up, player, 0, false);
585             updateInput(button_down, player, 1, false);
586             updateInput(button_left, player, 0, false);
587             updateInput(button_right, player, 1, false);
588             break;
589 
590          case SDL_HAT_RIGHTUP:
591             updateInput(button_up, player, 1, false);
592             updateInput(button_down, player, 0, false);
593             updateInput(button_left, player, 0, false);
594             updateInput(button_right, player, 1, false);
595             break;
596 
597          case SDL_HAT_LEFTUP:
598             updateInput(button_up, player, 1, false);
599             updateInput(button_down, player, 0, false);
600             updateInput(button_left, player, 1, false);
601             updateInput(button_right, player, 0, false);
602             break;
603 
604          case SDL_HAT_LEFTDOWN:
605             updateInput(button_up, player, 0, false);
606             updateInput(button_down, player, 1, false);
607             updateInput(button_left, player, 1, false);
608             updateInput(button_right, player, 0, false);
609             break;
610          }
611       }
612       break;
613 
614       case SDL_JOYBUTTONDOWN:
615          if (IFTRACES)
616          {
617             log_debug("Joystick %d button %d down\n",
618                       e.jbutton.which, e.jbutton.button);
619          }
620          switch (e.jbutton.button)
621          {
622          case 0:
623             updateInput(button_a, getPlayerFromJoystickPort(e.jbutton.which), 1, false);
624             break;
625 
626          case 1:
627             updateInput(button_b, getPlayerFromJoystickPort(e.jbutton.which), 1, false);
628             break;
629 
630          case 2:
631             updateInput(button_x, getPlayerFromJoystickPort(e.jbutton.which), 1, false);
632             break;
633 
634          case 3:
635             updateInput(button_y, getPlayerFromJoystickPort(e.jbutton.which), 1, false);
636             break;
637 
638          case 4:
639             updateInput(button_l, getPlayerFromJoystickPort(e.jbutton.which), 1, false);
640             break;
641 
642          case 5:
643             updateInput(button_r, getPlayerFromJoystickPort(e.jbutton.which), 1, false);
644             break;
645 
646          case 6:
647          case 8:
648             updateInput(button_select, getPlayerFromJoystickPort(e.jbutton.which), 1, false);
649             anySelectButtonPushedMask = anySelectButtonPushedMask | (1 << e.jbutton.which);
650             break;
651 
652          case 7:
653          case 9:
654             updateInput(button_start, getPlayerFromJoystickPort(e.jbutton.which), 1, false);
655             anyStartButtonPushedMask = anyStartButtonPushedMask | (1 << e.jbutton.which);
656             break;
657 
658          case 10:
659             updateInput(button_up, getPlayerFromJoystickPort(e.jbutton.which), 1, false);
660             anyStartButtonPushedMask = anyStartButtonPushedMask & ~(1 << e.jbutton.which);
661             break;
662 
663          case 11:
664             updateInput(button_down, getPlayerFromJoystickPort(e.jbutton.which), 1, false);
665             anyStartButtonPushedMask = anyStartButtonPushedMask & ~(1 << e.jbutton.which);
666             break;
667 
668          case 12:
669             updateInput(button_left, getPlayerFromJoystickPort(e.jbutton.which), 1, false);
670             anyStartButtonPushedMask = anyStartButtonPushedMask & ~(1 << e.jbutton.which);
671             break;
672 
673          case 13:
674             updateInput(button_right, getPlayerFromJoystickPort(e.jbutton.which), 1, false);
675             anyStartButtonPushedMask = anyStartButtonPushedMask & ~(1 << e.jbutton.which);
676             break;
677          }
678          anyButtonPushedMask = anyButtonPushedMask | (1 << e.jbutton.button);
679          break;
680 
681       case SDL_JOYBUTTONUP:
682          if (IFTRACES)
683          {
684             log_debug("Joystick %d button %d up\n",
685                       e.jbutton.which, e.jbutton.button);
686          }
687          switch (e.jbutton.button)
688          {
689          case 0:
690             updateInput(button_a, getPlayerFromJoystickPort(e.jbutton.which), 0, false);
691             break;
692 
693          case 1:
694             updateInput(button_b, getPlayerFromJoystickPort(e.jbutton.which), 0, false);
695             break;
696 
697          case 2:
698             updateInput(button_x, getPlayerFromJoystickPort(e.jbutton.which), 0, false);
699             break;
700 
701          case 3:
702             updateInput(button_y, getPlayerFromJoystickPort(e.jbutton.which), 0, false);
703             break;
704 
705          case 4:
706             updateInput(button_l, getPlayerFromJoystickPort(e.jbutton.which), 0, false);
707             break;
708 
709          case 5:
710             updateInput(button_r, getPlayerFromJoystickPort(e.jbutton.which), 0, false);
711             break;
712 
713          case 6:
714          case 8:
715             updateInput(button_select, getPlayerFromJoystickPort(e.jbutton.which), 0, false);
716             anySelectButtonPushedMask = anySelectButtonPushedMask & ~(1 << e.jbutton.which);
717             break;
718 
719          case 7:
720          case 9:
721             updateInput(button_start, getPlayerFromJoystickPort(e.jbutton.which), 0, false);
722             anyStartButtonPushedMask = anyStartButtonPushedMask & ~(1 << e.jbutton.which);
723             break;
724 
725          case 10:
726             updateInput(button_up, getPlayerFromJoystickPort(e.jbutton.which), 0, false);
727             anyStartButtonPushedMask = anyStartButtonPushedMask & ~(1 << e.jbutton.which);
728             break;
729 
730          case 11:
731             updateInput(button_down, getPlayerFromJoystickPort(e.jbutton.which), 0, false);
732             anyStartButtonPushedMask = anyStartButtonPushedMask & ~(1 << e.jbutton.which);
733             break;
734 
735          case 12:
736             updateInput(button_left, getPlayerFromJoystickPort(e.jbutton.which), 0, false);
737             anyStartButtonPushedMask = anyStartButtonPushedMask & ~(1 << e.jbutton.which);
738             break;
739 
740          case 13:
741             updateInput(button_right, getPlayerFromJoystickPort(e.jbutton.which), 0, false);
742             anyStartButtonPushedMask = anyStartButtonPushedMask & ~(1 << e.jbutton.which);
743             break;
744          }
745          anyButtonPushedMask = anyButtonPushedMask & ~(1 << e.jbutton.button);
746          break;
747 
748       case SDL_KEYDOWN:
749          updateKeyboard(e.key.keysym.scancode, 1);
750          break;
751 
752       case SDL_KEYUP:
753          updateKeyboard(e.key.keysym.scancode, 0);
754          break;
755 
756       case SDL_FINGERDOWN:
757       case SDL_MOUSEBUTTONDOWN:
758          break;
759 
760       case SDL_QUIT:
761          done = SDL_TRUE;
762          break;
763 
764       default:
765          break;
766       }
767    }
768 }
769 void
loop()770 loop()
771 {
772 
773    if (isGameActive())
774    {
775       beeingPlaying++;
776       if (beeingPlaying > BEEING_PLAYING_BUFFER)
777       {
778          if (anyStartButtonPushedMask && anySelectButtonPushedMask)
779          {
780             pressESC();
781          }
782          else
783          {
784             if (anyStartButtonPushedMask)
785             {
786                anyStartButtonPushedCounter++;
787                if (anyStartButtonPushedCounter == 1)
788                {
789                   pauseGameButton();
790                }
791             }
792             else
793             {
794                anyStartButtonPushedCounter = 0;
795             }
796          }
797       }
798    }
799    else
800    {
801       beeingPlaying = 0;
802    }
803 
804    if (checkDemoMode) {
805       static int zz = 0;
806       static int frameResult[16] = {
807          195663,60551,77663,53283,45147,79144,75015,149583,65639,142559,182999,132883,172439,123599,202267,112111
808       };
809       static bool isActive = false;
810       bool currentActive = !inTheMenu();
811 
812       if (!currentActive && isActive) {
813          int fn = frameNumber();
814          if (frameResult[zz] != fn) {
815             log_error("Failed check on demo %d\n", zz+1);
816             exit(1);
817          } else {
818             log_info("Checked demo %d/16 %d:OK\n", zz+1, fn);
819          }
820          zz++;
821          if ( zz == 16 ) {
822             log_info("SUCCESS check demos\n");
823             exit(0);
824          }
825       }
826       isActive = currentActive;
827       turboMode = MAX_TURBO;
828    }
829 
830    pollEvent();
831 
832 
833    mrboom_deal_with_autofire();
834    mrboom_loop();
835 
836 #ifdef DEBUG
837    if (mrboom_debug_state_failed())
838    {
839       log_error("mrboom_debug_state_failed!\n");
840       exit(1);
841    }
842 #endif
843 
844    if (m.executionFinished)
845    {
846       done = SDL_TRUE;
847    }
848 
849    mrboom_sound();
850 
851    bool        skipRendering = false;
852    static bool showFrame[MAX_TURBO][MAX_TURBO];
853    static bool calculateShowFrame = true;
854    int         counter            = 0;
855 
856 #define INT_SCALING    100000
857    if (calculateShowFrame)
858    {
859       for (int i = 0; i < MAX_TURBO; i++)
860       {
861          int delta = ((MAX_TURBO - i) * INT_SCALING) / MAX_TURBO;        // between 100 and 0
862          for (int j = 0; j < MAX_TURBO; j++)
863          {
864             counter += delta;
865             if (counter >= INT_SCALING)
866             {
867                counter        -= INT_SCALING;
868                showFrame[i][j] = true;
869             }
870             else
871             {
872                showFrame[i][j] = false;
873             }
874          }
875       }
876       calculateShowFrame = false;
877    }
878 
879    static int anyButtonPushedMaskSave = 0;
880    bool       goTurbo = false;
881    if (!anyButtonPushedMask)
882    {
883       anyButtonPushedMaskSave = 0;
884    }
885    if (someHumanPlayersNotDead())
886    {
887       anyButtonPushedMaskSave = anyButtonPushedMask;
888    }
889    else
890    {
891       if (!isAboutToWin() && isGameActive())
892       {
893          if (anyButtonPushedMaskSave != anyButtonPushedMask)                   // to avoid speeding straight away when still holding current keys
894          {
895             goTurbo = true;
896          }
897       }
898    }
899    if (goTurbo)
900    {
901       if (!turboMode)
902       {
903          turboMode = 1;
904       }
905       if (!(frameNumber() % 32) && (turboMode < MAX_TURBO / 2))
906       {
907          turboMode++;
908       }
909    }
910    else
911    {
912       if (!(frameNumber() % 32) && (turboMode))
913       {
914          turboMode--;
915       }
916    }
917 
918    int phaseShowFrame = turboMode ? turboMode + MAX_TURBO / 2 : 0;
919    if (phaseShowFrame > MAX_TURBO - 1)
920    {
921       phaseShowFrame = MAX_TURBO - 1;
922    }
923    if (showFrame[phaseShowFrame][frameNumber() % MAX_TURBO])
924    {
925       skipRendering = false;
926    }
927    else
928    {
929       skipRendering = true;
930    }
931    static bool previousSkipRendering = false;
932 
933    if ((isGameActive() == false) || isGamePaused())
934    {
935       previousSkipRendering = false;
936       turboMode             = 0;
937    }
938    if (noVGA == SDL_FALSE)
939    {
940       UpdateTexture(texture, previousSkipRendering);
941       previousSkipRendering = skipRendering;
942    }
943    if ((noVGA == SDL_FALSE) && skipRendering == false)
944    {
945       SDL_RenderClear(renderer);
946       SDL_RenderCopy(renderer, texture, NULL, NULL);
947       SDL_RenderPresent(renderer);
948    }
949 
950 #ifdef __EMSCRIPTEN__
951    if (done)
952    {
953       emscripten_cancel_main_loop();
954    }
955 #endif
956 }
957 
fps()958 static void fps()
959 {
960    nbFrames++;
961    if (!(nbFrames % 600) && (IFTRACES))
962    {
963       clock_t end        = clock();
964       double  time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
965       log_debug("x time_spent=%f %d\n", time_spent, nbFrames);
966       log_debug("x fps=%f\n", nbFrames / time_spent);
967    }
968 }
969 
printKeys()970 void printKeys()
971 {
972    log_info("Keys   Space - Add a bomberman bot\n");
973    log_info("       Return - Start a game\n");
974    log_info("       P or Pause/Break - Pause\n");
975    log_info("       ESC - Quit\n");
976    log_info("1st player\n");
977    log_info("       Left - Left\n");
978    log_info("       Right - Right\n");
979    log_info("       Up - Up\n");
980    log_info("       Down - Down\n");
981 #ifdef __APPLE__
982    log_info("       Right Ctrl, End, Keypad 0 or Right Command - Lay bomb\n");
983 #else
984    log_info("       Right Ctrl, End, Keypad 0 or Right GUI - Lay bomb\n");
985 #endif
986    log_info("       Right Alt, Keypad Dot or PageDown - Ignite bomb\n");
987    log_info("       Right Shift, Keypad Enter or Home - Jump\n");
988    log_info("2nd player\n");
989    log_info("       A - Left\n");
990    log_info("       D - Right\n");
991    log_info("       W - Up\n");
992    log_info("       S - Down\n");
993 #ifdef __APPLE__
994    log_info("       Left Ctrl or Left Command - Lay bomb\n");
995 #else
996    log_info("       Left Ctrl or Left GUI - Lay bomb\n");
997 #endif
998    log_info("       Left Alt - Ignite bomb\n");
999    log_info("       Left Shift - Jump\n\n");
1000    log_info("run with -h to list options\n");
1001 }
1002 
manageTestAI()1003 void manageTestAI()
1004 {
1005    static bool doItOnce  = true;
1006    static bool doItOnce2 = true;
1007 
1008    if (isGameActive() == false)
1009    {
1010       if (numberOfPlayers() != testAI)
1011       {
1012          addOneAIPlayer();
1013       }
1014       else
1015       {
1016          if (doItOnce)
1017          {
1018             pressStart();
1019             doItOnce = false;
1020          }
1021       }
1022    }
1023    else
1024    {
1025       if (cheatMode && doItOnce2)
1026       {
1027          activeCheatMode();
1028          doItOnce2 = false;
1029       }
1030    }
1031 }
1032 
1033 int  exitAtFrameNumber = 0;
1034 bool exitAtFrame       = false;
1035 int
main(int argc,char ** argv)1036 main(int argc, char **argv)
1037 {
1038    bool showVersion = false;
1039    int  c;
1040 
1041    while (1)
1042    {
1043       static struct option long_options[] =
1044       {
1045          { "fx",          required_argument, 0, 'f' },
1046          { "help",        no_argument,       0, 'h' },
1047          { "level",       required_argument, 0, 'l' },
1048          { "nomonster",   no_argument,       0, 'm' },
1049          { "sex",         no_argument,       0, 's' },
1050          { "color",       no_argument,       0, 'c' },
1051          { "noautofire",  no_argument,       0, 'n' },
1052          { "version",     no_argument,       0, 'v' },
1053          { "debugtraces", required_argument, 0, 'd' },
1054          { "cheat",       no_argument,       0, '1' },
1055          { "slow",        no_argument,       0, '2' },
1056          { "frame",       required_argument, 0, '3' },
1057          { "exit",        required_argument, 0, '4' },
1058          { "tracemask",   required_argument, 0, 't' },
1059          { "aitest",      required_argument, 0, 'a' },
1060          { "nomusic",     no_argument,       0, 'z' },
1061          { "xbrz",        required_argument, 0, 'x' },
1062          { "skynet",      no_argument,       0, 'k' },
1063          { "deadzone",    required_argument, 0, 'd' },
1064          { "checkDemoMode",      no_argument,       0, 'e' },
1065          {             0,                 0, 0,   0 }
1066       };
1067       /* getopt_long stores the option index here. */
1068       int option_index = 0;
1069 
1070       c = getopt_long(argc, argv, "hl:mscv123:4:t:f:o:a:nzx:kd:e",
1071                       long_options, &option_index);
1072 
1073       /* Detect the end of the options. */
1074       if (c == -1)
1075       {
1076          break;
1077       }
1078       switch (c)
1079       {
1080       case 'h':
1081          log_info("Usage: mrboom [options]\n");
1082          log_info("Options:\n");
1083          log_info("  -f <x>, --fx <x>\t\tFX volume: from 0 to 10\n");
1084          log_info("  -h, --help     \t\tShow summary of options\n");
1085          log_info("  -l <x>, --level <x>\t\tStart in level 0:Candy 1:Pinguine 2:Pink\n");
1086          log_info("                     \t\t3:Jungle 4:Board 5:Soccer 6:Sky 7:Aliens\n");
1087          log_info("  -m, --nomonster\t\tNo monster mode\n");
1088          log_info("  -s, --sex     \t\tSex team mode\n");
1089          log_info("  -c, --color     \t\tColor team mode\n");
1090          log_info("  -k, --skynet     \t\tHumans vs. machines mode\n");
1091 //         log_info("  -n, --noautofire     \t\tNo autofire for bomb drop\n");
1092          log_info("  -d <x>, --deadzone <x>\tSet joysticks dead zone, default is %d\n", DEFAULT_DEAD_ZONE);
1093          log_info("  -z, --nomusic     \t\tNo music\n");
1094          log_info("  -v, --version  \t\tDisplay version\n");
1095 #ifdef DEBUG
1096          log_info("Debugging options:\n");
1097          log_info("  -x <x>, --xbrz <x>\t\tSet xBRZ shader factor: from 1 to 6 (default is %d, 1 is off)\n", XBRZ_DEFAULT_FACTOR);
1098          log_info("  -o <x>, --output <x>\t\tDebug traces to <x> file\n");
1099          log_info("  -t <x>, --tracemask <x>\tDebug traces mask <x>:\n");
1100          log_info("                  \t\t1 to 128 player selection bit\n");
1101          log_info("                  \t\t256 Grids\n");
1102          log_info("                  \t\t512 Bot tree decisions\n");
1103          log_info("                  \t\t1024 SDL2 stuff\n");
1104 
1105          for (int i = 0; i < nb_dyna; i++)
1106          {
1107             const char *desc[nb_dyna] = { "white male", "white female", "red male", "red female", "blue male", "blue female", "green male", "green female" };
1108 
1109             log_info("                  \t\t%d all traces %s (#%d)\n", 256 + 512 + (1 << i), desc[i], i);
1110          }
1111 
1112          log_info("  -1, --cheat    \t\tActivate F1/F2 and L1/L2 pad key for debugging\n");
1113          log_info("  -2, --slow    \t\tSlow motion for AI debugging\n");
1114          log_info("  -3 <x>, --frame <x>    \tSet frame for randomness debugging\n");
1115          log_info("  -4 <x>, --exit <x>    \tExit at frame <x> use x = -1 to exit after one game\n");
1116          log_info("  -a <x>, --aitest <x>    \tTest <x> AI players\n");
1117 #endif
1118          exit(0);
1119          break;
1120 
1121       case 't':
1122          traceMask = atoi(optarg);
1123          log_info("-t option given. Set tracemask to %d.\n", traceMask);
1124          break;
1125 
1126       case 'f':
1127          sdl2_fx_volume = atoi(optarg);
1128          if ((sdl2_fx_volume < 0) || (sdl2_fx_volume > 10))
1129          {
1130             sdl2_fx_volume = DEFAULT_SDL2_FX_VOLUME;
1131          }
1132          log_info("-f option given. Set fx volume to %d.\n", sdl2_fx_volume);
1133          break;
1134 
1135       case 'n':
1136          log_info("-n option given. No autofire\n");
1137          setAutofire(false);
1138          break;
1139 
1140       case 'v':
1141          log_info("%s %s\n", GAME_NAME, GAME_VERSION);
1142          showVersion = true;
1143          break;
1144 
1145       case 'l':
1146 #define NB_LEVELS    8
1147          log_info("-l option given. choosing level %s.\n", optarg);
1148          chooseLevel(atoi(optarg) % NB_LEVELS);
1149          break;
1150 
1151       case 'm':
1152          log_info("-m option given. No monster mode.\n");
1153          setNoMonsterMode(true);
1154          break;
1155 
1156       case 'o':
1157          log_info("logging to file %s\n", optarg);
1158          logDebug = fopen(optarg, "w");
1159          break;
1160 
1161       case '1':
1162          log_info("-1 option given. Activate F1/F2 and L1/L2 pad key for debugging.\n");
1163          cheatMode = true;
1164          break;
1165 
1166       case '3':
1167          log_info("-3 option given. Set frame to %s.\n", optarg);
1168          setFrameNumber(atoi(optarg));
1169          break;
1170 
1171       case '4':
1172          log_info("-4 option given. Exit at frame %s.\n", optarg);
1173          exitAtFrameNumber = atoi(optarg);
1174          exitAtFrame       = true;
1175          break;
1176 
1177       case 'a':
1178          log_info("-a option given. Test mode for %s AI players.\n", optarg);
1179          testAI = atoi(optarg);
1180          break;
1181 
1182       case 'c':
1183          setTeamMode(1);
1184          log_info("-c option given. Color team mode.\n");
1185          break;
1186 
1187       case 's':
1188          setTeamMode(2);
1189          log_info("-s option given. Sex team mode.\n");
1190          break;
1191 
1192       case 'k':
1193          setTeamMode(4);
1194          log_info("-k option given. Skynet team mode.\n");
1195          break;
1196 
1197       case '2':
1198          log_info("-2 option given. Slow motion for AI debugging.\n");
1199          slowMode = true;
1200          break;
1201 
1202       case 'z':
1203          music = false;
1204          break;
1205 
1206       case 'x':
1207          log_info("-x xBRZ shader factor to %s.\n", optarg);
1208          xbrzFactor = atoi(optarg);
1209          if (xbrzFactor < 1)
1210          {
1211             xbrzFactor = 1;
1212          }
1213          if (xbrzFactor > 6)
1214          {
1215             xbrzFactor = 6;
1216          }
1217          break;
1218 
1219       case 'd':
1220          joystickDeadZone = atoi(optarg);
1221          log_info("-d Joystick deadzone to %d.\n", joystickDeadZone);
1222          break;
1223       case 'e':
1224          log_info("-e Check Demo Mode.\n");
1225          checkDemoMode = true;
1226         // m.temps_avant_demo = 10;
1227          break;
1228 
1229       default:
1230          exit(1);
1231       }
1232    }
1233 #if 0
1234    logDebug = fopen("/tmp/mrboom.log", "w");
1235 #endif
1236    /* Enable standard application logging */
1237    SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
1238    SDL_EventState(SDL_JOYAXISMOTION, SDL_ENABLE);
1239    SDL_EventState(SDL_JOYBALLMOTION, SDL_ENABLE);
1240    SDL_EventState(SDL_JOYHATMOTION, SDL_ENABLE);
1241    SDL_EventState(SDL_JOYBUTTONDOWN, SDL_ENABLE);
1242    SDL_EventState(SDL_JOYBUTTONUP, SDL_ENABLE);
1243 
1244    SDL_JoystickEventState(SDL_ENABLE);
1245 
1246    if ((exitAtFrame) && (exitAtFrameNumber == -1))
1247    {
1248       log_info("No VGA\n");
1249       noVGA = SDL_TRUE;
1250    }
1251 
1252    if ((noVGA == SDL_FALSE) && (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0))
1253    {
1254       log_error("Couldn't initialize SDL: %s\n", SDL_GetError());
1255       noVGA = SDL_TRUE;
1256       if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
1257       {
1258          log_error("Couldn't initialize SDL_INIT_JOYSTICK: %s\n", SDL_GetError());
1259          return(1);
1260       }
1261    }
1262 
1263    if (!mrboom_init())
1264    {
1265       log_error("Error in init.\n");
1266       quit(1);
1267    }
1268    if (showVersion)
1269    {
1270       quit(0);
1271    }
1272    printKeys();
1273 #define RESIZABLE
1274    if (noVGA == SDL_FALSE)
1275    {
1276       /* Create the window and renderer */
1277 #ifndef FULLSCREEN
1278 #ifdef RESIZABLE
1279       window = SDL_CreateWindow(GAME_NAME,
1280                                 SDL_WINDOWPOS_UNDEFINED,
1281                                 SDL_WINDOWPOS_UNDEFINED,
1282                                 WIDTH * 3, HEIGHT * 3,
1283                                 SDL_WINDOW_RESIZABLE
1284                                 );
1285       windowID = SDL_GetWindowID(window);
1286 #else
1287       SDL_DisplayMode DM;
1288       SDL_GetCurrentDisplayMode(0, &DM);
1289       int displayWidth  = DM.w;
1290       int displayHeight = DM.h;
1291 
1292       float aspectRatio = (float)displayWidth / (float)displayHeight;
1293       float height;
1294       float width;
1295       if (aspectRatio < ASPECT_RATIO)
1296       {
1297          width  = displayWidth;
1298          height = (1.f / ASPECT_RATIO) * displayWidth;
1299       }
1300       else
1301       {
1302          width  = ASPECT_RATIO * displayHeight;
1303          height = displayHeight;
1304       }
1305 
1306       window = SDL_CreateWindow(GAME_NAME,
1307                                 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
1308                                 width, height,
1309                                 SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
1310                                 );
1311 #endif
1312 #else
1313 // FULLSCREEN
1314       window = SDL_CreateWindow(GAME_NAME,
1315                                 SDL_WINDOWPOS_CENTERED,
1316                                 SDL_WINDOWPOS_CENTERED,
1317                                 WIDTH, HEIGHT,
1318                                 SDL_WINDOW_FULLSCREEN | SDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS | SDL_WINDOW_INPUT_GRABBED
1319                                 );
1320 #endif
1321 
1322 
1323       if (!window)
1324       {
1325          log_error("Couldn't set create window: %s\n", SDL_GetError());
1326          quit(3);
1327       }
1328       SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
1329 
1330       renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);
1331       if (!renderer)
1332       {
1333          log_error("Couldn't set create renderer: %s\n", SDL_GetError());
1334          quit(4);
1335       }
1336 
1337       widthTexture  = WIDTH * xbrzFactor;
1338       heightTexture = HEIGHT * xbrzFactor;
1339       texture       = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, widthTexture, heightTexture);
1340       if (!texture)
1341       {
1342          log_error("Couldn't set create texture: %s\n", SDL_GetError());
1343          quit(5);
1344       }
1345    }
1346 
1347    /* Loop, waiting for QUIT or the escape key */
1348 
1349 #ifdef __EMSCRIPTEN__
1350    emscripten_set_main_loop(loop, 0, 1);
1351 #else
1352    begin = clock();
1353    while (!done)
1354    {
1355       if (resizeDone)
1356       {
1357          SDL_SetWindowSize(window, width, height);
1358          resizeDone = false;
1359       }
1360       loop();
1361 #ifdef DEBUG
1362       fps();
1363       if (testAI)
1364       {
1365          manageTestAI();
1366       }
1367       if (slowMode)
1368       {
1369          usleep(100000);
1370       }
1371       if ((exitAtFrame) && (frameNumber() == exitAtFrameNumber))
1372       {
1373          log_info("Exit at frame\n");
1374          exit(0);
1375       }
1376       if ((exitAtFrame) && (exitAtFrameNumber == -1))
1377       {
1378          static bool first = false;
1379          if (isGameActive() == true)
1380          {
1381             first = true;
1382          }
1383          else
1384          {
1385             if (first)
1386             {
1387                log_info("Exit at frame\n");
1388                for (int i = 0; i < nb_dyna; i++)
1389                {
1390                   if (victories(i))
1391                   {
1392                      log_info("team %d won\n", teamOfPlayer(i));
1393                      exit(i);
1394                   }
1395                }
1396                log_info("draw game\n");
1397                exit(nb_dyna);
1398             }
1399          }
1400       }
1401 #endif
1402    }
1403 #endif
1404 
1405    if (noVGA == SDL_FALSE)
1406    {
1407       SDL_DestroyRenderer(renderer);
1408    }
1409    fps();
1410    log_debug("quit(0)\n");
1411    quit(0);
1412    return 0;
1413 }
1414