cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

application.cpp (15860B)


      1/*
      2 * Gearboy - Nintendo Game Boy Emulator
      3 * Copyright (C) 2012  Ignacio Sanchez
      4
      5 * This program is free software: you can redistribute it and/or modify
      6 * it under the terms of the GNU General Public License as published by
      7 * the Free Software Foundation, either version 3 of the License, or
      8 * any later version.
      9
     10 * This program is distributed in the hope that it will be useful,
     11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     13 * GNU General Public License for more details.
     14
     15 * You should have received a copy of the GNU General Public License
     16 * along with this program.  If not, see http://www.gnu.org/licenses/
     17 *
     18 */
     19
     20#include <SDL.h>
     21#include "imgui/imgui.h"
     22#include "imgui/imgui_impl_sdl.h"
     23#include "emu.h"
     24#include "gui.h"
     25#include "config.h"
     26#include "renderer.h"
     27
     28#define APPLICATION_IMPORT
     29#include "application.h"
     30
     31static SDL_Window* sdl_window;
     32static SDL_GLContext gl_context;
     33static bool running = true;
     34static bool paused_when_focus_lost = false;
     35static Uint64 frame_time_start;
     36static Uint64 frame_time_end;
     37
     38static int sdl_init(void);
     39static void sdl_destroy(void);
     40static void sdl_events(void);
     41static void sdl_events_emu(const SDL_Event* event);
     42static void sdl_shortcuts_gui(const SDL_Event* event);
     43static void run_emulator(void);
     44static void render(void);
     45static void frame_throttle(void);
     46
     47int application_init(const char* arg)
     48{
     49    Log ("<·> %s %s Desktop App <·>", GEARBOY_TITLE, GEARBOY_VERSION);
     50
     51    if (IsValidPointer(arg) && (strlen(arg) > 0))
     52    {
     53        Log ("Loading with argv: %s");
     54    }
     55
     56    int ret = sdl_init();
     57
     58    application_fullscreen = false;
     59    
     60    config_init();
     61    config_read();
     62
     63    emu_init();
     64
     65    strcpy(emu_savefiles_path, config_emulator.savefiles_path.c_str());
     66    strcpy(emu_savestates_path, config_emulator.savestates_path.c_str());
     67    emu_savefiles_dir_option = config_emulator.savefiles_dir_option;
     68    emu_savestates_dir_option = config_emulator.savestates_dir_option;
     69
     70    gui_init();
     71
     72    ImGui_ImplSDL2_InitForOpenGL(sdl_window, gl_context);
     73
     74    renderer_init();
     75
     76    SDL_GL_SetSwapInterval(config_video.sync ? 1 : 0);
     77
     78    if (IsValidPointer(arg) && (strlen(arg) > 0))
     79    {
     80        gui_load_rom(arg);
     81    }
     82
     83    return ret;
     84}
     85
     86void application_destroy(void)
     87{
     88    config_write();
     89    config_destroy();
     90    renderer_destroy();
     91    gui_destroy();
     92    emu_destroy();
     93    sdl_destroy();
     94}
     95
     96void application_mainloop(void)
     97{
     98    while (running)
     99    {
    100        frame_time_start = SDL_GetPerformanceCounter();
    101        sdl_events();
    102        run_emulator();
    103        render();
    104        frame_time_end = SDL_GetPerformanceCounter();
    105        frame_throttle();
    106    }
    107}
    108
    109void application_trigger_quit(void)
    110{
    111    SDL_Event event;
    112    event.type = SDL_QUIT;
    113    SDL_PushEvent(&event);
    114}
    115
    116void application_trigger_fullscreen(bool fullscreen)
    117{
    118    SDL_SetWindowFullscreen(sdl_window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
    119}
    120
    121static int sdl_init(void)
    122{
    123    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0)
    124    {
    125        Log("Error: %s\n", SDL_GetError());
    126        return -1;
    127    }
    128
    129    SDL_VERSION(&application_sdl_build_version);
    130    SDL_GetVersion(&application_sdl_link_version);
    131
    132    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    133    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
    134    SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
    135    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
    136    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
    137    SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
    138    sdl_window = SDL_CreateWindow(GEARBOY_TITLE " " GEARBOY_VERSION, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 700, window_flags);
    139    gl_context = SDL_GL_CreateContext(sdl_window);
    140    SDL_GL_MakeCurrent(sdl_window, gl_context);
    141    SDL_GL_SetSwapInterval(0);
    142
    143    SDL_SetWindowMinimumSize(sdl_window, 644, 602);
    144
    145    application_gamepad_mappings = SDL_GameControllerAddMappingsFromRW(SDL_RWFromFile("gamecontrollerdb.txt", "rb"), 1);
    146
    147    if (application_gamepad_mappings > 0)
    148    {
    149        Log("Succesfuly loaded %d game controller mappings", application_gamepad_mappings);
    150    }
    151    else
    152    {
    153        Log("Game controller database not found!");
    154    }
    155
    156    for (int i = 0; i < SDL_NumJoysticks(); ++i)
    157    {
    158        if (SDL_IsGameController(i))
    159        {
    160            application_gamepad = SDL_GameControllerOpen(i);
    161            if(!application_gamepad)
    162            {
    163                Log("Warning: Unable to open game controller! SDL Error: %s\n", SDL_GetError());
    164            }
    165            else
    166            {
    167                Log("Game controller %d correctly detected", i);
    168            }
    169
    170            break;
    171        }
    172    }
    173
    174    int w, h;
    175    int display_w, display_h;
    176    SDL_GetWindowSize(sdl_window, &w, &h);
    177    SDL_GL_GetDrawableSize(sdl_window, &display_w, &display_h);
    178    
    179    if (w > 0 && h > 0)
    180    {
    181        float scale_w = (float)display_w / w;
    182        float scale_h = (float)display_h / h;
    183
    184        application_display_scale = (scale_w > scale_h) ? scale_w : scale_h;
    185    }
    186
    187    SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
    188
    189    return 0;
    190}
    191
    192static void sdl_destroy(void)
    193{
    194    SDL_GameControllerClose(application_gamepad);
    195    ImGui_ImplSDL2_Shutdown();
    196    SDL_GL_DeleteContext(gl_context);
    197    SDL_DestroyWindow(sdl_window);
    198    SDL_Quit();
    199}
    200
    201static void sdl_events(void)
    202{
    203    SDL_Event event;
    204        
    205    while (SDL_PollEvent(&event))
    206    {
    207        if (event.type == SDL_QUIT)
    208        {
    209            running = false;
    210            break;
    211        }
    212
    213        ImGui_ImplSDL2_ProcessEvent(&event);
    214
    215        if (!gui_in_use)
    216        {
    217            sdl_events_emu(&event);
    218            sdl_shortcuts_gui(&event);
    219        }
    220    }
    221}
    222
    223static void sdl_events_emu(const SDL_Event* event)
    224{
    225    switch(event->type)
    226    {
    227        case (SDL_DROPFILE):
    228        {
    229            char* dropped_filedir = event->drop.file;
    230            gui_load_rom(dropped_filedir);
    231            SDL_free(dropped_filedir);    // Free dropped_filedir memory
    232            break;
    233        }
    234        case SDL_WINDOWEVENT:
    235        {
    236            switch (event->window.event)
    237            {
    238                case SDL_WINDOWEVENT_FOCUS_GAINED:
    239                {
    240                    if (!paused_when_focus_lost)
    241                        emu_resume();
    242                }
    243                break;
    244
    245                case SDL_WINDOWEVENT_FOCUS_LOST:
    246                {
    247                    paused_when_focus_lost = emu_is_paused();
    248                    emu_pause();
    249                }
    250                break;
    251            }
    252        }
    253        break;
    254
    255        case SDL_CONTROLLERBUTTONDOWN:
    256        {
    257            if (!config_input.gamepad)
    258                break;
    259            
    260            if (event->cbutton.button == config_input.gamepad_b)
    261                emu_key_pressed(B_Key);
    262            else if (event->cbutton.button == config_input.gamepad_a)
    263                emu_key_pressed(A_Key);
    264            else if (event->cbutton.button == config_input.gamepad_select)
    265                emu_key_pressed(Select_Key);
    266            else if (event->cbutton.button == config_input.gamepad_start)
    267                emu_key_pressed(Start_Key);
    268            
    269            if (config_input.gamepad_directional == 1)
    270                break;
    271            
    272            if (event->cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
    273                emu_key_pressed(Up_Key);
    274            else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
    275                emu_key_pressed(Down_Key);
    276            else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
    277                emu_key_pressed(Left_Key);
    278            else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
    279                emu_key_pressed(Right_Key);
    280        }
    281        break;
    282
    283        case SDL_CONTROLLERBUTTONUP:
    284        {
    285            if (!config_input.gamepad)
    286                break;
    287
    288            if (event->cbutton.button == config_input.gamepad_b)
    289                emu_key_released(B_Key);
    290            else if (event->cbutton.button == config_input.gamepad_a)
    291                emu_key_released(A_Key);
    292            else if (event->cbutton.button == config_input.gamepad_select)
    293                emu_key_released(Select_Key);
    294            else if (event->cbutton.button == config_input.gamepad_start)
    295                emu_key_released(Start_Key);
    296            
    297            if (config_input.gamepad_directional == 1)
    298                break;
    299            
    300            if (event->cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_UP)
    301                emu_key_released(Up_Key);
    302            else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN)
    303                emu_key_released(Down_Key);
    304            else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT)
    305                emu_key_released(Left_Key);
    306            else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT)
    307                emu_key_released(Right_Key);
    308        }
    309        break;
    310
    311        case SDL_CONTROLLERAXISMOTION:
    312        {
    313            if (!config_input.gamepad)
    314                break;
    315            
    316            if (config_input.gamepad_directional == 0)
    317                break;
    318
    319            const int STICK_DEAD_ZONE = 8000;
    320                
    321            if(event->caxis.axis == config_input.gamepad_x_axis)
    322            {
    323                int x_motion = event->caxis.value * (config_input.gamepad_invert_x_axis ? -1 : 1);
    324
    325                if (x_motion < -STICK_DEAD_ZONE)
    326                    emu_key_pressed(Left_Key);
    327                else if (x_motion > STICK_DEAD_ZONE)
    328                    emu_key_pressed(Right_Key);
    329                else
    330                {
    331                    emu_key_released(Left_Key);
    332                    emu_key_released(Right_Key);
    333                }
    334            }
    335            else if(event->caxis.axis == config_input.gamepad_y_axis)
    336            {
    337                int y_motion = event->caxis.value * (config_input.gamepad_invert_y_axis ? -1 : 1);
    338                
    339                if (y_motion < -STICK_DEAD_ZONE)
    340                    emu_key_pressed(Up_Key);
    341                else if (y_motion > STICK_DEAD_ZONE)
    342                    emu_key_pressed(Down_Key);
    343                else
    344                {
    345                    emu_key_released(Up_Key);
    346                    emu_key_released(Down_Key);
    347                }
    348            }
    349        }
    350        break;
    351
    352        case SDL_KEYDOWN:
    353        {
    354            if (event->key.keysym.mod & KMOD_CTRL)
    355                break;
    356
    357            int key = event->key.keysym.scancode;
    358
    359            if (key == SDL_SCANCODE_ESCAPE)
    360            {
    361                application_trigger_quit();
    362                break;
    363            }
    364
    365            if (key == SDL_SCANCODE_F11)
    366            {
    367                application_fullscreen = !application_fullscreen;
    368                application_trigger_fullscreen(application_fullscreen);
    369                break;
    370            }
    371
    372            if (key == config_input.key_left)
    373                emu_key_pressed(Left_Key);
    374            else if (key == config_input.key_right)
    375                emu_key_pressed(Right_Key);
    376            else if (key == config_input.key_up)
    377                emu_key_pressed(Up_Key);
    378            else if (key == config_input.key_down)
    379                emu_key_pressed(Down_Key);
    380            else if (key == config_input.key_b)
    381                emu_key_pressed(B_Key);
    382            else if (key == config_input.key_a)
    383                emu_key_pressed(A_Key);
    384            else if (key == config_input.key_select)
    385                emu_key_pressed(Select_Key);
    386            else if (key == config_input.key_start)
    387                emu_key_pressed(Start_Key);
    388        }
    389        break;
    390
    391        case SDL_KEYUP:
    392        {
    393            int key = event->key.keysym.scancode;
    394
    395            if (key == config_input.key_left)
    396                emu_key_released(Left_Key);
    397            else if (key == config_input.key_right)
    398                emu_key_released(Right_Key);
    399            else if (key == config_input.key_up)
    400                emu_key_released(Up_Key);
    401            else if (key == config_input.key_down)
    402                emu_key_released(Down_Key);
    403            else if (key == config_input.key_b)
    404                emu_key_released(B_Key);
    405            else if (key == config_input.key_a)
    406                emu_key_released(A_Key);
    407            else if (key == config_input.key_select)
    408                emu_key_released(Select_Key);
    409            else if (key == config_input.key_start)
    410                emu_key_released(Start_Key);
    411        }
    412        break;
    413    }
    414}
    415
    416static void sdl_shortcuts_gui(const SDL_Event* event)
    417{
    418    if ((event->type == SDL_KEYDOWN) && (event->key.keysym.mod & KMOD_CTRL))
    419    {
    420        int key = event->key.keysym.scancode;
    421        
    422        switch (key)
    423        {
    424            case SDL_SCANCODE_O:
    425                gui_shortcut(gui_ShortcutOpenROM);
    426                break;
    427            case SDL_SCANCODE_R:
    428                gui_shortcut(gui_ShortcutReset);
    429                break;
    430            case SDL_SCANCODE_P:
    431                gui_shortcut(gui_ShortcutPause);
    432                break;
    433            case SDL_SCANCODE_F:
    434                gui_shortcut(gui_ShortcutFFWD);
    435                break;
    436            case SDL_SCANCODE_L:
    437                gui_shortcut(gui_ShortcutLoadState);
    438                break;
    439            case SDL_SCANCODE_S:
    440                gui_shortcut(gui_ShortcutSaveState);
    441                break;
    442            case SDL_SCANCODE_M:
    443                gui_shortcut(gui_ShortcutShowMainMenu);
    444                break;
    445            case SDL_SCANCODE_F5:
    446                gui_shortcut(gui_ShortcutDebugContinue);
    447                break;
    448            case SDL_SCANCODE_F6:
    449                gui_shortcut(gui_ShortcutDebugNextFrame);
    450                break;
    451            case SDL_SCANCODE_F8:
    452                gui_shortcut(gui_ShortcutDebugRuntocursor);
    453                break;
    454            case SDL_SCANCODE_F9:
    455                gui_shortcut(gui_ShortcutDebugBreakpoint);
    456                break;
    457            case SDL_SCANCODE_F10:
    458                gui_shortcut(gui_ShortcutDebugStep);
    459                break;
    460            case SDL_SCANCODE_BACKSPACE:
    461                gui_shortcut(gui_ShortcutDebugGoBack);
    462                break;
    463        }
    464    }
    465}
    466
    467static void run_emulator(void)
    468{
    469    if (!emu_is_empty())
    470    {
    471        static int i = 0;
    472        i++;
    473
    474        if (i > 20)
    475        {
    476            i = 0;
    477
    478            char title[256];
    479            sprintf(title, "%s %s - %s", GEARBOY_TITLE, GEARBOY_VERSION, emu_get_core()->GetCartridge()->GetFileName());
    480            SDL_SetWindowTitle(sdl_window, title);
    481        }
    482    }
    483    config_emulator.paused = emu_is_paused();
    484    emu_audio_sync = config_audio.sync;
    485    emu_update();
    486}
    487
    488static void render(void)
    489{
    490    renderer_begin_render();
    491    ImGui_ImplSDL2_NewFrame(sdl_window);  
    492    gui_render();
    493    renderer_render();
    494    renderer_end_render();
    495
    496    SDL_GL_SwapWindow(sdl_window);
    497}
    498
    499static void frame_throttle(void)
    500{
    501    if (emu_is_empty() || emu_is_paused() || config_emulator.ffwd)
    502    {
    503        float elapsed = (float)((frame_time_end - frame_time_start) * 1000) / SDL_GetPerformanceFrequency();
    504
    505        float min = 16.666f;
    506
    507        if (config_emulator.ffwd)
    508        {
    509            switch (config_emulator.ffwd_speed)
    510            {
    511                case 0:
    512                    min = 16.666f / 1.5f;
    513                    break;
    514                case 1: 
    515                    min = 16.666f / 2.0f;
    516                    break;
    517                case 2:
    518                    min = 16.666f / 2.5f;
    519                    break;
    520                case 3:
    521                    min = 16.666f / 3.0f;
    522                    break;
    523                default:
    524                    min = 0.0f;
    525            }
    526        }
    527
    528        if (elapsed < min)
    529            SDL_Delay((Uint32)(min - elapsed));
    530    }
    531}