|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- /* Copyright (C) 2014-2019 Ben Kurtovic <ben.kurtovic@gmail.com>
- Released under the terms of the MIT License. See LICENSE for details. */
-
- #include <signal.h>
- #include <stdint.h>
- #include <unistd.h>
- #include <SDL.h>
-
- #include "emulator.h"
- #include "config.h"
- #include "gamegear.h"
- #include "logging.h"
- #include "save.h"
- #include "util.h"
-
- typedef struct {
- SDL_GameController **items;
- int num, capacity;
- } Controllers;
-
- typedef struct {
- GameGear *gg;
- SDL_Window *window;
- SDL_Renderer *renderer;
- SDL_Texture *texture;
- uint32_t *pixels;
- Controllers controllers;
- } Emulator;
-
- static Emulator emu;
-
- /*
- Signal handler for SIGINT. Tells the GameGear to power off, if it exists.
- */
- static void handle_sigint(int sig)
- {
- (void) sig;
- if (emu.gg)
- gamegear_power_off(emu.gg); // Safe!
- }
-
- /*
- Get the name of a SDL game controller.
- */
- static const char *get_controller_name(SDL_GameController *controller)
- {
- const char *name = SDL_GameControllerName(controller);
- if (name)
- return name;
- return "(UNKNOWN)";
- }
-
- /*
- Set up SDL for accepting controller input.
- */
- static void setup_input()
- {
- if (!access(CONTROLLER_DB_PATH, R_OK)) {
- if (SDL_GameControllerAddMappingsFromFile(CONTROLLER_DB_PATH) < 0)
- ERROR("SDL failed to load controller mappings: %s", SDL_GetError());
- }
-
- int i, c = 0, n = SDL_NumJoysticks();
- if (n <= 0)
- return;
-
- SDL_GameController **items = cr_calloc(n, sizeof(SDL_GameController*));
- for (i = 0; i < n; i++) {
- if (!SDL_IsGameController(i)) {
- DEBUG("Ignoring joystick %i: not a game controller", i);
- continue;
- }
-
- SDL_GameController *controller = SDL_GameControllerOpen(i);
- if (!controller) {
- ERROR("SDL failed to open controller %i: %s", i, SDL_GetError());
- continue;
- }
- DEBUG("Loaded controller %i: %s", i, get_controller_name(controller));
- items[c++] = controller;
- }
-
- emu.controllers.items = items;
- emu.controllers.num = c;
- emu.controllers.capacity = n;
- }
-
- /*
- Set up SDL for drawing the game.
- */
- static void setup_graphics(bool fullscreen, unsigned scale)
- {
- SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
-
- uint32_t flags;
- if (fullscreen)
- flags = SDL_WINDOW_FULLSCREEN_DESKTOP;
- else
- flags = SDL_WINDOW_RESIZABLE;
-
- SDL_CreateWindowAndRenderer(
- scale * GG_SCREEN_WIDTH, scale * GG_SCREEN_HEIGHT,
- flags, &emu.window, &emu.renderer);
-
- if (!emu.window)
- FATAL("SDL failed to create a window: %s", SDL_GetError());
- if (!emu.renderer)
- FATAL("SDL failed to create a renderer: %s", SDL_GetError());
-
- emu.texture = SDL_CreateTexture(emu.renderer, SDL_PIXELFORMAT_ARGB8888,
- SDL_TEXTUREACCESS_STREAMING, GG_SCREEN_WIDTH, GG_SCREEN_HEIGHT);
-
- if (!emu.texture)
- FATAL("SDL failed to create a texture: %s", SDL_GetError());
-
- emu.pixels = cr_malloc(
- sizeof(uint32_t) * GG_SCREEN_WIDTH * GG_SCREEN_HEIGHT);
-
- SDL_RenderSetLogicalSize(emu.renderer, GG_SCREEN_WIDTH, GG_SCREEN_HEIGHT);
- SDL_SetTextureBlendMode(emu.texture, SDL_BLENDMODE_BLEND);
- SDL_SetWindowTitle(emu.window, "crater");
- SDL_ShowCursor(SDL_DISABLE);
- SDL_GL_SetSwapInterval(1); // Vsync
-
- SDL_SetRenderDrawColor(emu.renderer, 0x00, 0x00, 0x00, 0xFF);
- SDL_RenderClear(emu.renderer);
- SDL_RenderPresent(emu.renderer);
- }
-
- /*
- Set up SDL.
- */
- static void setup_sdl(bool fullscreen, unsigned scale)
- {
- if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_GAMECONTROLLER) < 0)
- FATAL("SDL failed to initialize: %s", SDL_GetError());
-
- setup_input();
- setup_graphics(fullscreen, scale);
- }
-
- /*
- Actually send the pixel data to the screen.
- */
- static void draw_frame()
- {
- SDL_UpdateTexture(emu.texture, NULL, emu.pixels,
- GG_SCREEN_WIDTH * sizeof(uint32_t));
- SDL_SetRenderDrawColor(emu.renderer, 0x00, 0x00, 0x00, 0xFF);
- SDL_RenderClear(emu.renderer);
- SDL_RenderCopy(emu.renderer, emu.texture, NULL, NULL);
- SDL_RenderPresent(emu.renderer);
- }
-
- /*
- Handle a keyboard press; translate it into a Game Gear button press.
- */
- static void handle_keypress(GameGear *gg, SDL_Keycode key, bool state)
- {
- GGButton button;
- switch (key) {
- case SDLK_UP:
- case SDLK_w:
- button = BUTTON_UP; break;
- case SDLK_DOWN:
- case SDLK_s:
- button = BUTTON_DOWN; break;
- case SDLK_LEFT:
- case SDLK_a:
- button = BUTTON_LEFT; break;
- case SDLK_RIGHT:
- case SDLK_d:
- button = BUTTON_RIGHT; break;
- case SDLK_j:
- case SDLK_z:
- case SDLK_PERIOD:
- button = BUTTON_TRIGGER_1; break;
- case SDLK_k:
- case SDLK_x:
- case SDLK_SLASH:
- button = BUTTON_TRIGGER_2; break;
- case SDLK_RETURN:
- case SDLK_RETURN2:
- case SDLK_ESCAPE:
- button = BUTTON_START; break;
- default:
- return;
- }
- gamegear_input(gg, button, state);
- }
-
- /*
- Handle controller input.
- */
- static void handle_controller_input(
- GameGear *gg, SDL_GameControllerButton input, bool state)
- {
- GGButton button;
- switch (input) {
- case SDL_CONTROLLER_BUTTON_A:
- button = BUTTON_TRIGGER_1; break;
- case SDL_CONTROLLER_BUTTON_B:
- button = BUTTON_TRIGGER_2; break;
- case SDL_CONTROLLER_BUTTON_START:
- button = BUTTON_START; break;
- case SDL_CONTROLLER_BUTTON_DPAD_UP:
- button = BUTTON_UP; break;
- case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
- button = BUTTON_DOWN; break;
- case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
- button = BUTTON_LEFT; break;
- case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
- button = BUTTON_RIGHT; break;
- default:
- return;
- }
- gamegear_input(gg, button, state);
- }
-
- /*
- Handle a controller being added.
- */
- static void handle_controller_added(int i)
- {
- SDL_Joystick *joy = SDL_JoystickOpen(i);
- SDL_JoystickID id = SDL_JoystickInstanceID(joy);
-
- for (int i = 0; i < emu.controllers.num; i++) {
- SDL_Joystick *other = SDL_GameControllerGetJoystick(
- emu.controllers.items[i]);
- if (SDL_JoystickInstanceID(other) == id) {
- TRACE("SDL added duplicate joystick %i", i);
- SDL_JoystickClose(joy);
- return;
- }
- }
-
- SDL_JoystickClose(joy);
- if (!SDL_IsGameController(i)) {
- DEBUG("SDL added joystick %i: not a game controller", i);
- return;
- }
-
- SDL_GameController *controller = SDL_GameControllerOpen(i);
- if (!controller) {
- ERROR("SDL failed to open controller %i: %s", i, SDL_GetError());
- return;
- }
-
- DEBUG("Added controller %i: %s", i, get_controller_name(controller));
- if (emu.controllers.num == 0) {
- int n = 4;
- emu.controllers.items = cr_calloc(n, sizeof(SDL_GameController*));
- emu.controllers.capacity = n;
- }
- else if (emu.controllers.num >= emu.controllers.capacity) {
- emu.controllers.capacity *= 2;
- emu.controllers.items = cr_realloc(emu.controllers.items,
- emu.controllers.capacity * sizeof(SDL_GameController*));
- }
- emu.controllers.items[emu.controllers.num++] = controller;
- }
-
- /*
- Handle a controller being removed.
- */
- static void handle_controller_removed(int id)
- {
- for (int i = 0; i < emu.controllers.num; i++) {
- SDL_GameController *controller = emu.controllers.items[i];
- SDL_Joystick *joy = SDL_GameControllerGetJoystick(controller);
- if (!joy)
- continue;
-
- if (SDL_JoystickInstanceID(joy) == id) {
- DEBUG("Removed controller %i: %s", i,
- get_controller_name(controller));
- SDL_GameControllerClose(controller);
- for (; i < emu.controllers.num; i++)
- emu.controllers.items[i] = emu.controllers.items[i + 1];
- emu.controllers.num--;
- return;
- }
- }
- DEBUG("SDL removed unknown controller: %i", id)
- }
-
- /*
- Handle SDL events, mainly quit events and button presses.
- */
- static void handle_events(GameGear *gg)
- {
- SDL_Event event;
- while (SDL_PollEvent(&event)) {
- switch (event.type) {
- case SDL_QUIT:
- gamegear_power_off(gg);
- return;
- case SDL_KEYDOWN:
- handle_keypress(gg, event.key.keysym.sym, true);
- break;
- case SDL_KEYUP:
- handle_keypress(gg, event.key.keysym.sym, false);
- break;
- case SDL_CONTROLLERBUTTONDOWN:
- handle_controller_input(gg, event.cbutton.button, true);
- break;
- case SDL_CONTROLLERBUTTONUP:
- handle_controller_input(gg, event.cbutton.button, false);
- break;
- case SDL_CONTROLLERDEVICEADDED:
- handle_controller_added(event.cdevice.which);
- break;
- case SDL_CONTROLLERDEVICEREMOVED:
- handle_controller_removed(event.cdevice.which);
- break;
- }
- }
- }
-
- /*
- GameGear callback: Draw the current frame and handle SDL event logic.
- */
- static void frame_callback(GameGear *gg)
- {
- draw_frame();
- handle_events(gg);
- }
-
- /*
- Clean up SDL stuff allocated in setup_sdl().
- */
- static void cleanup_sdl()
- {
- free(emu.pixels);
- SDL_DestroyTexture(emu.texture);
- SDL_DestroyRenderer(emu.renderer);
- SDL_DestroyWindow(emu.window);
- for (int i = 0; i < emu.controllers.num; i++)
- SDL_GameControllerClose(emu.controllers.items[i]);
- if (emu.controllers.items)
- free(emu.controllers.items);
- SDL_Quit();
-
- emu.window = NULL;
- emu.renderer = NULL;
- emu.texture = NULL;
- emu.controllers.items = NULL;
- emu.controllers.num = emu.controllers.capacity = 0;
- }
-
- /*
- Emulate a ROM in a Game Gear while handling I/O with the host computer.
-
- Block until emulation is finished.
- */
- void emulate(ROM *rom, Config *config)
- {
- Save save;
- if (!config->no_saving) {
- if (!save_init(&save, config->sav_path, rom))
- return;
- }
-
- BIOS *bios = NULL;
- if (config->bios_path) {
- if (!(bios = bios_open(config->bios_path)))
- return;
- }
-
- emu.gg = gamegear_create();
- signal(SIGINT, handle_sigint);
- setup_sdl(config->fullscreen, config->scale);
-
- gamegear_attach_callback(emu.gg, frame_callback);
- gamegear_attach_display(emu.gg, emu.pixels);
- gamegear_load_rom(emu.gg, rom);
- if (bios)
- gamegear_load_bios(emu.gg, bios);
- if (!config->no_saving)
- gamegear_load_save(emu.gg, &save);
-
- gamegear_simulate(emu.gg);
-
- if (gamegear_get_exception(emu.gg))
- ERROR("caught exception: %s", gamegear_get_exception(emu.gg))
- else
- WARN("caught signal, stopping...")
- if (DEBUG_LEVEL)
- gamegear_print_state(emu.gg);
-
- cleanup_sdl();
- signal(SIGINT, SIG_DFL);
- gamegear_destroy(emu.gg);
- emu.gg = NULL;
- if (!config->no_saving)
- save_free(&save);
- }
|