3246 lines
102 KiB
C++
3246 lines
102 KiB
C++
/*
|
|
This file is part of Flycast.
|
|
|
|
Flycast is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Flycast is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <cstdio>
|
|
#include <cstdarg>
|
|
#include <math.h>
|
|
#include "types.h"
|
|
#ifndef _WIN32
|
|
#include <sys/time.h>
|
|
#endif
|
|
#include <mutex>
|
|
|
|
#ifdef __SWITCH__
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "nswitch.h"
|
|
#endif
|
|
|
|
#include <sys/stat.h>
|
|
#include <file/file_path.h>
|
|
|
|
#include <libretro.h>
|
|
|
|
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
|
|
#include <glsm/glsm.h>
|
|
#include "wsi/gl_context.h"
|
|
#endif
|
|
#ifdef HAVE_VULKAN
|
|
#include "rend/vulkan/vulkan_context.h"
|
|
#include <libretro_vulkan.h>
|
|
#endif
|
|
#ifdef HAVE_D3D11
|
|
#include <libretro_d3d.h>
|
|
#include "rend/dx11/dx11context_lr.h"
|
|
#endif
|
|
#include "emulator.h"
|
|
#include "hw/sh4/sh4_mem.h"
|
|
#include "hw/sh4/sh4_sched.h"
|
|
#include "hw/sh4/dyna/blockmanager.h"
|
|
#include "keyboard_map.h"
|
|
#include "hw/maple/maple_cfg.h"
|
|
#include "hw/maple/maple_if.h"
|
|
#include "hw/maple/maple_cfg.h"
|
|
#include "hw/pvr/spg.h"
|
|
#include "hw/naomi/naomi_cart.h"
|
|
#include "hw/naomi/card_reader.h"
|
|
#include "imgread/common.h"
|
|
#include "LogManager.h"
|
|
#include "cheats.h"
|
|
#include "rend/CustomTexture.h"
|
|
#include "rend/osd.h"
|
|
#include "cfg/option.h"
|
|
#include "version.h"
|
|
#include "rend/transform_matrix.h"
|
|
|
|
constexpr char slash = path_default_slash_c();
|
|
|
|
#define RETRO_DEVICE_TWINSTICK RETRO_DEVICE_SUBCLASS( RETRO_DEVICE_JOYPAD, 1 )
|
|
#define RETRO_DEVICE_TWINSTICK_SATURN RETRO_DEVICE_SUBCLASS( RETRO_DEVICE_JOYPAD, 2 )
|
|
#define RETRO_DEVICE_ASCIISTICK RETRO_DEVICE_SUBCLASS( RETRO_DEVICE_JOYPAD, 3 )
|
|
|
|
#define RETRO_ENVIRONMENT_RETROARCH_START_BLOCK 0x800000
|
|
|
|
#define RETRO_ENVIRONMENT_SET_SAVE_STATE_IN_BACKGROUND (2 | RETRO_ENVIRONMENT_RETROARCH_START_BLOCK)
|
|
/* bool * --
|
|
* Boolean value that tells the front end to save states in the
|
|
* background or not.
|
|
*/
|
|
|
|
#define RETRO_ENVIRONMENT_POLL_TYPE_OVERRIDE (4 | RETRO_ENVIRONMENT_RETROARCH_START_BLOCK)
|
|
/* unsigned * --
|
|
* Tells the frontend to override the poll type behavior.
|
|
* Allows the frontend to influence the polling behavior of the
|
|
* frontend.
|
|
*
|
|
* Will be unset when retro_unload_game is called.
|
|
*
|
|
* 0 - Don't Care, no changes, frontend still determines polling type behavior.
|
|
* 1 - Early
|
|
* 2 - Normal
|
|
* 3 - Late
|
|
*/
|
|
|
|
#include "libretro_core_option_defines.h"
|
|
#include "libretro_core_options.h"
|
|
#include "vmu_xhair.h"
|
|
|
|
extern void retro_audio_init(void);
|
|
extern void retro_audio_deinit(void);
|
|
extern void retro_audio_flush_buffer(void);
|
|
extern void retro_audio_upload(void);
|
|
|
|
std::string arcadeFlashPath;
|
|
static bool boot_to_bios;
|
|
|
|
static bool devices_need_refresh = false;
|
|
static int device_type[4] = {-1,-1,-1,-1};
|
|
static int astick_deadzone = 0;
|
|
static int trigger_deadzone = 0;
|
|
static bool digital_triggers = false;
|
|
static bool allow_service_buttons = false;
|
|
static bool haveCardReader;
|
|
|
|
static bool libretro_supports_bitmasks = false;
|
|
|
|
static bool categoriesSupported = false;
|
|
static bool platformIsDreamcast = true;
|
|
static bool platformIsArcade = false;
|
|
static bool threadedRenderingEnabled = true;
|
|
static bool oitEnabled = false;
|
|
static bool autoSkipFrameEnabled = false;
|
|
#ifdef _OPENMP
|
|
static bool textureUpscaleEnabled = false;
|
|
#endif
|
|
static bool vmuScreenSettingsShown = true;
|
|
static bool lightgunSettingsShown = true;
|
|
|
|
u32 kcode[4] = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
|
|
u8 rt[4];
|
|
u8 lt[4];
|
|
u32 vks[4];
|
|
s8 joyx[4], joyy[4];
|
|
s8 joyrx[4], joyry[4];
|
|
// Mouse buttons
|
|
// bit 0: Button C
|
|
// bit 1: Right button (B)
|
|
// bit 2: Left button (A)
|
|
// bit 3: Wheel button
|
|
u8 mo_buttons[4] = { 0xFF, 0xFF, 0xFF, 0xFF };
|
|
// Relative mouse coordinates [-512:511]
|
|
float mo_x_delta[4];
|
|
float mo_y_delta[4];
|
|
float mo_wheel_delta[4];
|
|
std::mutex relPosMutex;
|
|
// Absolute mouse coordinates
|
|
// Range [0:639] [0:479]
|
|
// but may be outside this range if the pointer is offscreen or outside the 4:3 window.
|
|
s32 mo_x_abs[4];
|
|
s32 mo_y_abs[4];
|
|
|
|
static bool enable_purupuru = true;
|
|
static u32 vib_stop_time[4];
|
|
static double vib_strength[4];
|
|
static double vib_delta[4];
|
|
|
|
unsigned per_content_vmus = 0;
|
|
|
|
static bool first_run = true;
|
|
static bool rotate_screen;
|
|
static bool rotate_game;
|
|
static int framebufferWidth;
|
|
static int framebufferHeight;
|
|
static int maxFramebufferWidth;
|
|
static int maxFramebufferHeight;
|
|
static float framebufferAspectRatio = 4.f / 3.f;
|
|
|
|
float libretro_expected_audio_samples_per_run;
|
|
unsigned libretro_vsync_swap_interval = 1;
|
|
bool libretro_detect_vsync_swap_interval = false;
|
|
|
|
static retro_perf_callback perf_cb;
|
|
static retro_get_cpu_features_t perf_get_cpu_features_cb;
|
|
|
|
// Callbacks
|
|
static retro_log_printf_t log_cb;
|
|
static retro_video_refresh_t video_cb;
|
|
static retro_input_poll_t poll_cb;
|
|
static retro_input_state_t input_cb;
|
|
retro_audio_sample_batch_t audio_batch_cb;
|
|
retro_environment_t environ_cb;
|
|
|
|
static retro_rumble_interface rumble;
|
|
|
|
static void refresh_devices(bool first_startup);
|
|
static void init_disk_control_interface();
|
|
static bool read_m3u(const char *file);
|
|
void UpdateInputState();
|
|
void gui_display_notification(const char *msg, int duration);
|
|
static void updateVibration(u32 port, float power, float inclination, u32 durationMs);
|
|
|
|
static std::string game_data;
|
|
static char g_base_name[128];
|
|
static char game_dir[1024];
|
|
char game_dir_no_slash[1024];
|
|
char vmu_dir_no_slash[PATH_MAX];
|
|
char content_name[PATH_MAX];
|
|
static char g_roms_dir[PATH_MAX];
|
|
static std::mutex mtx_serialization;
|
|
static bool gl_ctx_resetting = false;
|
|
static bool is_dupe;
|
|
static u64 startTime;
|
|
|
|
// Disk swapping
|
|
static struct retro_disk_control_callback retro_disk_control_cb;
|
|
static struct retro_disk_control_ext_callback retro_disk_control_ext_cb;
|
|
static unsigned disk_initial_index = 0;
|
|
static std::string disk_initial_path;
|
|
static unsigned disk_index = 0;
|
|
static std::vector<std::string> disk_paths;
|
|
static std::vector<std::string> disk_labels;
|
|
static bool disc_tray_open = false;
|
|
|
|
void UpdateInputState();
|
|
static bool set_variable_visibility(void);
|
|
|
|
void retro_set_video_refresh(retro_video_refresh_t cb)
|
|
{
|
|
video_cb = cb;
|
|
}
|
|
|
|
void retro_set_audio_sample(retro_audio_sample_t cb)
|
|
{
|
|
// Nothing to do here
|
|
}
|
|
|
|
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)
|
|
{
|
|
audio_batch_cb = cb;
|
|
}
|
|
|
|
void retro_set_input_poll(retro_input_poll_t cb)
|
|
{
|
|
poll_cb = cb;
|
|
}
|
|
|
|
void retro_set_input_state(retro_input_state_t cb)
|
|
{
|
|
input_cb = cb;
|
|
}
|
|
|
|
static void input_set_deadzone_stick(int percent)
|
|
{
|
|
if (percent >= 0 && percent <= 100)
|
|
astick_deadzone = (int)(percent * 0.01f * 0x8000);
|
|
}
|
|
|
|
static void input_set_deadzone_trigger(int percent)
|
|
{
|
|
if (percent >= 0 && percent <= 100)
|
|
trigger_deadzone = (int)(percent * 0.01f * 0x8000);
|
|
}
|
|
|
|
void retro_set_environment(retro_environment_t cb)
|
|
{
|
|
environ_cb = cb;
|
|
|
|
// An annoyance: retro_set_environment() can be called
|
|
// multiple times, and depending upon the current frontend
|
|
// state various environment callbacks may be disabled.
|
|
// This means the reported 'categories_supported' status
|
|
// may change on subsequent iterations. We therefore have
|
|
// to record whether 'categories_supported' is true on any
|
|
// iteration, and latch the result
|
|
bool optionCategoriesSupported = false;
|
|
libretro_set_core_options(environ_cb, &optionCategoriesSupported);
|
|
categoriesSupported |= optionCategoriesSupported;
|
|
|
|
struct retro_core_options_update_display_callback update_display_cb;
|
|
update_display_cb.callback = set_variable_visibility;
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_UPDATE_DISPLAY_CALLBACK, &update_display_cb);
|
|
|
|
static const struct retro_controller_description ports_default[] =
|
|
{
|
|
{ "Controller", RETRO_DEVICE_JOYPAD },
|
|
{ "Arcade Stick", RETRO_DEVICE_ASCIISTICK },
|
|
{ "Keyboard", RETRO_DEVICE_KEYBOARD },
|
|
{ "Mouse", RETRO_DEVICE_MOUSE },
|
|
{ "Light Gun", RETRO_DEVICE_LIGHTGUN },
|
|
{ "Twin Stick", RETRO_DEVICE_TWINSTICK },
|
|
{ "Saturn Twin-Stick", RETRO_DEVICE_TWINSTICK_SATURN },
|
|
{ 0 },
|
|
};
|
|
static const struct retro_controller_info ports[] = {
|
|
{ ports_default, 7 },
|
|
{ ports_default, 7 },
|
|
{ ports_default, 7 },
|
|
{ ports_default, 7 },
|
|
{ 0 },
|
|
};
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
|
|
}
|
|
|
|
static void retro_keyboard_event(bool down, unsigned keycode, uint32_t character, uint16_t key_modifiers);
|
|
|
|
// Now comes the interesting stuff
|
|
void retro_init()
|
|
{
|
|
static bool emuInited;
|
|
|
|
// Logging
|
|
struct retro_log_callback log;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
|
|
log_cb = log.log;
|
|
else
|
|
log_cb = NULL;
|
|
LogManager::Init((void *)log_cb);
|
|
NOTICE_LOG(BOOT, "retro_init");
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_PERF_INTERFACE, &perf_cb))
|
|
perf_get_cpu_features_cb = perf_cb.get_cpu_features;
|
|
else
|
|
perf_get_cpu_features_cb = NULL;
|
|
|
|
// Set color mode
|
|
unsigned color_mode = RETRO_PIXEL_FORMAT_XRGB8888;
|
|
environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &color_mode);
|
|
|
|
init_kb_map();
|
|
struct retro_keyboard_callback kb_callback = { &retro_keyboard_event };
|
|
environ_cb(RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK, &kb_callback);
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL))
|
|
libretro_supports_bitmasks = true;
|
|
|
|
init_disk_control_interface();
|
|
retro_audio_init();
|
|
|
|
if (!_vmem_reserve())
|
|
ERROR_LOG(VMEM, "Cannot reserve memory space");
|
|
|
|
os_InstallFaultHandler();
|
|
MapleConfigMap::UpdateVibration = updateVibration;
|
|
|
|
#if defined(__GNUC__) && defined(__linux__) && !defined(__ANDROID__)
|
|
if (!emuInited)
|
|
#endif
|
|
emu.init();
|
|
emuInited = true;
|
|
}
|
|
|
|
void retro_deinit()
|
|
{
|
|
INFO_LOG(COMMON, "retro_deinit");
|
|
first_run = true;
|
|
|
|
//When auto-save states are enabled this is needed to prevent the core from shutting down before
|
|
//any save state actions are still running - which results in partial saves
|
|
{
|
|
std::lock_guard<std::mutex> lock(mtx_serialization);
|
|
}
|
|
os_UninstallFaultHandler();
|
|
|
|
#if defined(__GNUC__) && defined(__linux__) && !defined(__ANDROID__)
|
|
_vmem_release();
|
|
#else
|
|
emu.term();
|
|
#endif
|
|
libretro_supports_bitmasks = false;
|
|
categoriesSupported = false;
|
|
platformIsDreamcast = true;
|
|
platformIsArcade = false;
|
|
threadedRenderingEnabled = true;
|
|
oitEnabled = false;
|
|
autoSkipFrameEnabled = false;
|
|
#ifdef _OPENMP
|
|
textureUpscaleEnabled = false;
|
|
#endif
|
|
vmuScreenSettingsShown = true;
|
|
lightgunSettingsShown = true;
|
|
libretro_vsync_swap_interval = 1;
|
|
libretro_detect_vsync_swap_interval = false;
|
|
LogManager::Shutdown();
|
|
|
|
retro_audio_deinit();
|
|
}
|
|
|
|
static bool set_variable_visibility(void)
|
|
{
|
|
struct retro_core_option_display option_display;
|
|
struct retro_variable var;
|
|
bool updated = false;
|
|
|
|
bool platformWasDreamcast = platformIsDreamcast;
|
|
bool platformWasArcade = platformIsArcade;
|
|
|
|
platformIsDreamcast = settings.platform.isConsole();
|
|
platformIsArcade = settings.platform.isArcade();
|
|
|
|
// Show/hide platform-dependent options
|
|
if (first_run || (platformIsDreamcast != platformWasDreamcast) || (platformIsArcade != platformWasArcade))
|
|
{
|
|
// Show/hide NAOMI/Atomiswave options
|
|
option_display.visible = platformIsArcade;
|
|
option_display.key = CORE_OPTION_NAME "_allow_service_buttons";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
|
|
// Show/hide Dreamcast options
|
|
option_display.visible = platformIsDreamcast;
|
|
option_display.key = CORE_OPTION_NAME "_boot_to_bios";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
option_display.key = CORE_OPTION_NAME "_hle_bios";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
option_display.key = CORE_OPTION_NAME "_gdrom_fast_loading";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
option_display.key = CORE_OPTION_NAME "_cable_type";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
option_display.key = CORE_OPTION_NAME "_broadcast";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
option_display.key = CORE_OPTION_NAME "_language";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
option_display.key = CORE_OPTION_NAME "_force_wince";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
option_display.key = CORE_OPTION_NAME "_enable_purupuru";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
option_display.key = CORE_OPTION_NAME "_per_content_vmus";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
|
|
vmuScreenSettingsShown = option_display.visible;
|
|
for (unsigned i = 0; i < 4; i++)
|
|
{
|
|
char key[256];
|
|
option_display.key = key;
|
|
|
|
snprintf(key, sizeof(key), "%s%u%s", CORE_OPTION_NAME "_vmu", i + 1, "_screen_display");
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
snprintf(key, sizeof(key), "%s%u%s", CORE_OPTION_NAME "_vmu", i + 1, "_screen_position");
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
snprintf(key, sizeof(key), "%s%u%s", CORE_OPTION_NAME "_vmu", i + 1, "_screen_size_mult");
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
snprintf(key, sizeof(key), "%s%u%s", CORE_OPTION_NAME "_vmu", i + 1, "_pixel_on_color");
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
snprintf(key, sizeof(key), "%s%u%s", CORE_OPTION_NAME "_vmu", i + 1, "_pixel_off_color");
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
snprintf(key, sizeof(key), "%s%u%s", CORE_OPTION_NAME "_vmu", i + 1, "_screen_opacity");
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
}
|
|
|
|
// Show/hide manual option visibility toggles
|
|
// > Only show if categories are not supported
|
|
option_display.visible = platformIsDreamcast && !categoriesSupported;
|
|
option_display.key = CORE_OPTION_NAME "_show_vmu_screen_settings";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
|
|
updated = true;
|
|
}
|
|
|
|
// Show/hide additional manual option visibility toggles
|
|
// > Only show if categories are not supported
|
|
if (first_run)
|
|
{
|
|
option_display.visible = !categoriesSupported;
|
|
option_display.key = CORE_OPTION_NAME "_show_lightgun_settings";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
updated = true;
|
|
}
|
|
|
|
// Show/hide settings-dependent options
|
|
|
|
// Only for threaded renderer
|
|
bool threadedRenderingWasEnabled = threadedRenderingEnabled;
|
|
threadedRenderingEnabled = true;
|
|
var.key = CORE_OPTION_NAME "_threaded_rendering";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && !strcmp(var.value, "disabled"))
|
|
threadedRenderingEnabled = false;
|
|
|
|
if (first_run || (threadedRenderingEnabled != threadedRenderingWasEnabled))
|
|
{
|
|
option_display.visible = threadedRenderingEnabled;
|
|
option_display.key = CORE_OPTION_NAME "_auto_skip_frame";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
updated = true;
|
|
}
|
|
|
|
#if defined(HAVE_OIT) || defined(HAVE_VULKAN) || defined(HAVE_D3D11)
|
|
// Only for per-pixel renderers
|
|
bool oitWasEnabled = oitEnabled;
|
|
oitEnabled = false;
|
|
var.key = CORE_OPTION_NAME "_alpha_sorting";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && !strcmp(var.value, "per-pixel (accurate)"))
|
|
oitEnabled = true;
|
|
|
|
if (first_run || (oitEnabled != oitWasEnabled))
|
|
{
|
|
option_display.visible = oitEnabled;
|
|
option_display.key = CORE_OPTION_NAME "_oit_abuffer_size";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
option_display.key = CORE_OPTION_NAME "_oit_layers";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
updated = true;
|
|
}
|
|
#endif
|
|
|
|
#ifdef _OPENMP
|
|
// Only if texture upscaling is enabled
|
|
bool textureUpscaleWasEnabled = textureUpscaleEnabled;
|
|
textureUpscaleEnabled = false;
|
|
var.key = CORE_OPTION_NAME "_texupscale";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && strcmp(var.value, "off"))
|
|
textureUpscaleEnabled = true;
|
|
|
|
if (first_run || (textureUpscaleEnabled != textureUpscaleWasEnabled))
|
|
{
|
|
option_display.visible = textureUpscaleEnabled;
|
|
option_display.key = CORE_OPTION_NAME "_texupscale_max_filtered_texture_size";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
updated = true;
|
|
}
|
|
#endif
|
|
|
|
// Only if automatic frame skipping is disabled
|
|
bool autoSkipFrameWasEnabled = autoSkipFrameEnabled;
|
|
|
|
autoSkipFrameEnabled = false;
|
|
var.key = CORE_OPTION_NAME "_auto_skip_frame";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && strcmp(var.value, "disabled"))
|
|
autoSkipFrameEnabled = true;
|
|
|
|
if (first_run ||
|
|
(autoSkipFrameEnabled != autoSkipFrameWasEnabled) ||
|
|
(threadedRenderingEnabled != threadedRenderingWasEnabled))
|
|
{
|
|
option_display.visible = (!autoSkipFrameEnabled || !threadedRenderingEnabled);
|
|
option_display.key = CORE_OPTION_NAME "_detect_vsync_swap_interval";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
updated = true;
|
|
}
|
|
|
|
// If categories are supported, no further action is required
|
|
if (categoriesSupported)
|
|
return updated;
|
|
|
|
// Show/hide VMU screen options
|
|
bool vmuScreenSettingsWereShown = vmuScreenSettingsShown;
|
|
|
|
if (platformIsDreamcast)
|
|
{
|
|
vmuScreenSettingsShown = true;
|
|
var.key = CORE_OPTION_NAME "_show_vmu_screen_settings";
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && !strcmp(var.value, "disabled"))
|
|
vmuScreenSettingsShown = false;
|
|
}
|
|
else
|
|
vmuScreenSettingsShown = false;
|
|
|
|
if (first_run || (vmuScreenSettingsShown != vmuScreenSettingsWereShown))
|
|
{
|
|
option_display.visible = vmuScreenSettingsShown;
|
|
|
|
for (unsigned i = 0; i < 4; i++)
|
|
{
|
|
char key[256];
|
|
option_display.key = key;
|
|
|
|
snprintf(key, sizeof(key), "%s%u%s", CORE_OPTION_NAME "_vmu", i + 1, "_screen_display");
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
snprintf(key, sizeof(key), "%s%u%s", CORE_OPTION_NAME "_vmu", i + 1, "_screen_position");
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
snprintf(key, sizeof(key), "%s%u%s", CORE_OPTION_NAME "_vmu", i + 1, "_screen_size_mult");
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
snprintf(key, sizeof(key), "%s%u%s", CORE_OPTION_NAME "_vmu", i + 1, "_pixel_on_color");
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
snprintf(key, sizeof(key), "%s%u%s", CORE_OPTION_NAME "_vmu", i + 1, "_pixel_off_color");
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
snprintf(key, sizeof(key), "%s%u%s", CORE_OPTION_NAME "_vmu", i + 1, "_screen_opacity");
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
}
|
|
|
|
updated = true;
|
|
}
|
|
|
|
// Show/hide light gun options
|
|
bool lightgunSettingsWereShown = lightgunSettingsShown;
|
|
lightgunSettingsShown = true;
|
|
var.key = CORE_OPTION_NAME "_show_lightgun_settings";
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && !strcmp(var.value, "disabled"))
|
|
lightgunSettingsShown = false;
|
|
|
|
if (first_run || (lightgunSettingsShown != lightgunSettingsWereShown))
|
|
{
|
|
option_display.visible = lightgunSettingsShown;
|
|
|
|
for (unsigned i = 0; i < 4; i++)
|
|
{
|
|
char key[256];
|
|
option_display.key = key;
|
|
|
|
snprintf(key, sizeof(key), "%s%u%s", CORE_OPTION_NAME "_lightgun", i + 1, "_crosshair");
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
}
|
|
|
|
updated = true;
|
|
}
|
|
|
|
return updated;
|
|
}
|
|
|
|
static void setGameGeometry(retro_game_geometry& geometry)
|
|
{
|
|
geometry.aspect_ratio = framebufferAspectRatio;
|
|
if (rotate_screen)
|
|
geometry.aspect_ratio = 1 / geometry.aspect_ratio;
|
|
geometry.max_width = std::max(framebufferHeight * 16 / 9, framebufferWidth);
|
|
geometry.max_height = geometry.max_width;
|
|
// Avoid gigantic window size at startup
|
|
geometry.base_width = 640;
|
|
geometry.base_height = 480;
|
|
}
|
|
|
|
void setAVInfo(retro_system_av_info& avinfo)
|
|
{
|
|
double sample_rate = 44100.0;
|
|
double fps = SPG_CONTROL.NTSC ? 59.94 : SPG_CONTROL.PAL ? 50.0 : 60.0;
|
|
|
|
setGameGeometry(avinfo.geometry);
|
|
avinfo.timing.sample_rate = sample_rate;
|
|
avinfo.timing.fps = fps / (double)libretro_vsync_swap_interval;
|
|
|
|
libretro_expected_audio_samples_per_run = sample_rate / fps;
|
|
}
|
|
|
|
void retro_resize_renderer(int w, int h, float aspectRatio)
|
|
{
|
|
if (w == framebufferWidth && h == framebufferHeight && aspectRatio == framebufferAspectRatio)
|
|
return;
|
|
framebufferWidth = w;
|
|
framebufferHeight = h;
|
|
framebufferAspectRatio = aspectRatio;
|
|
bool avinfoNeeded = framebufferHeight > maxFramebufferHeight || framebufferWidth > maxFramebufferWidth;
|
|
maxFramebufferHeight = std::max(maxFramebufferHeight, framebufferHeight);
|
|
maxFramebufferWidth = std::max(maxFramebufferWidth, framebufferWidth);
|
|
|
|
if (avinfoNeeded)
|
|
{
|
|
retro_system_av_info avinfo;
|
|
setAVInfo(avinfo);
|
|
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avinfo);
|
|
}
|
|
else
|
|
{
|
|
retro_game_geometry geometry;
|
|
setGameGeometry(geometry);
|
|
environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &geometry);
|
|
}
|
|
}
|
|
|
|
static void setRotation()
|
|
{
|
|
int rotation = 0;
|
|
if (rotate_game)
|
|
{
|
|
if (!rotate_screen)
|
|
rotation = 1;
|
|
rotate_screen = !rotate_screen;
|
|
}
|
|
else
|
|
{
|
|
if (rotate_screen)
|
|
rotation = 3;
|
|
}
|
|
environ_cb(RETRO_ENVIRONMENT_SET_ROTATION, &rotation);
|
|
}
|
|
|
|
static void update_variables(bool first_startup)
|
|
{
|
|
bool wasThreadedRendering = config::ThreadedRendering;
|
|
bool prevRotateScreen = rotate_screen;
|
|
bool prevDetectVsyncSwapInterval = libretro_detect_vsync_swap_interval;
|
|
config::Settings::instance().setRetroEnvironment(environ_cb);
|
|
config::Settings::instance().setOptionDefinitions(option_defs_us);
|
|
config::Settings::instance().load(false);
|
|
|
|
retro_variable var;
|
|
|
|
var.key = CORE_OPTION_NAME "_per_content_vmus";
|
|
unsigned previous_per_content_vmus = per_content_vmus;
|
|
per_content_vmus = 0;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp("VMU A1", var.value))
|
|
per_content_vmus = 1;
|
|
else if (!strcmp("All VMUs", var.value))
|
|
per_content_vmus = 2;
|
|
}
|
|
if (!first_startup && per_content_vmus != previous_per_content_vmus
|
|
&& settings.platform.isConsole())
|
|
{
|
|
// Recreate the VMUs so that the save location is taken into account.
|
|
// Don't do this at startup because we don't know the system type yet
|
|
// and the VMUs haven't been created anyway
|
|
maple_ReconnectDevices();
|
|
}
|
|
|
|
var.key = CORE_OPTION_NAME "_screen_rotation";
|
|
rotate_screen = false;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && !strcmp("vertical", var.value))
|
|
rotate_screen = true;
|
|
|
|
var.key = CORE_OPTION_NAME "_internal_resolution";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
char str[100];
|
|
snprintf(str, sizeof(str), "%s", var.value);
|
|
|
|
char *pch = strtok(str, "x");
|
|
pch = strtok(NULL, "x");
|
|
if (pch != nullptr)
|
|
config::RenderResolution = strtoul(pch, NULL, 0);
|
|
|
|
DEBUG_LOG(COMMON, "Got height: %u", (int)config::RenderResolution);
|
|
}
|
|
|
|
var.key = CORE_OPTION_NAME "_boot_to_bios";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "enabled"))
|
|
boot_to_bios = true;
|
|
else if (!strcmp(var.value, "disabled"))
|
|
boot_to_bios = false;
|
|
}
|
|
else
|
|
boot_to_bios = false;
|
|
|
|
var.key = CORE_OPTION_NAME "_alpha_sorting";
|
|
var.value = nullptr;
|
|
RenderType previous_renderer = config::RendererType;
|
|
environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var);
|
|
if (var.value != nullptr && !strcmp(var.value, "per-pixel (accurate)"))
|
|
{
|
|
switch (config::RendererType)
|
|
{
|
|
case RenderType::Vulkan:
|
|
config::RendererType = RenderType::Vulkan_OIT;
|
|
break;
|
|
case RenderType::DirectX11:
|
|
config::RendererType = RenderType::DirectX11_OIT;
|
|
break;
|
|
case RenderType::OpenGL:
|
|
config::RendererType = RenderType::OpenGL_OIT;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
config::PerStripSorting = false; // Not used
|
|
}
|
|
else
|
|
{
|
|
switch (config::RendererType)
|
|
{
|
|
case RenderType::Vulkan_OIT:
|
|
config::RendererType = RenderType::Vulkan;
|
|
break;
|
|
case RenderType::DirectX11_OIT:
|
|
config::RendererType = RenderType::DirectX11;
|
|
break;
|
|
case RenderType::OpenGL_OIT:
|
|
config::RendererType = RenderType::OpenGL;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
config::PerStripSorting = var.value != nullptr && !strcmp(var.value, "per-strip (fast, least accurate)");
|
|
}
|
|
|
|
if (!first_startup && previous_renderer != config::RendererType) {
|
|
rend_term_renderer();
|
|
rend_init_renderer();
|
|
}
|
|
|
|
#if defined(HAVE_OIT) || defined(HAVE_VULKAN) || defined(HAVE_D3D11)
|
|
var.key = CORE_OPTION_NAME "_oit_abuffer_size";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "512MB"))
|
|
config::PixelBufferSize = 0x20000000u;
|
|
else if (!strcmp(var.value, "1GB"))
|
|
config::PixelBufferSize = 0x40000000u;
|
|
else if (!strcmp(var.value, "2GB"))
|
|
config::PixelBufferSize = 0x7ff00000u;
|
|
else if (!strcmp(var.value, "4GB"))
|
|
config::PixelBufferSize = 0xFFFFFFFFu;
|
|
else
|
|
config::PixelBufferSize = 0x20000000u;
|
|
}
|
|
else
|
|
config::PixelBufferSize = 0x20000000u;
|
|
#endif
|
|
|
|
if ((config::AutoSkipFrame != 0) && config::ThreadedRendering)
|
|
libretro_detect_vsync_swap_interval = false;
|
|
else
|
|
{
|
|
var.key = CORE_OPTION_NAME "_detect_vsync_swap_interval";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "enabled"))
|
|
libretro_detect_vsync_swap_interval = true;
|
|
else if (!strcmp(var.value, "disabled"))
|
|
libretro_detect_vsync_swap_interval = false;
|
|
}
|
|
else
|
|
libretro_detect_vsync_swap_interval = false;
|
|
}
|
|
|
|
if (first_startup)
|
|
{
|
|
if (config::ThreadedRendering)
|
|
{
|
|
bool save_state_in_background = true ;
|
|
unsigned poll_type_early = 1; /* POLL_TYPE_EARLY */
|
|
environ_cb(RETRO_ENVIRONMENT_SET_SAVE_STATE_IN_BACKGROUND, &save_state_in_background);
|
|
environ_cb(RETRO_ENVIRONMENT_POLL_TYPE_OVERRIDE, &poll_type_early);
|
|
}
|
|
|
|
config::Cable = 3;
|
|
var.key = CORE_OPTION_NAME "_cable_type";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp("VGA", var.value))
|
|
config::Cable = 0;
|
|
else if (!strcmp("TV (RGB)", var.value))
|
|
config::Cable = 2;
|
|
}
|
|
}
|
|
|
|
var.key = CORE_OPTION_NAME "_enable_purupuru";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (enable_purupuru != (strcmp("enabled", var.value) == 0) && settings.platform.isConsole())
|
|
{
|
|
enable_purupuru = strcmp("enabled", var.value) == 0;
|
|
for (int i = 0; i < MAPLE_PORTS; i++) {
|
|
if (config::MapleMainDevices[i] == MDT_SegaController)
|
|
config::MapleExpansionDevices[i][1] = enable_purupuru ? MDT_PurupuruPack : MDT_SegaVMU;
|
|
else if (config::MapleMainDevices[i] == MDT_LightGun || config::MapleMainDevices[i] == MDT_TwinStick
|
|
|| config::MapleMainDevices[i] == MDT_AsciiStick)
|
|
config::MapleExpansionDevices[i][0] = enable_purupuru ? MDT_PurupuruPack : MDT_SegaVMU;
|
|
}
|
|
|
|
if (!first_startup)
|
|
maple_ReconnectDevices();
|
|
}
|
|
}
|
|
|
|
var.key = CORE_OPTION_NAME "_analog_stick_deadzone";
|
|
var.value = NULL;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
input_set_deadzone_stick( atoi( var.value ) );
|
|
|
|
var.key = CORE_OPTION_NAME "_trigger_deadzone";
|
|
var.value = NULL;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
input_set_deadzone_trigger( atoi( var.value ) );
|
|
|
|
var.key = CORE_OPTION_NAME "_digital_triggers";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp("enabled", var.value))
|
|
digital_triggers = true;
|
|
else
|
|
digital_triggers = false;
|
|
}
|
|
else
|
|
digital_triggers = false;
|
|
|
|
var.key = CORE_OPTION_NAME "_allow_service_buttons";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp("enabled", var.value))
|
|
allow_service_buttons = true;
|
|
else
|
|
allow_service_buttons = false;
|
|
}
|
|
else
|
|
allow_service_buttons = false;
|
|
|
|
char key[256];
|
|
key[0] = '\0';
|
|
|
|
var.key = key ;
|
|
for (int i = 0 ; i < 4 ; i++)
|
|
{
|
|
lightgun_params[i].offscreen = true;
|
|
lightgun_params[i].x = 0;
|
|
lightgun_params[i].y = 0;
|
|
lightgun_params[i].dirty = true;
|
|
lightgun_params[i].colour = LIGHTGUN_COLOR_OFF;
|
|
|
|
snprintf(key, sizeof(key), CORE_OPTION_NAME "_lightgun%d_crosshair", i+1) ;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value )
|
|
{
|
|
if (!strcmp("disabled", var.value))
|
|
lightgun_params[i].colour = LIGHTGUN_COLOR_OFF;
|
|
else if (!strcmp("White", var.value))
|
|
lightgun_params[i].colour = LIGHTGUN_COLOR_WHITE;
|
|
else if (!strcmp("Red", var.value))
|
|
lightgun_params[i].colour = LIGHTGUN_COLOR_RED;
|
|
else if (!strcmp("Green", var.value))
|
|
lightgun_params[i].colour = LIGHTGUN_COLOR_GREEN;
|
|
else if (!strcmp("Blue", var.value))
|
|
lightgun_params[i].colour = LIGHTGUN_COLOR_BLUE;
|
|
}
|
|
if (lightgun_params[i].colour == LIGHTGUN_COLOR_OFF)
|
|
config::CrosshairColor[i] = 0;
|
|
else
|
|
config::CrosshairColor[i] = lightgun_palette[lightgun_params[i].colour * 3]
|
|
| (lightgun_palette[lightgun_params[i].colour * 3 + 1] << 8)
|
|
| (lightgun_palette[lightgun_params[i].colour * 3 + 2] << 16)
|
|
| 0xff000000;
|
|
|
|
vmu_lcd_status[i * 2] = false;
|
|
vmu_lcd_changed[i * 2] = true;
|
|
vmu_screen_params[i].vmu_screen_position = UPPER_LEFT;
|
|
vmu_screen_params[i].vmu_screen_size_mult = 1;
|
|
vmu_screen_params[i].vmu_pixel_on_R = VMU_SCREEN_COLOR_MAP[VMU_DEFAULT_ON].r;
|
|
vmu_screen_params[i].vmu_pixel_on_G = VMU_SCREEN_COLOR_MAP[VMU_DEFAULT_ON].g;
|
|
vmu_screen_params[i].vmu_pixel_on_B = VMU_SCREEN_COLOR_MAP[VMU_DEFAULT_ON].b;
|
|
vmu_screen_params[i].vmu_pixel_off_R = VMU_SCREEN_COLOR_MAP[VMU_DEFAULT_OFF].r;
|
|
vmu_screen_params[i].vmu_pixel_off_G = VMU_SCREEN_COLOR_MAP[VMU_DEFAULT_OFF].g;
|
|
vmu_screen_params[i].vmu_pixel_off_B = VMU_SCREEN_COLOR_MAP[VMU_DEFAULT_OFF].b;
|
|
vmu_screen_params[i].vmu_screen_opacity = 0xFF;
|
|
|
|
snprintf(key, sizeof(key), CORE_OPTION_NAME "_vmu%d_screen_display", i+1);
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && !strcmp("enabled", var.value) )
|
|
vmu_lcd_status[i * 2] = true;
|
|
|
|
snprintf(key, sizeof(key), CORE_OPTION_NAME "_vmu%d_screen_position", i+1);
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp("Upper Left", var.value))
|
|
vmu_screen_params[i].vmu_screen_position = UPPER_LEFT;
|
|
else if (!strcmp("Upper Right", var.value))
|
|
vmu_screen_params[i].vmu_screen_position = UPPER_RIGHT;
|
|
else if (!strcmp("Lower Left", var.value))
|
|
vmu_screen_params[i].vmu_screen_position = LOWER_LEFT;
|
|
else if (!strcmp("Lower Right", var.value))
|
|
vmu_screen_params[i].vmu_screen_position = LOWER_RIGHT;
|
|
}
|
|
|
|
snprintf(key, sizeof(key), CORE_OPTION_NAME "_vmu%d_screen_size_mult", i+1);
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp("1x", var.value))
|
|
vmu_screen_params[i].vmu_screen_size_mult = 1;
|
|
else if (!strcmp("2x", var.value))
|
|
vmu_screen_params[i].vmu_screen_size_mult = 2;
|
|
else if (!strcmp("3x", var.value))
|
|
vmu_screen_params[i].vmu_screen_size_mult = 3;
|
|
else if (!strcmp("4x", var.value))
|
|
vmu_screen_params[i].vmu_screen_size_mult = 4;
|
|
else if (!strcmp("5x", var.value))
|
|
vmu_screen_params[i].vmu_screen_size_mult = 5;
|
|
}
|
|
|
|
snprintf(key, sizeof(key), CORE_OPTION_NAME "_vmu%d_screen_opacity", i + 1);
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp("100%", var.value))
|
|
vmu_screen_params[i].vmu_screen_opacity = 255;
|
|
else if (!strcmp("90%", var.value))
|
|
vmu_screen_params[i].vmu_screen_opacity = 9*25.5;
|
|
else if (!strcmp("80%", var.value))
|
|
vmu_screen_params[i].vmu_screen_opacity = 8*25.5;
|
|
else if (!strcmp("70%", var.value))
|
|
vmu_screen_params[i].vmu_screen_opacity = 7*25.5;
|
|
else if (!strcmp("60%", var.value))
|
|
vmu_screen_params[i].vmu_screen_opacity = 6*25.5;
|
|
else if (!strcmp("50%", var.value))
|
|
vmu_screen_params[i].vmu_screen_opacity = 5*25.5;
|
|
else if (!strcmp("40%", var.value))
|
|
vmu_screen_params[i].vmu_screen_opacity = 4*25.5;
|
|
else if (!strcmp("30%", var.value))
|
|
vmu_screen_params[i].vmu_screen_opacity = 3*25.5;
|
|
else if (!strcmp("20%", var.value))
|
|
vmu_screen_params[i].vmu_screen_opacity = 2*25.5;
|
|
else if (!strcmp("10%", var.value))
|
|
vmu_screen_params[i].vmu_screen_opacity = 1*25.5;
|
|
}
|
|
|
|
snprintf(key, sizeof(key), CORE_OPTION_NAME "_vmu%d_pixel_on_color", i + 1);
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && strlen(var.value)>1)
|
|
{
|
|
int color_idx = atoi(var.value+(strlen(var.value)-2));
|
|
vmu_screen_params[i].vmu_pixel_on_R = VMU_SCREEN_COLOR_MAP[color_idx].r;
|
|
vmu_screen_params[i].vmu_pixel_on_G = VMU_SCREEN_COLOR_MAP[color_idx].g;
|
|
vmu_screen_params[i].vmu_pixel_on_B = VMU_SCREEN_COLOR_MAP[color_idx].b;
|
|
}
|
|
|
|
snprintf(key, sizeof(key), CORE_OPTION_NAME "_vmu%d_pixel_off_color", i+1);
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && strlen(var.value)>1)
|
|
{
|
|
int color_idx = atoi(var.value+(strlen(var.value)-2));
|
|
vmu_screen_params[i].vmu_pixel_off_R = VMU_SCREEN_COLOR_MAP[color_idx].r;
|
|
vmu_screen_params[i].vmu_pixel_off_G = VMU_SCREEN_COLOR_MAP[color_idx].g;
|
|
vmu_screen_params[i].vmu_pixel_off_B = VMU_SCREEN_COLOR_MAP[color_idx].b;
|
|
}
|
|
}
|
|
|
|
set_variable_visibility();
|
|
|
|
if (!first_startup)
|
|
{
|
|
if (wasThreadedRendering != config::ThreadedRendering)
|
|
{
|
|
config::ThreadedRendering = wasThreadedRendering;
|
|
emu.stop();
|
|
config::ThreadedRendering = !wasThreadedRendering;
|
|
emu.start();
|
|
}
|
|
if (rotate_screen != (prevRotateScreen ^ rotate_game))
|
|
{
|
|
setRotation();
|
|
retro_game_geometry geometry;
|
|
setGameGeometry(geometry);
|
|
environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &geometry);
|
|
}
|
|
else
|
|
rotate_screen ^= rotate_game;
|
|
if (rotate_game)
|
|
config::Widescreen.override(false);
|
|
|
|
if ((libretro_detect_vsync_swap_interval != prevDetectVsyncSwapInterval) &&
|
|
!libretro_detect_vsync_swap_interval &&
|
|
(libretro_vsync_swap_interval != 1))
|
|
{
|
|
libretro_vsync_swap_interval = 1;
|
|
retro_system_av_info avinfo;
|
|
setAVInfo(avinfo);
|
|
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avinfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
void retro_run()
|
|
{
|
|
bool updated = false;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
|
|
update_variables(false);
|
|
|
|
if (devices_need_refresh)
|
|
refresh_devices(false);
|
|
|
|
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
|
|
if (isOpenGL(config::RendererType))
|
|
glsm_ctl(GLSM_CTL_STATE_BIND, nullptr);
|
|
#endif
|
|
|
|
// On the first call, we start the emulator
|
|
if (first_run)
|
|
emu.start();
|
|
|
|
poll_cb();
|
|
UpdateInputState();
|
|
bool fastforward = false;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_FASTFORWARDING, &fastforward))
|
|
settings.input.fastForwardMode = fastforward;
|
|
|
|
is_dupe = true;
|
|
try {
|
|
if (config::ThreadedRendering)
|
|
{
|
|
// Render
|
|
for (int i = 0; i < 5 && is_dupe; i++)
|
|
is_dupe = !emu.render();
|
|
}
|
|
else
|
|
{
|
|
startTime = sh4_sched_now64();
|
|
emu.render();
|
|
}
|
|
} catch (const FlycastException& e) {
|
|
ERROR_LOG(COMMON, "%s", e.what());
|
|
gui_display_notification(e.what(), 5000);
|
|
environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, NULL);
|
|
}
|
|
|
|
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
|
|
if (isOpenGL(config::RendererType))
|
|
glsm_ctl(GLSM_CTL_STATE_UNBIND, nullptr);
|
|
#endif
|
|
|
|
video_cb(is_dupe ? 0 : RETRO_HW_FRAME_BUFFER_VALID, framebufferWidth, framebufferHeight, 0);
|
|
|
|
if (!config::ThreadedRendering || config::LimitFPS)
|
|
retro_audio_upload();
|
|
else
|
|
retro_audio_flush_buffer();
|
|
|
|
first_run = false;
|
|
}
|
|
|
|
static bool loadGame()
|
|
{
|
|
try {
|
|
emu.loadGame(game_data.c_str());
|
|
} catch (const FlycastException& e) {
|
|
ERROR_LOG(BOOT, "%s", e.what());
|
|
gui_display_notification(e.what(), 5000);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void retro_reset()
|
|
{
|
|
std::lock_guard<std::mutex> lock(mtx_serialization);
|
|
|
|
emu.unloadGame();
|
|
|
|
config::ScreenStretching = 100;
|
|
loadGame();
|
|
if (rotate_game)
|
|
config::Widescreen.override(false);
|
|
config::Rotate90 = false;
|
|
|
|
retro_game_geometry geometry;
|
|
setGameGeometry(geometry);
|
|
environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &geometry);
|
|
blankVmus();
|
|
retro_audio_flush_buffer();
|
|
|
|
emu.start();
|
|
}
|
|
|
|
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
|
|
static void context_reset()
|
|
{
|
|
INFO_LOG(RENDERER, "GL context_reset");
|
|
gl_ctx_resetting = false;
|
|
glsm_ctl(GLSM_CTL_STATE_CONTEXT_RESET, NULL);
|
|
glsm_ctl(GLSM_CTL_STATE_SETUP, NULL);
|
|
rend_term_renderer();
|
|
theGLContext.init();
|
|
rend_init_renderer();
|
|
}
|
|
|
|
static void context_destroy()
|
|
{
|
|
gl_ctx_resetting = true;
|
|
rend_term_renderer();
|
|
glsm_ctl(GLSM_CTL_STATE_CONTEXT_DESTROY, NULL);
|
|
}
|
|
#endif
|
|
|
|
static void extract_directory(char *buf, const char *path, size_t size)
|
|
{
|
|
strncpy(buf, path, size - 1);
|
|
buf[size - 1] = '\0';
|
|
|
|
char *base = find_last_slash(buf);
|
|
if (base)
|
|
*base = '\0';
|
|
else
|
|
strncpy(buf, ".", size - 1);
|
|
}
|
|
|
|
static uint32_t map_gamepad_button(unsigned device, unsigned id)
|
|
{
|
|
static const uint32_t dc_joymap[] =
|
|
{
|
|
/* JOYPAD_B */ DC_BTN_A,
|
|
/* JOYPAD_Y */ DC_BTN_X,
|
|
/* JOYPAD_SELECT */ 0,
|
|
/* JOYPAD_START */ DC_BTN_START,
|
|
/* JOYPAD_UP */ DC_DPAD_UP,
|
|
/* JOYPAD_DOWN */ DC_DPAD_DOWN,
|
|
/* JOYPAD_LEFT */ DC_DPAD_LEFT,
|
|
/* JOYPAD_RIGHT */ DC_DPAD_RIGHT,
|
|
/* JOYPAD_A */ DC_BTN_B,
|
|
/* JOYPAD_X */ DC_BTN_Y,
|
|
};
|
|
|
|
static const uint32_t dc_lg_joymap[] =
|
|
{
|
|
/* deprecated */ 0,
|
|
/* deprecated */ 0,
|
|
/* LIGHTGUN_TRIGGER */ DC_BTN_A,
|
|
/* LIGHTGUN_AUX_A */ DC_BTN_B,
|
|
/* LIGHTGUN_AUX_B */ 0,
|
|
/* deprecated */ 0,
|
|
/* LIGHTGUN_START */ DC_BTN_START,
|
|
/* LIGHTGUN_SELECT */ 0,
|
|
/* LIGHTGUN_AUX_C */ 0,
|
|
/* LIGHTGUN_UP */ DC_DPAD_UP,
|
|
/* LIGHTGUN_DOWN */ DC_DPAD_DOWN,
|
|
/* LIGHTGUN_LEFT */ DC_DPAD_LEFT,
|
|
/* LIGHTGUN_RIGHT */ DC_DPAD_RIGHT,
|
|
};
|
|
|
|
static const uint32_t aw_joymap[] =
|
|
{
|
|
/* JOYPAD_B */ AWAVE_BTN0_KEY, /* BTN1 */
|
|
/* JOYPAD_Y */ AWAVE_BTN2_KEY, /* BTN3 */
|
|
/* JOYPAD_SELECT */ AWAVE_COIN_KEY,
|
|
/* JOYPAD_START */ AWAVE_START_KEY,
|
|
/* JOYPAD_UP */ AWAVE_UP_KEY,
|
|
/* JOYPAD_DOWN */ AWAVE_DOWN_KEY,
|
|
/* JOYPAD_LEFT */ AWAVE_LEFT_KEY,
|
|
/* JOYPAD_RIGHT */ AWAVE_RIGHT_KEY,
|
|
/* JOYPAD_A */ AWAVE_BTN1_KEY, /* BTN2 */
|
|
/* JOYPAD_X */ AWAVE_BTN3_KEY, /* BTN4 */
|
|
/* JOYPAD_L */ 0,
|
|
/* JOYPAD_R */ AWAVE_BTN4_KEY, /* BTN5 */
|
|
/* JOYPAD_L2 */ 0,
|
|
/* JOYPAD_R2 */ 0,
|
|
/* JOYPAD_L3 */ AWAVE_TEST_KEY,
|
|
/* JOYPAD_R3 */ AWAVE_SERVICE_KEY,
|
|
};
|
|
|
|
static const uint32_t aw_lg_joymap[] =
|
|
{
|
|
/* deprecated */ 0,
|
|
/* deprecated */ 0,
|
|
/* LIGHTGUN_TRIGGER */ AWAVE_TRIGGER_KEY,
|
|
/* LIGHTGUN_AUX_A */ AWAVE_BTN0_KEY,
|
|
/* LIGHTGUN_AUX_B */ AWAVE_BTN1_KEY,
|
|
/* deprecated */ 0,
|
|
/* LIGHTGUN_START */ AWAVE_START_KEY,
|
|
/* LIGHTGUN_SELECT */ AWAVE_COIN_KEY,
|
|
/* LIGHTGUN_AUX_C */ AWAVE_BTN2_KEY,
|
|
/* LIGHTGUN_UP */ AWAVE_UP_KEY,
|
|
/* LIGHTGUN_DOWN */ AWAVE_DOWN_KEY,
|
|
/* LIGHTGUN_LEFT */ AWAVE_LEFT_KEY,
|
|
/* LIGHTGUN_RIGHT */ AWAVE_RIGHT_KEY,
|
|
};
|
|
|
|
static const uint32_t nao_joymap[] =
|
|
{
|
|
/* JOYPAD_B */ NAOMI_BTN0_KEY, /* BTN1 */
|
|
/* JOYPAD_Y */ NAOMI_BTN2_KEY, /* BTN3 */
|
|
/* JOYPAD_SELECT */ NAOMI_COIN_KEY,
|
|
/* JOYPAD_START */ NAOMI_START_KEY,
|
|
/* JOYPAD_UP */ NAOMI_UP_KEY,
|
|
/* JOYPAD_DOWN */ NAOMI_DOWN_KEY,
|
|
/* JOYPAD_LEFT */ NAOMI_LEFT_KEY,
|
|
/* JOYPAD_RIGHT */ NAOMI_RIGHT_KEY,
|
|
/* JOYPAD_A */ NAOMI_BTN1_KEY, /* BTN2 */
|
|
/* JOYPAD_X */ NAOMI_BTN3_KEY, /* BTN4 */
|
|
/* JOYPAD_L */ NAOMI_BTN5_KEY, /* BTN6 */
|
|
/* JOYPAD_R */ NAOMI_BTN4_KEY, /* BTN5 */
|
|
/* JOYPAD_L2 */ NAOMI_BTN7_KEY, /* BTN8 */
|
|
/* JOYPAD_R2 */ NAOMI_BTN6_KEY, /* BTN7 */
|
|
/* JOYPAD_L3 */ NAOMI_TEST_KEY,
|
|
/* JOYPAD_R3 */ NAOMI_SERVICE_KEY,
|
|
};
|
|
|
|
static const uint32_t nao_lg_joymap[] =
|
|
{
|
|
/* deprecated */ 0,
|
|
/* deprecated */ 0,
|
|
/* LIGHTGUN_TRIGGER */ NAOMI_BTN0_KEY,
|
|
/* LIGHTGUN_AUX_A */ NAOMI_BTN1_KEY,
|
|
/* LIGHTGUN_AUX_B */ NAOMI_BTN2_KEY,
|
|
/* deprecated */ 0,
|
|
/* LIGHTGUN_START */ NAOMI_START_KEY,
|
|
/* LIGHTGUN_SELECT */ NAOMI_COIN_KEY,
|
|
/* LIGHTGUN_AUX_C */ NAOMI_BTN3_KEY,
|
|
/* LIGHTGUN_UP */ NAOMI_UP_KEY,
|
|
/* LIGHTGUN_DOWN */ NAOMI_DOWN_KEY,
|
|
/* LIGHTGUN_LEFT */ NAOMI_LEFT_KEY,
|
|
/* LIGHTGUN_RIGHT */ NAOMI_RIGHT_KEY,
|
|
};
|
|
|
|
const uint32_t *joymap;
|
|
size_t joymap_size;
|
|
|
|
switch (settings.platform.system)
|
|
{
|
|
case DC_PLATFORM_DREAMCAST:
|
|
case DC_PLATFORM_DEV_UNIT:
|
|
switch (device)
|
|
{
|
|
case RETRO_DEVICE_JOYPAD:
|
|
joymap = dc_joymap;
|
|
joymap_size = ARRAY_SIZE(dc_joymap);
|
|
break;
|
|
case RETRO_DEVICE_LIGHTGUN:
|
|
joymap = dc_lg_joymap;
|
|
joymap_size = ARRAY_SIZE(dc_lg_joymap);
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case DC_PLATFORM_NAOMI:
|
|
case DC_PLATFORM_NAOMI2:
|
|
switch (device)
|
|
{
|
|
case RETRO_DEVICE_JOYPAD:
|
|
joymap = nao_joymap;
|
|
joymap_size = ARRAY_SIZE(nao_joymap);
|
|
break;
|
|
case RETRO_DEVICE_LIGHTGUN:
|
|
joymap = nao_lg_joymap;
|
|
joymap_size = ARRAY_SIZE(nao_lg_joymap);
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case DC_PLATFORM_ATOMISWAVE:
|
|
switch (device)
|
|
{
|
|
case RETRO_DEVICE_JOYPAD:
|
|
joymap = aw_joymap;
|
|
joymap_size = ARRAY_SIZE(aw_joymap);
|
|
break;
|
|
case RETRO_DEVICE_LIGHTGUN:
|
|
joymap = aw_lg_joymap;
|
|
joymap_size = ARRAY_SIZE(aw_lg_joymap);
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
if (id >= joymap_size)
|
|
return 0;
|
|
uint32_t mapped = joymap[id];
|
|
// Hack to bind Button 9 instead of Service when not used
|
|
if (id == RETRO_DEVICE_ID_JOYPAD_R3 && device == RETRO_DEVICE_JOYPAD
|
|
&& settings.platform.isNaomi()
|
|
&& !allow_service_buttons)
|
|
mapped = NAOMI_BTN8_KEY;
|
|
return mapped;
|
|
}
|
|
|
|
static const char *get_button_name(unsigned device, unsigned id, const char *default_name)
|
|
{
|
|
if (NaomiGameInputs == NULL)
|
|
return default_name;
|
|
uint32_t mask = map_gamepad_button(device, id);
|
|
if (mask == 0)
|
|
return NULL;
|
|
for (int i = 0; NaomiGameInputs->buttons[i].source != 0; i++)
|
|
if (NaomiGameInputs->buttons[i].source == mask)
|
|
{
|
|
if (NaomiGameInputs->buttons[i].name[0] != '\0')
|
|
return NaomiGameInputs->buttons[i].name;
|
|
else
|
|
return default_name;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static const char *get_axis_name(unsigned index, const char *default_name)
|
|
{
|
|
if (NaomiGameInputs == NULL)
|
|
return default_name;
|
|
for (int i = 0; NaomiGameInputs->axes[i].name != NULL; i++)
|
|
if (NaomiGameInputs->axes[i].axis == index)
|
|
{
|
|
if (NaomiGameInputs->axes[i].name[0] != '\0')
|
|
return NaomiGameInputs->axes[i].name;
|
|
else
|
|
return default_name;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void set_input_descriptors()
|
|
{
|
|
struct retro_input_descriptor desc[22 * 4 + 1];
|
|
int descriptor_index = 0;
|
|
if (settings.platform.isArcade())
|
|
{
|
|
const char *name;
|
|
|
|
for (unsigned i = 0; i < MAPLE_PORTS; i++)
|
|
{
|
|
switch (config::MapleMainDevices[i])
|
|
{
|
|
case MDT_LightGun:
|
|
name = get_button_name(RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_DPAD_LEFT, "D-Pad Left");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_DPAD_LEFT, name };
|
|
name = get_button_name(RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_DPAD_UP, "D-Pad Up");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_DPAD_UP, name };
|
|
name = get_button_name(RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_DPAD_DOWN, "D-Pad Down");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_DPAD_DOWN, name };
|
|
name = get_button_name(RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT, "D-Pad Right") ;
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT, name };
|
|
name = get_button_name(RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER, "Trigger");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER, name };
|
|
name = get_button_name(RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_AUX_A, "Button 1");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_A, name };
|
|
name = get_button_name(RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_AUX_B, "Button 2");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_B, name };
|
|
name = get_button_name(RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_AUX_C, "Button 3");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_C, name };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_RELOAD, "Reload" };
|
|
name = get_button_name(RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_SELECT, "Coin");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SELECT, name };
|
|
name = get_button_name(RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_START, "Start");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_START, name };
|
|
break;
|
|
|
|
case MDT_SegaController:
|
|
name = get_button_name(RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, name };
|
|
name = get_button_name(RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, name };
|
|
name = get_button_name(RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, name };
|
|
name = get_button_name(RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, name };
|
|
name = get_button_name(RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_B, "Button 1");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, name };
|
|
name = get_button_name(RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_A, "Button 2");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, name };
|
|
name = get_button_name(RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_Y, "Button 3");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, name };
|
|
name = get_button_name(RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_X, "Button 4");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, name };
|
|
name = get_button_name(RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_R, "Button 5");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, name };
|
|
name = haveCardReader ? "Insert Card" : get_button_name(RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_L, "Button 6");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, name };
|
|
name = get_button_name(RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_R2, "Button 7");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, name };
|
|
name = get_button_name(RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_L2, "Button 8");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, name };
|
|
name = get_button_name(RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_START, "Start");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, name };
|
|
name = get_button_name(RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_SELECT, "Coin");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, name };
|
|
name = get_button_name(RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_L3, "Test");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3, name };
|
|
name = get_button_name(RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_R3, "Service");
|
|
if (name != NULL)
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3, name };
|
|
name = get_axis_name(0, "Axis 1");
|
|
if (name != NULL && name[0] != '\0')
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, name };
|
|
name = get_axis_name(1, "Axis 2");
|
|
if (name != NULL && name[0] != '\0')
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, name };
|
|
name = get_axis_name(2, "Axis 3");
|
|
if (name != NULL && name[0] != '\0')
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, name };
|
|
name = get_axis_name(3, "Axis 4");
|
|
if (name != NULL && name[0] != '\0')
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, name };
|
|
name = get_axis_name(4, NULL);
|
|
if (name != NULL && name[0] != '\0')
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, name };
|
|
name = get_axis_name(5, NULL);
|
|
if (name != NULL && name[0] != '\0')
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, name };
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (unsigned i = 0; i < MAPLE_PORTS; i++)
|
|
{
|
|
switch (config::MapleMainDevices[i])
|
|
{
|
|
case MDT_SegaController:
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "A" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "B" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "X" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, "L Trigger" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, "R Trigger" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Analog X" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Analog Y" };
|
|
break;
|
|
|
|
case MDT_TwinStick:
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "L-Stick Left" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_UP, "L-Stick Up" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "L-Stick Down" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "L-Stick Right" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_B, "R-Stick Down" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_A, "R-Stick Right" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_X, "R-Stick Up" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_Y, "R-Stick Left" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_L, "L Turbo" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_R, "R Turbo" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_L2, "L Trigger" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_R2, "R Trigger" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Special" };
|
|
break;
|
|
|
|
case MDT_AsciiStick:
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Stick Left" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Stick Up" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Stick Down" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Stick Right" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_B, "A" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_A, "B" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_Y, "X" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_L, "C" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_R, "Z" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" };
|
|
break;
|
|
|
|
case MDT_LightGun:
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_DPAD_LEFT, "D-Pad Left" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_DPAD_UP, "D-Pad Up" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_DPAD_DOWN, "D-Pad Down" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT, "D-Pad Right" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER, "A" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_START, "Start" };
|
|
desc[descriptor_index++] = { i, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_A, "B" };
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
desc[descriptor_index++] = { 0 };
|
|
|
|
environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
|
|
}
|
|
|
|
static void extract_basename(char *buf, const char *path, size_t size)
|
|
{
|
|
const char *base = find_last_slash(path);
|
|
if (!base)
|
|
base = path;
|
|
else
|
|
base++;
|
|
|
|
strncpy(buf, base, size - 1);
|
|
buf[size - 1] = '\0';
|
|
}
|
|
|
|
static void remove_extension(char *buf, const char *path, size_t size)
|
|
{
|
|
char *base;
|
|
strncpy(buf, path, size - 1);
|
|
buf[size - 1] = '\0';
|
|
|
|
base = strrchr(buf, '.');
|
|
|
|
if (base)
|
|
*base = '\0';
|
|
}
|
|
|
|
#ifdef HAVE_VULKAN
|
|
static VulkanContext theVulkanContext;
|
|
|
|
static void retro_vk_context_reset()
|
|
{
|
|
NOTICE_LOG(RENDERER, "retro_vk_context_reset");
|
|
retro_hw_render_interface* vulkan;
|
|
if (!environ_cb(RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE, (void**)&vulkan) || !vulkan)
|
|
{
|
|
ERROR_LOG(RENDERER, "Get Vulkan HW interface failed");
|
|
return;
|
|
}
|
|
theVulkanContext.init((retro_hw_render_interface_vulkan *)vulkan);
|
|
rend_term_renderer();
|
|
rend_init_renderer();
|
|
}
|
|
|
|
static void retro_vk_context_destroy()
|
|
{
|
|
NOTICE_LOG(RENDERER, "retro_vk_context_destroy");
|
|
rend_term_renderer();
|
|
theVulkanContext.term();
|
|
}
|
|
|
|
static bool set_vulkan_hw_render()
|
|
{
|
|
retro_hw_render_callback hw_render{};
|
|
hw_render.context_type = RETRO_HW_CONTEXT_VULKAN;
|
|
hw_render.version_major = VK_API_VERSION_1_0;
|
|
hw_render.version_minor = 0;
|
|
hw_render.context_reset = retro_vk_context_reset;
|
|
hw_render.context_destroy = retro_vk_context_destroy;
|
|
hw_render.debug_context = false;
|
|
|
|
if (!environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render))
|
|
return false;
|
|
|
|
static const struct retro_hw_render_context_negotiation_interface_vulkan negotiation_interface = {
|
|
RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN,
|
|
RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION,
|
|
VkGetApplicationInfo,
|
|
VkCreateDevice,
|
|
nullptr,
|
|
};
|
|
environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE, (void *)&negotiation_interface);
|
|
|
|
if (config::RendererType == RenderType::OpenGL_OIT || config::RendererType == RenderType::DirectX11_OIT)
|
|
config::RendererType = RenderType::Vulkan_OIT;
|
|
else if (config::RendererType != RenderType::Vulkan_OIT)
|
|
config::RendererType = RenderType::Vulkan;
|
|
return true;
|
|
}
|
|
#else
|
|
static bool set_vulkan_hw_render()
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
static bool set_opengl_hw_render(u32 preferred)
|
|
{
|
|
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
|
|
glsm_ctx_params_t params = {0};
|
|
|
|
params.context_reset = context_reset;
|
|
params.context_destroy = context_destroy;
|
|
params.environ_cb = environ_cb;
|
|
#if defined(TARGET_NO_STENCIL)
|
|
params.stencil = false;
|
|
#else
|
|
params.stencil = true;
|
|
#endif
|
|
params.imm_vbo_draw = NULL;
|
|
params.imm_vbo_disable = NULL;
|
|
#if defined(__APPLE__) && defined(HAVE_OPENGL)
|
|
preferred = RETRO_HW_CONTEXT_OPENGL_CORE;
|
|
#endif
|
|
#ifdef HAVE_OIT
|
|
if (config::RendererType == RenderType::OpenGL_OIT)
|
|
{
|
|
params.context_type = (retro_hw_context_type)preferred;
|
|
if (preferred == RETRO_HW_CONTEXT_OPENGL)
|
|
{
|
|
// There are some weirdness with RA's gl context's versioning :
|
|
// - any value above 3.0 won't provide a valid context, while the GLSM_CTL_STATE_CONTEXT_INIT call returns true...
|
|
// - the only way to overwrite previously set version with zero values is to set them directly in hw_render, otherwise they are ignored (see glsm_state_ctx_init logic)
|
|
// FIXME what's the point of this?
|
|
retro_hw_render_callback hw_render;
|
|
hw_render.version_major = 3;
|
|
hw_render.version_minor = 0;
|
|
}
|
|
else
|
|
{
|
|
params.major = 4;
|
|
params.minor = 3;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
#ifndef HAVE_OPENGLES
|
|
params.context_type = (retro_hw_context_type)preferred;
|
|
params.major = 3;
|
|
params.minor = preferred == RETRO_HW_CONTEXT_OPENGL_CORE ? 2 : 0;
|
|
#endif
|
|
config::RendererType = RenderType::OpenGL;
|
|
}
|
|
|
|
if (glsm_ctl(GLSM_CTL_STATE_CONTEXT_INIT, ¶ms))
|
|
return true;
|
|
|
|
#if defined(HAVE_GL3)
|
|
params.context_type = (retro_hw_context_type)preferred;
|
|
params.major = 3;
|
|
params.minor = 0;
|
|
#else
|
|
params.context_type = (retro_hw_context_type)preferred;
|
|
params.major = 0;
|
|
params.minor = 0;
|
|
#endif
|
|
config::RendererType = RenderType::OpenGL;
|
|
return glsm_ctl(GLSM_CTL_STATE_CONTEXT_INIT, ¶ms);
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
#ifdef HAVE_D3D11
|
|
static void dx11_context_reset()
|
|
{
|
|
NOTICE_LOG(RENDERER, "DX11 context reset");
|
|
retro_hw_render_interface_d3d11 *hw_render = nullptr;
|
|
if (!environ_cb(RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE, &hw_render) || hw_render == nullptr || hw_render->interface_type != RETRO_HW_RENDER_INTERFACE_D3D11)
|
|
return;
|
|
if (hw_render->interface_version != RETRO_HW_RENDER_INTERFACE_D3D11_VERSION)
|
|
{
|
|
WARN_LOG(RENDERER, "Unsupported interface version %d, expecting %d", hw_render->interface_version, RETRO_HW_RENDER_INTERFACE_D3D11_VERSION);
|
|
return;
|
|
}
|
|
rend_term_renderer();
|
|
theDX11Context.term();
|
|
|
|
theDX11Context.init(hw_render->device, hw_render->context, hw_render->D3DCompile, hw_render->featureLevel);
|
|
if (config::RendererType == RenderType::OpenGL_OIT || config::RendererType == RenderType::Vulkan_OIT)
|
|
config::RendererType = RenderType::DirectX11_OIT;
|
|
else if (config::RendererType != RenderType::DirectX11_OIT)
|
|
config::RendererType = RenderType::DirectX11;
|
|
rend_init_renderer();
|
|
}
|
|
|
|
static void dx11_context_destroy()
|
|
{
|
|
NOTICE_LOG(RENDERER, "DX11 context destroyed");
|
|
rend_term_renderer();
|
|
theDX11Context.term();
|
|
}
|
|
#endif
|
|
|
|
static bool set_dx11_hw_render()
|
|
{
|
|
#ifdef HAVE_D3D11
|
|
retro_hw_render_callback hw_render_{};
|
|
hw_render_.context_type = RETRO_HW_CONTEXT_DIRECT3D;
|
|
hw_render_.version_major = 11;
|
|
hw_render_.version_minor = 0;
|
|
hw_render_.context_reset = dx11_context_reset;
|
|
hw_render_.context_destroy = dx11_context_destroy;
|
|
|
|
if (!environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render_))
|
|
{
|
|
WARN_LOG(RENDERER, "DX11 hardware rendering not available");
|
|
return false;
|
|
}
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
// Loading/unloading games
|
|
bool retro_load_game(const struct retro_game_info *game)
|
|
{
|
|
NOTICE_LOG(BOOT, "retro_load_game: %s", game->path);
|
|
|
|
extract_basename(g_base_name, game->path, sizeof(g_base_name));
|
|
extract_directory(game_dir, game->path, sizeof(game_dir));
|
|
|
|
// Storing rom dir for later use
|
|
snprintf(g_roms_dir, sizeof(g_roms_dir), "%s%c", game_dir, slash);
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE, &rumble) && log_cb)
|
|
log_cb(RETRO_LOG_DEBUG, "Rumble interface supported!\n");
|
|
|
|
const char *dir = NULL;
|
|
if (!(environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir))
|
|
dir = game_dir;
|
|
|
|
snprintf(game_dir, sizeof(game_dir), "%s%cdc%c", dir, slash, slash);
|
|
snprintf(game_dir_no_slash, sizeof(game_dir_no_slash), "%s%cdc", dir, slash);
|
|
|
|
// Per-content VMU additions START
|
|
// > Get save directory
|
|
const char *vmu_dir = NULL;
|
|
if (!(environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &vmu_dir) && vmu_dir))
|
|
vmu_dir = game_dir;
|
|
|
|
snprintf(vmu_dir_no_slash, sizeof(vmu_dir_no_slash), "%s", vmu_dir);
|
|
|
|
// > Get content name
|
|
remove_extension(content_name, g_base_name, sizeof(content_name));
|
|
|
|
if (content_name[0] == '\0')
|
|
snprintf(content_name, sizeof(content_name), "vmu_save");
|
|
// Per-content VMU additions END
|
|
|
|
update_variables(true);
|
|
|
|
char *ext = strrchr(g_base_name, '.');
|
|
|
|
{
|
|
/* Check for extension .lst, .bin, .dat or .zip. If found, we will set the system type
|
|
* automatically to Naomi or AtomisWave. */
|
|
if (ext)
|
|
{
|
|
log_cb(RETRO_LOG_INFO, "File extension is: %s\n", ext);
|
|
if (!strcmp(".lst", ext)
|
|
|| !strcmp(".bin", ext) || !strcmp(".BIN", ext)
|
|
|| !strcmp(".dat", ext) || !strcmp(".DAT", ext)
|
|
|| !strcmp(".zip", ext) || !strcmp(".ZIP", ext)
|
|
|| !strcmp(".7z", ext) || !strcmp(".7Z", ext))
|
|
{
|
|
settings.platform.system = naomi_cart_GetPlatform(game->path);
|
|
// Users should use the superior format instead, let's warn them
|
|
if (!strcmp(".lst", ext)
|
|
|| !strcmp(".bin", ext) || !strcmp(".BIN", ext)
|
|
|| !strcmp(".dat", ext) || !strcmp(".DAT", ext))
|
|
{
|
|
struct retro_message msg;
|
|
// Sadly, this callback is only able to display short messages, so we can't give proper explanations...
|
|
msg.msg = "Please upgrade to MAME romsets or expect issues";
|
|
msg.frames = 1200;
|
|
environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, &msg);
|
|
}
|
|
}
|
|
// If m3u playlist found load the paths into array
|
|
else if (!strcmp(".m3u", ext) || !strcmp(".M3U", ext))
|
|
{
|
|
if (!read_m3u(game->path))
|
|
{
|
|
if (log_cb)
|
|
log_cb(RETRO_LOG_ERROR, "%s\n", "[libretro]: failed to read m3u file ...\n");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (game->path[0] == '\0')
|
|
{
|
|
if (settings.platform.isConsole())
|
|
boot_to_bios = true;
|
|
else
|
|
return false;
|
|
}
|
|
if (settings.platform.isArcade())
|
|
boot_to_bios = false;
|
|
|
|
if (boot_to_bios)
|
|
game_data.clear();
|
|
// if an m3u file was loaded, disk_paths will already be populated so load the game from there
|
|
else if (disk_paths.size() > 0)
|
|
{
|
|
disk_index = 0;
|
|
|
|
// Attempt to set initial disk index
|
|
if (disk_paths.size() > 1
|
|
&& disk_initial_index > 0
|
|
&& disk_initial_index < disk_paths.size()
|
|
&& disk_paths[disk_initial_index].compare(disk_initial_path) == 0)
|
|
disk_index = disk_initial_index;
|
|
|
|
game_data = disk_paths[disk_index];
|
|
}
|
|
else
|
|
{
|
|
char disk_label[PATH_MAX];
|
|
disk_label[0] = '\0';
|
|
|
|
disk_paths.push_back(game->path);
|
|
|
|
fill_short_pathname_representation(disk_label, game->path, sizeof(disk_label));
|
|
disk_labels.push_back(disk_label);
|
|
|
|
game_data = game->path;
|
|
}
|
|
|
|
{
|
|
char data_dir[1024];
|
|
|
|
snprintf(data_dir, sizeof(data_dir), "%s%s", game_dir, "data");
|
|
|
|
INFO_LOG(COMMON, "Creating dir: %s", data_dir);
|
|
struct stat buf;
|
|
if (stat(data_dir, &buf) < 0)
|
|
{
|
|
path_mkdir(data_dir);
|
|
}
|
|
}
|
|
|
|
u32 preferred;
|
|
if (!environ_cb(RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER, &preferred))
|
|
preferred = RETRO_HW_CONTEXT_DUMMY;
|
|
bool foundRenderApi = false;
|
|
|
|
if (preferred == RETRO_HW_CONTEXT_OPENGL || preferred == RETRO_HW_CONTEXT_OPENGL_CORE
|
|
|| preferred == RETRO_HW_CONTEXT_OPENGLES2 || preferred == RETRO_HW_CONTEXT_OPENGLES3
|
|
|| preferred == RETRO_HW_CONTEXT_OPENGLES_VERSION)
|
|
{
|
|
foundRenderApi = set_opengl_hw_render(preferred);
|
|
}
|
|
else if (preferred == RETRO_HW_CONTEXT_VULKAN)
|
|
{
|
|
foundRenderApi = set_vulkan_hw_render();
|
|
}
|
|
else if (preferred == RETRO_HW_CONTEXT_DIRECT3D)
|
|
{
|
|
foundRenderApi = set_dx11_hw_render();
|
|
}
|
|
else
|
|
{
|
|
// fallback when not supported (or auto-switching disabled), let's try all supported drivers
|
|
foundRenderApi = set_dx11_hw_render();
|
|
if (!foundRenderApi)
|
|
foundRenderApi = set_vulkan_hw_render();
|
|
#if defined(HAVE_OPENGLES)
|
|
if (!foundRenderApi)
|
|
foundRenderApi = set_opengl_hw_render(RETRO_HW_CONTEXT_OPENGLES3);
|
|
if (!foundRenderApi)
|
|
foundRenderApi = set_opengl_hw_render(RETRO_HW_CONTEXT_OPENGLES2);
|
|
#else
|
|
if (!foundRenderApi)
|
|
foundRenderApi = set_opengl_hw_render(RETRO_HW_CONTEXT_OPENGL_CORE);
|
|
if (!foundRenderApi)
|
|
foundRenderApi = set_opengl_hw_render(RETRO_HW_CONTEXT_OPENGL);
|
|
#endif
|
|
}
|
|
|
|
if (!foundRenderApi)
|
|
return false;
|
|
|
|
if (settings.platform.isArcade())
|
|
{
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &dir)
|
|
&& dir != nullptr
|
|
&& strcmp(dir, g_roms_dir) != 0)
|
|
{
|
|
static char save_dir[PATH_MAX];
|
|
snprintf(save_dir, sizeof(save_dir), "%s%creicast%c", dir, slash, slash);
|
|
|
|
struct stat buf;
|
|
if (stat(save_dir, &buf) < 0)
|
|
{
|
|
DEBUG_LOG(BOOT, "Creating dir: %s", save_dir);
|
|
path_mkdir(save_dir);
|
|
}
|
|
arcadeFlashPath = std::string(save_dir) + g_base_name;
|
|
} else {
|
|
arcadeFlashPath = std::string(g_roms_dir) + g_base_name;
|
|
}
|
|
INFO_LOG(BOOT, "Setting flash base path to %s", arcadeFlashPath.c_str());
|
|
}
|
|
|
|
config::ScreenStretching = 100;
|
|
if (!loadGame())
|
|
return false;
|
|
|
|
rotate_game = config::Rotate90;
|
|
if (rotate_game)
|
|
config::Widescreen.override(false);
|
|
config::Rotate90 = false; // actual framebuffer rotation is done by frontend
|
|
|
|
setRotation();
|
|
|
|
if (settings.content.gameId == "INITIAL D"
|
|
|| settings.content.gameId == "INITIAL D Ver.2"
|
|
|| settings.content.gameId == "INITIAL D Ver.3"
|
|
|| settings.content.gameId == "INITIAL D CYCRAFT")
|
|
haveCardReader = true;
|
|
else
|
|
haveCardReader = false;
|
|
refresh_devices(true);
|
|
|
|
// System may have changed - have to update hidden core options
|
|
set_variable_visibility();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void retro_unload_game()
|
|
{
|
|
INFO_LOG(COMMON, "Flycast unloading game");
|
|
emu.unloadGame();
|
|
game_data.clear();
|
|
disk_paths.clear();
|
|
disk_labels.clear();
|
|
blankVmus();
|
|
}
|
|
|
|
|
|
// Memory/Serialization
|
|
void *retro_get_memory_data(unsigned type)
|
|
{
|
|
if (type == RETRO_MEMORY_SYSTEM_RAM)
|
|
return mem_b.data;
|
|
return nullptr;
|
|
}
|
|
|
|
size_t retro_get_memory_size(unsigned type)
|
|
{
|
|
if (type == RETRO_MEMORY_SYSTEM_RAM)
|
|
return mem_b.size;
|
|
return 0;
|
|
}
|
|
|
|
size_t retro_serialize_size()
|
|
{
|
|
DEBUG_LOG(SAVESTATE, "retro_serialize_size");
|
|
std::lock_guard<std::mutex> lock(mtx_serialization);
|
|
|
|
if (!first_run)
|
|
emu.stop();
|
|
|
|
Serializer ser;
|
|
dc_serialize(ser);
|
|
if (!first_run)
|
|
emu.start();
|
|
|
|
return ser.size();
|
|
}
|
|
|
|
bool retro_serialize(void *data, size_t size)
|
|
{
|
|
DEBUG_LOG(SAVESTATE, "retro_serialize %d bytes", (int)size);
|
|
std::lock_guard<std::mutex> lock(mtx_serialization);
|
|
|
|
if (!first_run)
|
|
emu.stop();
|
|
|
|
Serializer ser(data, size);
|
|
dc_serialize(ser);
|
|
if (!first_run)
|
|
emu.start();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool retro_unserialize(const void * data, size_t size)
|
|
{
|
|
DEBUG_LOG(SAVESTATE, "retro_unserialize");
|
|
std::lock_guard<std::mutex> lock(mtx_serialization);
|
|
|
|
if (!first_run)
|
|
emu.stop();
|
|
|
|
try {
|
|
Deserializer deser(data, size);
|
|
dc_loadstate(deser);
|
|
retro_audio_flush_buffer();
|
|
if (!first_run)
|
|
emu.start();
|
|
|
|
return true;
|
|
} catch (const Deserializer::Exception& e) {
|
|
ERROR_LOG(SAVESTATE, "Loading state failed: %s", e.what());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Cheats
|
|
void retro_cheat_reset()
|
|
{
|
|
// Nothing to do here
|
|
}
|
|
void retro_cheat_set(unsigned unused, bool unused1, const char* unused2)
|
|
{
|
|
// Nothing to do here
|
|
}
|
|
|
|
|
|
// Get info
|
|
const char* retro_get_system_directory()
|
|
{
|
|
const char* dir;
|
|
environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir);
|
|
return dir ? dir : ".";
|
|
}
|
|
|
|
void retro_get_system_info(struct retro_system_info *info)
|
|
{
|
|
info->library_name = "Flycast";
|
|
#ifndef GIT_VERSION
|
|
#define GIT_VERSION "undefined"
|
|
#endif
|
|
info->library_version = GIT_VERSION;
|
|
info->valid_extensions = "chd|cdi|elf|cue|gdi|lst|bin|dat|zip|7z|m3u";
|
|
info->need_fullpath = true;
|
|
info->block_extract = true;
|
|
}
|
|
|
|
void retro_get_system_av_info(retro_system_av_info *info)
|
|
{
|
|
NOTICE_LOG(RENDERER, "retro_get_system_av_info: Res=%d", (int)config::RenderResolution);
|
|
|
|
if (cheatManager.isWidescreen())
|
|
{
|
|
retro_message msg;
|
|
msg.msg = "Widescreen cheat activated";
|
|
msg.frames = 120;
|
|
environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, &msg);
|
|
}
|
|
framebufferWidth = config::RenderResolution * 16 / 9;
|
|
framebufferHeight = config::RenderResolution;
|
|
setAVInfo(*info);
|
|
maxFramebufferWidth = info->geometry.max_width;
|
|
maxFramebufferHeight = info->geometry.max_height;
|
|
}
|
|
|
|
unsigned retro_get_region()
|
|
{
|
|
return config::Broadcast == 0 ? RETRO_REGION_NTSC : RETRO_REGION_PAL;
|
|
}
|
|
|
|
// Controller
|
|
void retro_set_controller_port_device(unsigned in_port, unsigned device)
|
|
{
|
|
if (device_type[in_port] != (int)device && in_port < MAPLE_PORTS)
|
|
{
|
|
devices_need_refresh = true;
|
|
device_type[in_port] = device;
|
|
switch (device)
|
|
{
|
|
case RETRO_DEVICE_JOYPAD:
|
|
config::MapleMainDevices[in_port] = MDT_SegaController;
|
|
if (settings.platform.isConsole()) {
|
|
config::MapleExpansionDevices[in_port][0] = MDT_SegaVMU;
|
|
config::MapleExpansionDevices[in_port][1] = enable_purupuru ? MDT_PurupuruPack : MDT_SegaVMU;
|
|
}
|
|
break;
|
|
case RETRO_DEVICE_TWINSTICK:
|
|
case RETRO_DEVICE_TWINSTICK_SATURN:
|
|
config::MapleMainDevices[in_port] = MDT_TwinStick;
|
|
if (settings.platform.isConsole()) {
|
|
config::MapleExpansionDevices[in_port][0] = enable_purupuru ? MDT_PurupuruPack : MDT_SegaVMU;
|
|
config::MapleExpansionDevices[in_port][1] = MDT_None;
|
|
}
|
|
break;
|
|
case RETRO_DEVICE_ASCIISTICK:
|
|
config::MapleMainDevices[in_port] = MDT_AsciiStick;
|
|
if (settings.platform.isConsole()) {
|
|
config::MapleExpansionDevices[in_port][0] = enable_purupuru ? MDT_PurupuruPack : MDT_SegaVMU;
|
|
config::MapleExpansionDevices[in_port][1] = MDT_None;
|
|
}
|
|
break;
|
|
case RETRO_DEVICE_KEYBOARD:
|
|
config::MapleMainDevices[in_port] = MDT_Keyboard;
|
|
if (settings.platform.isConsole()) {
|
|
config::MapleExpansionDevices[in_port][0] = MDT_None;
|
|
config::MapleExpansionDevices[in_port][1] = MDT_None;
|
|
}
|
|
break;
|
|
case RETRO_DEVICE_MOUSE:
|
|
config::MapleMainDevices[in_port] = MDT_Mouse;
|
|
if (settings.platform.isConsole()) {
|
|
config::MapleExpansionDevices[in_port][0] = MDT_None;
|
|
config::MapleExpansionDevices[in_port][1] = MDT_None;
|
|
}
|
|
break;
|
|
case RETRO_DEVICE_LIGHTGUN:
|
|
config::MapleMainDevices[in_port] = MDT_LightGun;
|
|
if (settings.platform.isConsole()) {
|
|
config::MapleExpansionDevices[in_port][0] = enable_purupuru ? MDT_PurupuruPack : MDT_SegaVMU;
|
|
config::MapleExpansionDevices[in_port][1] = MDT_None;
|
|
}
|
|
break;
|
|
default:
|
|
config::MapleMainDevices[in_port] = MDT_None;
|
|
if (settings.platform.isConsole()) {
|
|
config::MapleExpansionDevices[in_port][0] = MDT_None;
|
|
config::MapleExpansionDevices[in_port][1] = MDT_None;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void refresh_devices(bool first_startup)
|
|
{
|
|
devices_need_refresh = false;
|
|
set_input_descriptors();
|
|
|
|
if (!first_startup)
|
|
{
|
|
if (settings.platform.isConsole())
|
|
maple_ReconnectDevices();
|
|
|
|
if (rumble.set_rumble_state)
|
|
{
|
|
for(int i = 0; i < MAPLE_PORTS; i++)
|
|
{
|
|
rumble.set_rumble_state(i, RETRO_RUMBLE_STRONG, 0);
|
|
rumble.set_rumble_state(i, RETRO_RUMBLE_WEAK, 0);
|
|
}
|
|
}
|
|
}
|
|
else if (settings.platform.isConsole())
|
|
{
|
|
mcfg_DestroyDevices();
|
|
mcfg_CreateDevices();
|
|
}
|
|
}
|
|
|
|
// API version (to detect version mismatch)
|
|
unsigned retro_api_version()
|
|
{
|
|
return RETRO_API_VERSION;
|
|
}
|
|
|
|
void retro_rend_present()
|
|
{
|
|
if (!config::ThreadedRendering)
|
|
is_dupe = false;
|
|
}
|
|
|
|
static uint32_t get_time_ms()
|
|
{
|
|
return (uint32_t)(os_GetSeconds() * 1000.0);
|
|
}
|
|
|
|
static void get_analog_stick( retro_input_state_t input_state_cb,
|
|
int player_index,
|
|
int stick,
|
|
s8* p_analog_x,
|
|
s8* p_analog_y )
|
|
{
|
|
int analog_x, analog_y;
|
|
analog_x = input_state_cb( player_index, RETRO_DEVICE_ANALOG, stick, RETRO_DEVICE_ID_ANALOG_X );
|
|
analog_y = input_state_cb( player_index, RETRO_DEVICE_ANALOG, stick, RETRO_DEVICE_ID_ANALOG_Y );
|
|
|
|
// Analog stick deadzone (borrowed code from parallel-n64 core)
|
|
if ( astick_deadzone > 0 )
|
|
{
|
|
static const int ASTICK_MAX = 0x8000;
|
|
|
|
// Convert cartesian coordinate analog stick to polar coordinates
|
|
double radius = sqrt(analog_x * analog_x + analog_y * analog_y);
|
|
double angle = atan2(analog_y, analog_x);
|
|
|
|
if (radius > astick_deadzone)
|
|
{
|
|
// Re-scale analog stick range to negate deadzone (makes slow movements possible)
|
|
radius = (radius - astick_deadzone)*((float)ASTICK_MAX/(ASTICK_MAX - astick_deadzone));
|
|
|
|
// Convert back to cartesian coordinates
|
|
analog_x = (int)round(radius * cos(angle));
|
|
analog_y = (int)round(radius * sin(angle));
|
|
|
|
// Clamp to correct range
|
|
if (analog_x > +32767) analog_x = +32767;
|
|
if (analog_x < -32767) analog_x = -32767;
|
|
if (analog_y > +32767) analog_y = +32767;
|
|
if (analog_y < -32767) analog_y = -32767;
|
|
}
|
|
else
|
|
{
|
|
analog_x = 0;
|
|
analog_y = 0;
|
|
}
|
|
}
|
|
|
|
// output
|
|
*p_analog_x = (s8)(analog_x >> 8);
|
|
*p_analog_y = (s8)(analog_y >> 8);
|
|
}
|
|
|
|
static uint16_t apply_trigger_deadzone( uint16_t input )
|
|
{
|
|
if ( trigger_deadzone > 0 )
|
|
{
|
|
if ( input > trigger_deadzone )
|
|
{
|
|
// Re-scale analog range
|
|
static const int TRIGGER_MAX = 0x8000;
|
|
const float scale = ((float)TRIGGER_MAX/(float)(TRIGGER_MAX - trigger_deadzone));
|
|
float scaled = (input - trigger_deadzone)*scale;
|
|
|
|
input = (int)round(scaled);
|
|
if (input > +32767)
|
|
input = +32767;
|
|
}
|
|
else
|
|
input = 0;
|
|
}
|
|
|
|
return input;
|
|
}
|
|
|
|
static uint16_t get_analog_trigger(
|
|
int16_t ret,
|
|
retro_input_state_t input_state_cb,
|
|
int player_index,
|
|
int id )
|
|
{
|
|
// NOTE: Analog triggers were added Nov 2017. Not all front-ends support this
|
|
// feature (or pre-date it) so we need to handle this in a graceful way.
|
|
|
|
// First, try and get an analog value using the new libretro API constant
|
|
uint16_t trigger = input_state_cb( player_index,
|
|
RETRO_DEVICE_ANALOG,
|
|
RETRO_DEVICE_INDEX_ANALOG_BUTTON,
|
|
id );
|
|
|
|
if ( trigger == 0 )
|
|
{
|
|
// If we got exactly zero, we're either not pressing the button, or the front-end
|
|
// is not reporting analog values. We need to do a second check using the classic
|
|
// digital API method, to at least get some response - better than nothing.
|
|
|
|
// NOTE: If we're really just not holding the trigger, we're still going to get zero.
|
|
|
|
trigger = (ret & (1 << id)) ? 0x7FFF : 0;
|
|
}
|
|
else
|
|
{
|
|
// We got something, which means the front-end can handle analog buttons.
|
|
// So we apply a deadzone to the input and use it.
|
|
|
|
trigger = apply_trigger_deadzone( trigger );
|
|
}
|
|
|
|
return trigger;
|
|
}
|
|
|
|
static void setDeviceButtonState(u32 port, int deviceType, int btnId)
|
|
{
|
|
uint32_t dc_key = map_gamepad_button(deviceType, btnId);
|
|
bool is_down = input_cb(port, deviceType, 0, btnId);
|
|
if (is_down)
|
|
kcode[port] &= ~dc_key;
|
|
else
|
|
kcode[port] |= dc_key;
|
|
}
|
|
|
|
static void setDeviceButtonStateFromBitmap(u32 bitmap, u32 port, int deviceType, int btnId)
|
|
{
|
|
uint32_t dc_key = map_gamepad_button(deviceType, btnId);
|
|
bool is_down = bitmap & (1 << btnId);
|
|
if (is_down)
|
|
kcode[port] &= ~dc_key;
|
|
else
|
|
kcode[port] |= dc_key;
|
|
}
|
|
|
|
// don't call map_gamepad_button, we supply the DC bit directly.
|
|
static void setDeviceButtonStateDirect(u32 bitmap, u32 port, int deviceType, int btnId, int dc_bit)
|
|
{
|
|
uint32_t dc_key = 1 << dc_bit;
|
|
bool is_down = bitmap & (1 << btnId);
|
|
if (is_down)
|
|
kcode[port] &= ~dc_key;
|
|
else
|
|
kcode[port] |= dc_key;
|
|
}
|
|
|
|
static void updateMouseState(u32 port)
|
|
{
|
|
std::lock_guard<std::mutex> lock(relPosMutex);
|
|
|
|
mo_x_delta[port] += input_cb(port, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X);
|
|
mo_y_delta[port] += input_cb(port, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_Y);
|
|
|
|
bool btn_state = input_cb(port, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT);
|
|
if (btn_state)
|
|
mo_buttons[port] &= ~(1 << 2);
|
|
else
|
|
mo_buttons[port] |= 1 << 2;
|
|
btn_state = input_cb(port, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT);
|
|
if (btn_state)
|
|
mo_buttons[port] &= ~(1 << 1);
|
|
else
|
|
mo_buttons[port] |= 1 << 1;
|
|
btn_state = input_cb(port, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_MIDDLE);
|
|
if (btn_state)
|
|
mo_buttons[port] &= ~(1 << 3);
|
|
else
|
|
mo_buttons[port] |= 1 << 3;
|
|
if (input_cb(port, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELDOWN))
|
|
mo_wheel_delta[port] -= 10;
|
|
else if (input_cb(port, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_WHEELUP))
|
|
mo_wheel_delta[port] += 10;
|
|
}
|
|
|
|
static void updateLightgunCoordinates(u32 port)
|
|
{
|
|
int x = input_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X);
|
|
int y = input_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y);
|
|
if (config::Widescreen && config::ScreenStretching == 100 && !config::EmulateFramebuffer)
|
|
mo_x_abs[port] = 640.f * ((x + 0x8000) * 4.f / 3.f / 0x10000 - (4.f / 3.f - 1.f) / 2.f);
|
|
else
|
|
mo_x_abs[port] = (x + 0x8000) * 640.f / 0x10000;
|
|
mo_y_abs[port] = (y + 0x8000) * 480.f / 0x10000;
|
|
|
|
lightgun_params[port].offscreen = false;
|
|
lightgun_params[port].x = mo_x_abs[port];
|
|
lightgun_params[port].y = mo_y_abs[port];
|
|
}
|
|
|
|
static void UpdateInputStateNaomi(u32 port)
|
|
{
|
|
switch (config::MapleMainDevices[port])
|
|
{
|
|
case MDT_LightGun:
|
|
{
|
|
//
|
|
// -- buttons
|
|
setDeviceButtonState(port, RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER);
|
|
setDeviceButtonState(port, RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_AUX_A);
|
|
setDeviceButtonState(port, RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_AUX_B);
|
|
setDeviceButtonState(port, RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_AUX_C);
|
|
setDeviceButtonState(port, RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_START);
|
|
setDeviceButtonState(port, RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_SELECT);
|
|
setDeviceButtonState(port, RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_DPAD_UP);
|
|
setDeviceButtonState(port, RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_DPAD_DOWN);
|
|
setDeviceButtonState(port, RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_DPAD_LEFT);
|
|
setDeviceButtonState(port, RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT);
|
|
|
|
bool force_offscreen = false;
|
|
|
|
if (input_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_RELOAD))
|
|
{
|
|
force_offscreen = true;
|
|
if (settings.platform.isAtomiswave())
|
|
kcode[port] &= ~AWAVE_TRIGGER_KEY;
|
|
else
|
|
kcode[port] &= ~NAOMI_BTN0_KEY;
|
|
}
|
|
|
|
if (force_offscreen || input_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN))
|
|
{
|
|
mo_x_abs[port] = 0;
|
|
mo_y_abs[port] = 0;
|
|
lightgun_params[port].offscreen = true;
|
|
|
|
if (input_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER) || input_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_RELOAD))
|
|
{
|
|
if (settings.platform.isNaomi())
|
|
kcode[port] &= ~NAOMI_BTN1_KEY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
updateLightgunCoordinates(port);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
//
|
|
// -- buttons
|
|
int16_t ret = 0;
|
|
if (libretro_supports_bitmasks)
|
|
ret = input_cb(port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
|
|
else
|
|
{
|
|
for (int id = RETRO_DEVICE_ID_JOYPAD_B; id <= RETRO_DEVICE_ID_JOYPAD_R3; ++id)
|
|
if (input_cb(port, RETRO_DEVICE_JOYPAD, 0, id))
|
|
ret |= (1 << id);
|
|
}
|
|
|
|
for (int id = RETRO_DEVICE_ID_JOYPAD_B; id <= RETRO_DEVICE_ID_JOYPAD_R3; ++id)
|
|
{
|
|
switch (id)
|
|
{
|
|
case RETRO_DEVICE_ID_JOYPAD_L3:
|
|
if (allow_service_buttons)
|
|
setDeviceButtonStateFromBitmap(ret, port, RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_L3);
|
|
break;
|
|
case RETRO_DEVICE_ID_JOYPAD_R3:
|
|
if (settings.platform.isNaomi()
|
|
|| allow_service_buttons)
|
|
setDeviceButtonStateFromBitmap(ret, port, RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_R3);
|
|
break;
|
|
case RETRO_DEVICE_ID_JOYPAD_L:
|
|
if (haveCardReader)
|
|
{
|
|
if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_L))
|
|
card_reader::insertCard();
|
|
}
|
|
else
|
|
setDeviceButtonStateFromBitmap(ret, port, RETRO_DEVICE_JOYPAD, RETRO_DEVICE_ID_JOYPAD_L);
|
|
break;
|
|
default:
|
|
setDeviceButtonStateFromBitmap(ret, port, RETRO_DEVICE_JOYPAD, id);
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// -- analog stick
|
|
|
|
get_analog_stick(input_cb, port, RETRO_DEVICE_INDEX_ANALOG_LEFT, &joyx[port], &joyy[port] );
|
|
get_analog_stick(input_cb, port, RETRO_DEVICE_INDEX_ANALOG_RIGHT, &joyrx[port], &joyry[port]);
|
|
lt[port] = get_analog_trigger(ret, input_cb, port, RETRO_DEVICE_ID_JOYPAD_L2) / 128;
|
|
rt[port] = get_analog_trigger(ret, input_cb, port, RETRO_DEVICE_ID_JOYPAD_R2) / 128;
|
|
|
|
if (NaomiGameInputs != NULL)
|
|
{
|
|
for (int i = 0; NaomiGameInputs->axes[i].name != NULL; i++)
|
|
{
|
|
if (NaomiGameInputs->axes[i].type == Half)
|
|
{
|
|
/* Note:
|
|
* - Analog stick axes have a range of [-128, 127]
|
|
* - Analog triggers have a range of [0, 255] */
|
|
switch (NaomiGameInputs->axes[i].axis)
|
|
{
|
|
case 0:
|
|
/* Left stick X: [-128, 127] */
|
|
joyx[port] = std::max((int)joyx[port], 0) * 2;
|
|
break;
|
|
case 1:
|
|
/* Left stick Y: [-128, 127] */
|
|
joyy[port] = std::max((int)joyy[port], 0) * 2;
|
|
break;
|
|
case 2:
|
|
/* Right stick X: [-128, 127] */
|
|
joyrx[port] = std::max((int)joyrx[port], 0) * 2;
|
|
break;
|
|
case 3:
|
|
/* Right stick Y: [-128, 127] */
|
|
joyry[port] = std::max((int)joyry[port], 0) * 2;
|
|
break;
|
|
/* Case 4/5 correspond to right/left trigger.
|
|
* These inputs are always classified as 'Half',
|
|
* and already have the correct range - so no
|
|
* further action is required */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// -- mouse, for rotary encoders
|
|
updateMouseState(port);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Avoid Left+Right or Up+Down buttons being pressed together as this crashes some games
|
|
if (settings.platform.isAtomiswave())
|
|
{
|
|
if ((kcode[port] & (AWAVE_UP_KEY|AWAVE_DOWN_KEY)) == 0)
|
|
kcode[port] |= AWAVE_UP_KEY|AWAVE_DOWN_KEY;
|
|
if ((kcode[port] & (AWAVE_LEFT_KEY|AWAVE_RIGHT_KEY)) == 0)
|
|
kcode[port] |= AWAVE_LEFT_KEY|AWAVE_RIGHT_KEY;
|
|
}
|
|
else
|
|
{
|
|
if ((kcode[port] & (NAOMI_UP_KEY|NAOMI_DOWN_KEY)) == 0)
|
|
kcode[port] |= NAOMI_UP_KEY|NAOMI_DOWN_KEY;
|
|
if ((kcode[port] & (NAOMI_LEFT_KEY|NAOMI_RIGHT_KEY)) == 0)
|
|
kcode[port] |= NAOMI_LEFT_KEY|NAOMI_RIGHT_KEY;
|
|
}
|
|
}
|
|
|
|
static void UpdateInputState(u32 port)
|
|
{
|
|
if (gl_ctx_resetting)
|
|
return;
|
|
|
|
if (settings.platform.isArcade())
|
|
{
|
|
UpdateInputStateNaomi(port);
|
|
return;
|
|
}
|
|
if (rumble.set_rumble_state != NULL && vib_stop_time[port] > 0)
|
|
{
|
|
if (get_time_ms() >= vib_stop_time[port])
|
|
{
|
|
vib_stop_time[port] = 0;
|
|
rumble.set_rumble_state(port, RETRO_RUMBLE_STRONG, 0);
|
|
}
|
|
else if (vib_delta[port] > 0.0)
|
|
{
|
|
u32 rem_time = vib_stop_time[port] - get_time_ms();
|
|
rumble.set_rumble_state(port, RETRO_RUMBLE_STRONG, 65535 * vib_strength[port] * rem_time * vib_delta[port]);
|
|
}
|
|
}
|
|
|
|
lightgun_params[port].offscreen = true;
|
|
|
|
switch (config::MapleMainDevices[port])
|
|
{
|
|
case MDT_SegaController:
|
|
{
|
|
int16_t ret = 0;
|
|
//
|
|
// -- buttons
|
|
|
|
if (libretro_supports_bitmasks)
|
|
ret = input_cb(port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
|
|
else
|
|
{
|
|
for (int id = RETRO_DEVICE_ID_JOYPAD_B; id <= RETRO_DEVICE_ID_JOYPAD_R3; ++id)
|
|
if (input_cb(port, RETRO_DEVICE_JOYPAD, 0, id))
|
|
ret |= (1 << id);
|
|
}
|
|
|
|
for (int id = RETRO_DEVICE_ID_JOYPAD_B; id <= RETRO_DEVICE_ID_JOYPAD_X; ++id)
|
|
setDeviceButtonStateFromBitmap(ret, port, RETRO_DEVICE_JOYPAD, id);
|
|
|
|
//
|
|
// -- analog stick
|
|
|
|
get_analog_stick( input_cb, port, RETRO_DEVICE_INDEX_ANALOG_LEFT, &(joyx[port]), &(joyy[port]) );
|
|
|
|
//
|
|
// -- triggers
|
|
|
|
if ( digital_triggers )
|
|
{
|
|
// -- digital left trigger
|
|
if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_L2))
|
|
lt[port]=0xFF;
|
|
else
|
|
lt[port]=0;
|
|
// -- digital right trigger
|
|
if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_R2))
|
|
rt[port]=0xFF;
|
|
else
|
|
rt[port]=0;
|
|
}
|
|
else
|
|
{
|
|
// -- analog triggers
|
|
lt[port] = get_analog_trigger(ret, input_cb, port, RETRO_DEVICE_ID_JOYPAD_L2 ) / 128;
|
|
rt[port] = get_analog_trigger(ret, input_cb, port, RETRO_DEVICE_ID_JOYPAD_R2 ) / 128;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MDT_AsciiStick:
|
|
{
|
|
int16_t ret = 0;
|
|
|
|
if (libretro_supports_bitmasks)
|
|
ret = input_cb(port, RETRO_DEVICE_ASCIISTICK, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
|
|
else
|
|
{
|
|
for (int id = RETRO_DEVICE_ID_JOYPAD_B; id <= RETRO_DEVICE_ID_JOYPAD_R3; ++id)
|
|
if (input_cb(port, RETRO_DEVICE_ASCIISTICK, 0, id))
|
|
ret |= (1 << id);
|
|
}
|
|
|
|
kcode[port] = 0xFFFF; // active-low
|
|
|
|
// stick
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_ASCIISTICK, RETRO_DEVICE_ID_JOYPAD_UP, 4 );
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_ASCIISTICK, RETRO_DEVICE_ID_JOYPAD_DOWN, 5 );
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_ASCIISTICK, RETRO_DEVICE_ID_JOYPAD_LEFT, 6 );
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_ASCIISTICK, RETRO_DEVICE_ID_JOYPAD_RIGHT, 7 );
|
|
|
|
// buttons
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_ASCIISTICK, RETRO_DEVICE_ID_JOYPAD_B, 2 ); // A
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_ASCIISTICK, RETRO_DEVICE_ID_JOYPAD_A, 1 ); // B
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_ASCIISTICK, RETRO_DEVICE_ID_JOYPAD_Y, 10 ); // X
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_ASCIISTICK, RETRO_DEVICE_ID_JOYPAD_X, 9 ); // Y
|
|
|
|
// Z
|
|
{
|
|
uint32_t dc_key = 1 << 8; // Z
|
|
bool is_down = (ret & (1 << RETRO_DEVICE_ID_JOYPAD_L )) ||
|
|
(ret & (1 << RETRO_DEVICE_ID_JOYPAD_L2));
|
|
if (is_down)
|
|
kcode[port] &= ~dc_key;
|
|
else
|
|
kcode[port] |= dc_key;
|
|
}
|
|
|
|
// C
|
|
{
|
|
uint32_t dc_key = 1 << 0; // C
|
|
bool is_down = (ret & (1 << RETRO_DEVICE_ID_JOYPAD_R)) ||
|
|
(ret & (1 << RETRO_DEVICE_ID_JOYPAD_R2));
|
|
if (is_down)
|
|
kcode[port] &= ~dc_key;
|
|
else
|
|
kcode[port] |= dc_key;
|
|
}
|
|
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_ASCIISTICK, RETRO_DEVICE_ID_JOYPAD_START, 3 ); // Start
|
|
|
|
// unused inputs
|
|
lt[port]=0;
|
|
rt[port]=0;
|
|
joyx[port]=0;
|
|
joyy[port]=0;
|
|
}
|
|
break;
|
|
|
|
case MDT_TwinStick:
|
|
{
|
|
int16_t ret = 0;
|
|
|
|
kcode[port] = 0xFFFF; // active-low
|
|
|
|
if ( device_type[port] == RETRO_DEVICE_TWINSTICK_SATURN )
|
|
{
|
|
// NOTE: This is a remapping of the RetroPad layout in the block below to make using a real
|
|
// Saturn Twin-Stick controller (via a USB adapter) less effort.
|
|
|
|
// The Saturn Twin-Stick identifies as a regular Saturn controller internally but with its controls
|
|
// wired to the two sticks without much rhyme or reason. The mapping below untangles that layout
|
|
// into DC compatible inputs, without requiring a change for the Reicast and Beetle Saturn cores.
|
|
|
|
// Hope that makes sense!!
|
|
|
|
// NOTE: the dc_bits below are the same, only the retro id values have been rearranged.
|
|
|
|
if (libretro_supports_bitmasks)
|
|
ret = input_cb(port, RETRO_DEVICE_TWINSTICK_SATURN, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
|
|
else
|
|
{
|
|
for (int id = RETRO_DEVICE_ID_JOYPAD_B; id <= RETRO_DEVICE_ID_JOYPAD_R3; ++id)
|
|
if (input_cb(port, RETRO_DEVICE_TWINSTICK_SATURN, 0, id))
|
|
ret |= (1 << id);
|
|
}
|
|
|
|
// left-stick
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK_SATURN, RETRO_DEVICE_ID_JOYPAD_UP, 4 );
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK_SATURN, RETRO_DEVICE_ID_JOYPAD_DOWN, 5 );
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK_SATURN, RETRO_DEVICE_ID_JOYPAD_LEFT, 6 );
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK_SATURN, RETRO_DEVICE_ID_JOYPAD_RIGHT, 7 );
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK_SATURN, RETRO_DEVICE_ID_JOYPAD_L2, 10 ); // left-trigger
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK_SATURN, RETRO_DEVICE_ID_JOYPAD_R2, 9 ); // left-turbo
|
|
|
|
// right-stick
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK_SATURN, RETRO_DEVICE_ID_JOYPAD_X, 12 ); // up
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK_SATURN, RETRO_DEVICE_ID_JOYPAD_L, 15 ); // right
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK_SATURN, RETRO_DEVICE_ID_JOYPAD_A, 13 ); // down
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK_SATURN, RETRO_DEVICE_ID_JOYPAD_Y, 14 ); // left
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK_SATURN, RETRO_DEVICE_ID_JOYPAD_B, 2 ); // right-trigger
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK_SATURN, RETRO_DEVICE_ID_JOYPAD_R, 1 ); // right-turbo
|
|
|
|
// misc control
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK_SATURN, RETRO_DEVICE_ID_JOYPAD_START, 3 );
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK_SATURN, RETRO_DEVICE_ID_JOYPAD_SELECT, 11 ); //D
|
|
}
|
|
else
|
|
{
|
|
int analog;
|
|
|
|
const int thresh = 11000; // about 33%, allows for 8-way movement
|
|
|
|
if (libretro_supports_bitmasks)
|
|
ret = input_cb(port, RETRO_DEVICE_TWINSTICK, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
|
|
else
|
|
{
|
|
for (int id = RETRO_DEVICE_ID_JOYPAD_B; id <= RETRO_DEVICE_ID_JOYPAD_R3; ++id)
|
|
if (input_cb(port, RETRO_DEVICE_TWINSTICK, 0, id))
|
|
ret |= (1 << id);
|
|
}
|
|
|
|
// LX
|
|
analog = input_cb( port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X );
|
|
if ( analog < -thresh )
|
|
kcode[port] &= ~( 1 << 6 ); // L
|
|
else if ( analog > thresh )
|
|
kcode[port] &= ~( 1 << 7 ); // R
|
|
else
|
|
{
|
|
// digital
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK, RETRO_DEVICE_ID_JOYPAD_LEFT, 6 );
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK, RETRO_DEVICE_ID_JOYPAD_RIGHT, 7 );
|
|
}
|
|
|
|
// LY
|
|
analog = input_cb( port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y );
|
|
if ( analog < -thresh )
|
|
kcode[port] &= ~( 1 << 4 ); // U
|
|
else if ( analog > thresh )
|
|
kcode[port] &= ~( 1 << 5 ); // D
|
|
else
|
|
{
|
|
// digital
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK, RETRO_DEVICE_ID_JOYPAD_UP, 4 );
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK, RETRO_DEVICE_ID_JOYPAD_DOWN, 5 );
|
|
}
|
|
|
|
// RX
|
|
analog = input_cb( port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X );
|
|
if ( analog < -thresh )
|
|
kcode[port] &= ~( 1 << 14 ); // L
|
|
else if ( analog > thresh )
|
|
kcode[port] &= ~( 1 << 15 ); // R
|
|
else
|
|
{
|
|
// digital
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK, RETRO_DEVICE_ID_JOYPAD_Y, 14 ); // left
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK, RETRO_DEVICE_ID_JOYPAD_A, 15 ); // right
|
|
}
|
|
|
|
// RY
|
|
analog = input_cb( port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y );
|
|
if ( analog < -thresh )
|
|
kcode[port] &= ~( 1 << 12 ); // U
|
|
else if ( analog > thresh )
|
|
kcode[port] &= ~( 1 << 13 ); // D
|
|
else
|
|
{
|
|
// digital
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK, RETRO_DEVICE_ID_JOYPAD_X, 12 ); // up
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK, RETRO_DEVICE_ID_JOYPAD_B, 13 ); // down
|
|
}
|
|
|
|
// left-stick buttons
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK, RETRO_DEVICE_ID_JOYPAD_L2, 10 ); // left-trigger
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK, RETRO_DEVICE_ID_JOYPAD_L, 9 ); // left-turbo
|
|
|
|
// right-stick buttons
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK, RETRO_DEVICE_ID_JOYPAD_R2, 2 ); // right-trigger
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK, RETRO_DEVICE_ID_JOYPAD_R, 1 ); // right-turbo
|
|
|
|
// misc control
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK, RETRO_DEVICE_ID_JOYPAD_START, 3 );
|
|
setDeviceButtonStateDirect(ret, port, RETRO_DEVICE_TWINSTICK, RETRO_DEVICE_ID_JOYPAD_SELECT, 11 ); //D
|
|
}
|
|
|
|
// unused inputs
|
|
lt[port]=0;
|
|
rt[port]=0;
|
|
joyx[port]=0;
|
|
joyy[port]=0;
|
|
}
|
|
break;
|
|
|
|
case MDT_LightGun:
|
|
{
|
|
//
|
|
// -- buttons
|
|
setDeviceButtonState(port, RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER);
|
|
setDeviceButtonState(port, RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_AUX_A);
|
|
setDeviceButtonState(port, RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_START);
|
|
setDeviceButtonState(port, RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_DPAD_UP);
|
|
setDeviceButtonState(port, RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_DPAD_DOWN);
|
|
setDeviceButtonState(port, RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_DPAD_LEFT);
|
|
setDeviceButtonState(port, RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT);
|
|
|
|
bool force_offscreen = false;
|
|
|
|
if (input_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_RELOAD))
|
|
{
|
|
force_offscreen = true;
|
|
kcode[port] &= ~DC_BTN_A;
|
|
}
|
|
|
|
if (force_offscreen || input_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN))
|
|
{
|
|
mo_x_abs[port] = -1000;
|
|
mo_y_abs[port] = -1000;
|
|
lightgun_params[port].offscreen = true;
|
|
|
|
lightgun_params[port].x = mo_x_abs[port];
|
|
lightgun_params[port].y = mo_y_abs[port];
|
|
}
|
|
else
|
|
{
|
|
updateLightgunCoordinates(port);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MDT_Mouse:
|
|
updateMouseState(port);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void UpdateInputState()
|
|
{
|
|
UpdateInputState(0);
|
|
UpdateInputState(1);
|
|
UpdateInputState(2);
|
|
UpdateInputState(3);
|
|
}
|
|
|
|
static void updateVibration(u32 port, float power, float inclination, u32 durationMs)
|
|
{
|
|
if (!rumble.set_rumble_state)
|
|
return;
|
|
|
|
vib_strength[port] = power;
|
|
|
|
rumble.set_rumble_state(port, RETRO_RUMBLE_STRONG, (u16)(65535 * power));
|
|
vib_stop_time[port] = get_time_ms() + durationMs;
|
|
vib_delta[port] = inclination;
|
|
}
|
|
|
|
u8 kb_key[4][6]; // normal keys pressed
|
|
u8 kb_shift[4]; // modifier keys pressed (bitmask)
|
|
static int kb_used;
|
|
|
|
static void release_key(unsigned dc_keycode)
|
|
{
|
|
if (dc_keycode == 0)
|
|
return;
|
|
|
|
if (kb_used > 0)
|
|
{
|
|
for (int i = 0; i < 6; i++)
|
|
{
|
|
if (kb_key[0][i] == dc_keycode)
|
|
{
|
|
kb_used--;
|
|
for (int j = i; j < 5; j++)
|
|
kb_key[0][j] = kb_key[0][j + 1];
|
|
kb_key[0][5] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void retro_keyboard_event(bool down, unsigned keycode, uint32_t character, uint16_t key_modifiers)
|
|
{
|
|
// Dreamcast keyboard emulation
|
|
if (keycode == RETROK_LSHIFT || keycode == RETROK_RSHIFT)
|
|
{
|
|
if (!down)
|
|
kb_shift[0] &= ~(0x02 | 0x20);
|
|
else
|
|
kb_shift[0] |= (0x02 | 0x20);
|
|
}
|
|
if (keycode == RETROK_LCTRL || keycode == RETROK_RCTRL)
|
|
{
|
|
if (!down)
|
|
kb_shift[0] &= ~(0x01 | 0x10);
|
|
else
|
|
kb_shift[0] |= (0x01 | 0x10);
|
|
}
|
|
// Make sure modifier keys are released
|
|
if ((key_modifiers & RETROKMOD_SHIFT) == 0)
|
|
{
|
|
release_key(kb_map[RETROK_LSHIFT]);
|
|
release_key(kb_map[RETROK_LSHIFT]);
|
|
}
|
|
if ((key_modifiers & RETROKMOD_CTRL) == 0)
|
|
{
|
|
release_key(kb_map[RETROK_LCTRL]);
|
|
release_key(kb_map[RETROK_RCTRL]);
|
|
}
|
|
|
|
u8 dc_keycode = kb_map[keycode];
|
|
if (dc_keycode != 0)
|
|
{
|
|
if (down)
|
|
{
|
|
if (kb_used < 6)
|
|
{
|
|
bool found = false;
|
|
for (int i = 0; !found && i < 6; i++)
|
|
{
|
|
if (kb_key[0][i] == dc_keycode)
|
|
found = true;
|
|
}
|
|
if (!found)
|
|
{
|
|
kb_key[0][kb_used] = dc_keycode;
|
|
kb_used++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
release_key(dc_keycode);
|
|
}
|
|
}
|
|
}
|
|
|
|
void fatal_error(const char* text, ...)
|
|
{
|
|
if (log_cb)
|
|
{
|
|
va_list args;
|
|
char temp[2048];
|
|
va_start(args, text);
|
|
vsprintf(temp, text, args);
|
|
va_end(args);
|
|
strcat(temp, "\n");
|
|
log_cb(RETRO_LOG_ERROR, temp);
|
|
}
|
|
}
|
|
|
|
void os_DebugBreak()
|
|
{
|
|
ERROR_LOG(COMMON, "DEBUGBREAK!");
|
|
//exit(-1);
|
|
#ifdef __SWITCH__
|
|
svcExitProcess();
|
|
#else
|
|
__builtin_trap();
|
|
#endif
|
|
}
|
|
|
|
static bool retro_set_eject_state(bool ejected)
|
|
{
|
|
disc_tray_open = ejected;
|
|
if (ejected)
|
|
{
|
|
DiscOpenLid();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
try {
|
|
return DiscSwap(disk_paths[disk_index]);
|
|
} catch (const FlycastException& e) {
|
|
ERROR_LOG(GDROM, "%s", e.what());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool retro_get_eject_state()
|
|
{
|
|
return disc_tray_open;
|
|
}
|
|
|
|
static unsigned retro_get_image_index()
|
|
{
|
|
return disk_index;
|
|
}
|
|
|
|
static bool retro_set_image_index(unsigned index)
|
|
{
|
|
disk_index = index;
|
|
if (disk_index >= disk_paths.size())
|
|
{
|
|
// No disk in drive
|
|
settings.content.path.clear();
|
|
return true;
|
|
}
|
|
settings.content.path = disk_paths[index];
|
|
|
|
if (disc_tray_open)
|
|
return true;
|
|
|
|
try {
|
|
return DiscSwap(settings.content.path);
|
|
} catch (const FlycastException& e) {
|
|
ERROR_LOG(GDROM, "%s", e.what());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static unsigned retro_get_num_images()
|
|
{
|
|
return disk_paths.size();
|
|
}
|
|
|
|
static bool retro_add_image_index()
|
|
{
|
|
disk_paths.push_back("");
|
|
disk_labels.push_back("");
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool retro_replace_image_index(unsigned index, const struct retro_game_info *info)
|
|
{
|
|
if (index >= disk_paths.size() || index >= disk_labels.size())
|
|
return false;
|
|
|
|
if (info == nullptr)
|
|
{
|
|
disk_paths.erase(disk_paths.begin() + index);
|
|
disk_labels.erase(disk_labels.begin() + index);
|
|
|
|
if (disk_index >= index && disk_index > 0)
|
|
disk_index--;
|
|
}
|
|
else
|
|
{
|
|
char disk_label[PATH_MAX];
|
|
disk_label[0] = '\0';
|
|
|
|
disk_paths[index] = info->path;
|
|
|
|
fill_short_pathname_representation(disk_label, info->path, sizeof(disk_label));
|
|
disk_labels[index] = disk_label;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool retro_set_initial_image(unsigned index, const char *path)
|
|
{
|
|
if (!path || *path == '\0')
|
|
return false;
|
|
|
|
disk_initial_index = index;
|
|
disk_initial_path = path;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool retro_get_image_path(unsigned index, char *path, size_t len)
|
|
{
|
|
if (len < 1)
|
|
return false;
|
|
|
|
if (index >= disk_paths.size())
|
|
return false;
|
|
|
|
if (disk_paths[index].empty())
|
|
return false;
|
|
|
|
strncpy(path, disk_paths[index].c_str(), len - 1);
|
|
path[len - 1] = '\0';
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool retro_get_image_label(unsigned index, char *label, size_t len)
|
|
{
|
|
if (len < 1)
|
|
return false;
|
|
|
|
if (index >= disk_paths.size() || index >= disk_labels.size())
|
|
return false;
|
|
|
|
if (disk_labels[index].empty())
|
|
return false;
|
|
|
|
strncpy(label, disk_labels[index].c_str(), len - 1);
|
|
label[len - 1] = '\0';
|
|
|
|
return true;
|
|
}
|
|
|
|
static void init_disk_control_interface()
|
|
{
|
|
unsigned dci_version = 0;
|
|
|
|
retro_disk_control_cb.set_eject_state = retro_set_eject_state;
|
|
retro_disk_control_cb.get_eject_state = retro_get_eject_state;
|
|
retro_disk_control_cb.set_image_index = retro_set_image_index;
|
|
retro_disk_control_cb.get_image_index = retro_get_image_index;
|
|
retro_disk_control_cb.get_num_images = retro_get_num_images;
|
|
retro_disk_control_cb.add_image_index = retro_add_image_index;
|
|
retro_disk_control_cb.replace_image_index = retro_replace_image_index;
|
|
|
|
retro_disk_control_ext_cb.set_eject_state = retro_set_eject_state;
|
|
retro_disk_control_ext_cb.get_eject_state = retro_get_eject_state;
|
|
retro_disk_control_ext_cb.set_image_index = retro_set_image_index;
|
|
retro_disk_control_ext_cb.get_image_index = retro_get_image_index;
|
|
retro_disk_control_ext_cb.get_num_images = retro_get_num_images;
|
|
retro_disk_control_ext_cb.add_image_index = retro_add_image_index;
|
|
retro_disk_control_ext_cb.replace_image_index = retro_replace_image_index;
|
|
retro_disk_control_ext_cb.set_initial_image = retro_set_initial_image;
|
|
retro_disk_control_ext_cb.get_image_path = retro_get_image_path;
|
|
retro_disk_control_ext_cb.get_image_label = retro_get_image_label;
|
|
|
|
disk_initial_index = 0;
|
|
disk_initial_path.clear();
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION, &dci_version) && (dci_version >= 1))
|
|
environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE, &retro_disk_control_ext_cb);
|
|
else
|
|
environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &retro_disk_control_cb);
|
|
}
|
|
|
|
static bool read_m3u(const char *file)
|
|
{
|
|
char line[PATH_MAX];
|
|
char name[PATH_MAX];
|
|
FILE *f = fopen(file, "r");
|
|
|
|
if (!f)
|
|
{
|
|
log_cb(RETRO_LOG_ERROR, "Could not read file\n");
|
|
return false;
|
|
}
|
|
|
|
while (fgets(line, sizeof(line), f) && disk_index <= disk_paths.size())
|
|
{
|
|
if (line[0] == '#')
|
|
continue;
|
|
|
|
char *carriage_return = strchr(line, '\r');
|
|
if (carriage_return)
|
|
*carriage_return = '\0';
|
|
|
|
char *newline = strchr(line, '\n');
|
|
if (newline)
|
|
*newline = '\0';
|
|
|
|
// Remove any beginning and ending quotes as these can cause issues when feeding the paths into command line later
|
|
if (line[0] == '"')
|
|
memmove(line, line + 1, strlen(line));
|
|
|
|
if (line[strlen(line) - 1] == '"')
|
|
line[strlen(line) - 1] = '\0';
|
|
|
|
if (line[0] != '\0')
|
|
{
|
|
char disk_label[PATH_MAX];
|
|
disk_label[0] = '\0';
|
|
|
|
if (path_is_absolute(line))
|
|
snprintf(name, sizeof(name), "%s", line);
|
|
else
|
|
snprintf(name, sizeof(name), "%s%s", g_roms_dir, line);
|
|
disk_paths.push_back(name);
|
|
|
|
fill_short_pathname_representation(disk_label, name, sizeof(disk_label));
|
|
disk_labels.push_back(disk_label);
|
|
|
|
disk_index++;
|
|
}
|
|
}
|
|
|
|
fclose(f);
|
|
return disk_index != 0;
|
|
}
|
|
|
|
void gui_display_notification(const char *msg, int duration)
|
|
{
|
|
retro_message retromsg;
|
|
retromsg.msg = msg;
|
|
retromsg.frames = duration / 17;
|
|
environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, &retromsg);
|
|
}
|
|
|
|
void os_RunInstance(int argc, const char *argv[]) { }
|