mirror of https://github.com/bsnes-emu/bsnes.git
Merged libretro target [Themaister and rtretiakov]
This commit is contained in:
parent
5ae1bcd973
commit
23467b5b1f
|
@ -0,0 +1,27 @@
|
|||
name := libretro.so
|
||||
flags += -std=c++17 -fpermissive -Wno-narrowing -Wno-multichar -fopenmp -g
|
||||
#flags += -DSFC_SUPERGAMEBOY
|
||||
|
||||
#include fc/GNUmakefile
|
||||
#include sfc/GNUmakefile
|
||||
#include ms/GNUmakefile
|
||||
#include md/GNUmakefile
|
||||
#include pce/GNUmakefile
|
||||
#include gb/GNUmakefile
|
||||
#include gba/GNUmakefile
|
||||
#include ws/GNUmakefile
|
||||
#include processor/GNUmakefile
|
||||
|
||||
# rules
|
||||
objects := libretro $(objects)
|
||||
objects := $(patsubst %,obj/%.o,$(objects))
|
||||
|
||||
obj/libretro.o: target-libretro/libretro.cpp
|
||||
|
||||
# targets
|
||||
build: $(objects)
|
||||
ifeq ($(platform),linux)
|
||||
$(strip $(compiler) -o out/bsnes_libretro.so -shared $(objects) -Wl,--no-undefined -Wl,--version-script=target-libretro/link.T -Wl,-Bdynamic -lpthread -ldl -lgomp)
|
||||
else ifeq ($(platform),windows)
|
||||
$(strip $(compiler) -o out/bsnes_libretro.dll -shared $(objects) -Wl,--no-undefined -Wl,--version-script=target-libretro/link.T -static-libgcc -static-libstdc++ -Wl,-Bstatic -lws2_32 -lpthread -lgomp -Wl,-Bdynamic)
|
||||
endif
|
|
@ -0,0 +1,501 @@
|
|||
#include "libretro.h"
|
||||
|
||||
static retro_environment_t environ_cb;
|
||||
static retro_video_refresh_t video_cb;
|
||||
static retro_audio_sample_t audio_cb;
|
||||
static retro_input_poll_t input_poll;
|
||||
static retro_input_state_t input_state;
|
||||
static retro_log_printf_t libretro_print;
|
||||
|
||||
#include "program.cpp"
|
||||
|
||||
#define RETRO_DEVICE_JOYPAD_MULTITAP RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 0)
|
||||
#define RETRO_DEVICE_LIGHTGUN_SUPER_SCOPE RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_LIGHTGUN, 0)
|
||||
#define RETRO_DEVICE_LIGHTGUN_JUSTIFIER RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_LIGHTGUN, 1)
|
||||
#define RETRO_DEVICE_LIGHTGUN_JUSTIFIERS RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_LIGHTGUN, 2)
|
||||
|
||||
static void flush_variables()
|
||||
{
|
||||
retro_variable variable = { "bsnes_blur_emulation", nullptr };
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &variable) && variable.value)
|
||||
{
|
||||
if (strcmp(variable.value, "ON") == 0)
|
||||
emulator->configure("Video/BlurEmulation", true);
|
||||
else if (strcmp(variable.value, "OFF") == 0)
|
||||
emulator->configure("Video/BlurEmulation", false);
|
||||
}
|
||||
|
||||
variable = { "bsnes_cpu_overclock", nullptr };
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &variable) && variable.value)
|
||||
{
|
||||
int val = atoi(variable.value);
|
||||
emulator->configure("Hacks/CPU/Overclock", val);
|
||||
}
|
||||
|
||||
variable = { "bsnes_cpu_sa1_overclock", nullptr };
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &variable) && variable.value)
|
||||
{
|
||||
int val = atoi(variable.value);
|
||||
emulator->configure("Hacks/SA1/Overclock", val);
|
||||
}
|
||||
|
||||
variable = { "bsnes_cpu_sfx_overclock", nullptr };
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &variable) && variable.value)
|
||||
{
|
||||
int val = atoi(variable.value);
|
||||
emulator->configure("Hacks/SuperFX/Overclock", val);
|
||||
}
|
||||
|
||||
variable = { "bsnes_ppu_fast", nullptr };
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &variable) && variable.value)
|
||||
{
|
||||
if (strcmp(variable.value, "ON") == 0)
|
||||
emulator->configure("Hacks/PPU/Fast", true);
|
||||
else if (strcmp(variable.value, "OFF") == 0)
|
||||
emulator->configure("Hacks/PPU/Fast", false);
|
||||
}
|
||||
|
||||
variable = { "bsnes_ppu_deinterlace", nullptr };
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &variable) && variable.value)
|
||||
{
|
||||
if (strcmp(variable.value, "ON") == 0)
|
||||
emulator->configure("Hacks/PPU/Deinterlace", true);
|
||||
else if (strcmp(variable.value, "OFF") == 0)
|
||||
emulator->configure("Hacks/PPU/Deinterlace", false);
|
||||
}
|
||||
|
||||
variable = { "bsnes_ppu_no_sprite_limit", nullptr };
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &variable) && variable.value)
|
||||
{
|
||||
if (strcmp(variable.value, "ON") == 0)
|
||||
emulator->configure("Hacks/PPU/NoSpriteLimit", true);
|
||||
else if (strcmp(variable.value, "OFF") == 0)
|
||||
emulator->configure("Hacks/PPU/NoSpriteLimit", false);
|
||||
}
|
||||
|
||||
variable = { "bsnes_ppu_show_overscan", nullptr };
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &variable) && variable.value)
|
||||
{
|
||||
if (strcmp(variable.value, "ON") == 0)
|
||||
program->overscan = true;
|
||||
else if (strcmp(variable.value, "OFF") == 0)
|
||||
program->overscan = false;
|
||||
}
|
||||
|
||||
variable = { "bsnes_mode7_scale", nullptr };
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &variable) && variable.value)
|
||||
{
|
||||
int val = variable.value[0] - '0';
|
||||
|
||||
if (val >= 1 && val <= 8)
|
||||
emulator->configure("Hacks/PPU/Mode7/Scale", val);
|
||||
}
|
||||
|
||||
variable = { "bsnes_mode7_perspective", nullptr };
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &variable) && variable.value)
|
||||
{
|
||||
if (strcmp(variable.value, "ON") == 0)
|
||||
emulator->configure("Hacks/PPU/Mode7/Perspective", true);
|
||||
else if (strcmp(variable.value, "OFF") == 0)
|
||||
emulator->configure("Hacks/PPU/Mode7/Perspective", false);
|
||||
}
|
||||
|
||||
variable = { "bsnes_mode7_supersample", nullptr };
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &variable) && variable.value)
|
||||
{
|
||||
if (strcmp(variable.value, "ON") == 0)
|
||||
emulator->configure("Hacks/PPU/Mode7/Supersample", true);
|
||||
else if (strcmp(variable.value, "OFF") == 0)
|
||||
emulator->configure("Hacks/PPU/Mode7/Supersample", false);
|
||||
}
|
||||
|
||||
variable = { "bsnes_mode7_mosaic", nullptr };
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &variable) && variable.value)
|
||||
{
|
||||
if (strcmp(variable.value, "ON") == 0)
|
||||
emulator->configure("Hacks/PPU/Mode7/Mosaic", true);
|
||||
else if (strcmp(variable.value, "OFF") == 0)
|
||||
emulator->configure("Hacks/PPU/Mode7/Mosaic", false);
|
||||
}
|
||||
|
||||
variable = { "bsnes_dsp_fast", nullptr };
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &variable) && variable.value)
|
||||
{
|
||||
if (strcmp(variable.value, "ON") == 0)
|
||||
emulator->configure("Hacks/DSP/Fast", true);
|
||||
else if (strcmp(variable.value, "OFF") == 0)
|
||||
emulator->configure("Hacks/DSP/Fast", false);
|
||||
}
|
||||
|
||||
variable = { "bsnes_dsp_cubic", nullptr };
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &variable) && variable.value)
|
||||
{
|
||||
if (strcmp(variable.value, "ON") == 0)
|
||||
emulator->configure("Hacks/DSP/Cubic", true);
|
||||
else if (strcmp(variable.value, "OFF") == 0)
|
||||
emulator->configure("Hacks/DSP/Cubic", false);
|
||||
}
|
||||
|
||||
variable = { "bsnes_coprocessor_delayed_sync", nullptr };
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &variable) && variable.value)
|
||||
{
|
||||
if (strcmp(variable.value, "ON") == 0)
|
||||
emulator->configure("Hacks/Coprocessor/DelayedSync", true);
|
||||
else if (strcmp(variable.value, "OFF") == 0)
|
||||
emulator->configure("Hacks/Coprocessor/DelayedSync", false);
|
||||
}
|
||||
|
||||
variable = { "bsnes_coprocessor_prefer_hle", nullptr };
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &variable) && variable.value)
|
||||
{
|
||||
if (strcmp(variable.value, "ON") == 0)
|
||||
emulator->configure("Hacks/Coprocessor/PreferHLE", true);
|
||||
else if (strcmp(variable.value, "OFF") == 0)
|
||||
emulator->configure("Hacks/Coprocessor/PreferHLE", false);
|
||||
}
|
||||
}
|
||||
|
||||
static void check_variables()
|
||||
{
|
||||
bool updated = false;
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
|
||||
flush_variables();
|
||||
}
|
||||
|
||||
static uint retro_device_to_snes(unsigned device)
|
||||
{
|
||||
switch (device)
|
||||
{
|
||||
default:
|
||||
case RETRO_DEVICE_NONE:
|
||||
return SuperFamicom::ID::Device::None;
|
||||
case RETRO_DEVICE_JOYPAD:
|
||||
return SuperFamicom::ID::Device::Gamepad;
|
||||
case RETRO_DEVICE_ANALOG:
|
||||
return SuperFamicom::ID::Device::Gamepad;
|
||||
case RETRO_DEVICE_JOYPAD_MULTITAP:
|
||||
return SuperFamicom::ID::Device::SuperMultitap;
|
||||
case RETRO_DEVICE_MOUSE:
|
||||
return SuperFamicom::ID::Device::Mouse;
|
||||
case RETRO_DEVICE_LIGHTGUN_SUPER_SCOPE:
|
||||
return SuperFamicom::ID::Device::SuperScope;
|
||||
case RETRO_DEVICE_LIGHTGUN_JUSTIFIER:
|
||||
return SuperFamicom::ID::Device::Justifier;
|
||||
case RETRO_DEVICE_LIGHTGUN_JUSTIFIERS:
|
||||
return SuperFamicom::ID::Device::Justifiers;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_controller_ports(unsigned port, unsigned device)
|
||||
{
|
||||
if (port < 2)
|
||||
emulator->connect(port, retro_device_to_snes(device));
|
||||
}
|
||||
|
||||
static void set_environment_info(retro_environment_t cb)
|
||||
{
|
||||
// TODO: Hook up RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO for Sufami/BSX/SGB?
|
||||
// IIRC, no known frontend actually hooks it up properly, so doubt there is any
|
||||
// real need for now.
|
||||
|
||||
static const retro_controller_description port_1[] = {
|
||||
{ "SNES Joypad", RETRO_DEVICE_JOYPAD },
|
||||
{ "SNES Mouse", RETRO_DEVICE_MOUSE },
|
||||
};
|
||||
|
||||
static const retro_controller_description port_2[] = {
|
||||
{ "SNES Joypad", RETRO_DEVICE_JOYPAD },
|
||||
{ "SNES Mouse", RETRO_DEVICE_MOUSE },
|
||||
{ "Multitap", RETRO_DEVICE_JOYPAD_MULTITAP },
|
||||
{ "SuperScope", RETRO_DEVICE_LIGHTGUN_SUPER_SCOPE },
|
||||
{ "Justifier", RETRO_DEVICE_LIGHTGUN_JUSTIFIER },
|
||||
{ "Justifiers", RETRO_DEVICE_LIGHTGUN_JUSTIFIERS },
|
||||
};
|
||||
|
||||
static const retro_controller_info ports[] = {
|
||||
{ port_1, 2 },
|
||||
{ port_2, 6 },
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, const_cast<retro_controller_info *>(ports));
|
||||
|
||||
static const retro_input_descriptor desc[] = {
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "X" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Y" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
|
||||
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
|
||||
|
||||
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
|
||||
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
|
||||
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
|
||||
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
|
||||
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
|
||||
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" },
|
||||
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "X" },
|
||||
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Y" },
|
||||
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L" },
|
||||
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" },
|
||||
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
|
||||
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
|
||||
|
||||
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
|
||||
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
|
||||
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
|
||||
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
|
||||
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
|
||||
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" },
|
||||
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "X" },
|
||||
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Y" },
|
||||
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L" },
|
||||
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" },
|
||||
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
|
||||
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
|
||||
|
||||
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
|
||||
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
|
||||
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
|
||||
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
|
||||
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
|
||||
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" },
|
||||
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "X" },
|
||||
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Y" },
|
||||
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L" },
|
||||
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" },
|
||||
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
|
||||
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
|
||||
|
||||
{ 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
|
||||
{ 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
|
||||
{ 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
|
||||
{ 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
|
||||
{ 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
|
||||
{ 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" },
|
||||
{ 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "X" },
|
||||
{ 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Y" },
|
||||
{ 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L" },
|
||||
{ 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" },
|
||||
{ 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
|
||||
{ 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
|
||||
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, const_cast<retro_input_descriptor *>(desc));
|
||||
|
||||
static const retro_variable vars[] = {
|
||||
{ "bsnes_blur_emulation", "Blur emulation; OFF|ON" },
|
||||
{ "bsnes_cpu_overclock", "CPU Overclocking; 100|110|120|130|140|150|160|170|180|190|200|210|220|230|240|250|260|270|280|290|300|310|320|330|340|350|360|370|380|390|400|10|20|30|40|50|60|70|80|90" },
|
||||
{ "bsnes_sa1_overclock", "SA1 Coprocessor Overclocking; 100|110|120|130|140|150|160|170|180|190|200|210|220|230|240|250|260|270|280|290|300|310|320|330|340|350|360|370|380|390|400|10|20|30|40|50|60|70|80|90" },
|
||||
{ "bsnes_sfx_overclock", "SuperFX Coprocessor Overclocking; 100|110|120|130|140|150|160|170|180|190|200|210|220|230|240|250|260|270|280|290|300|310|320|330|340|350|360|370|380|390|400|410|420|430|440|450|460|470|480|490|500|510|520|530|540|550|560|570|580|590|600|610|620|630|640|650|660|670|680|690|700|710|720|730|740|750|760|770|780|790|800|10|20|30|40|50|60|70|80|90" },
|
||||
{ "bsnes_ppu_fast", "PPU Fast mode; ON|OFF" },
|
||||
{ "bsnes_ppu_deinterlace", "PPU Deinterlace; ON|OFF" },
|
||||
{ "bsnes_ppu_no_sprite_limit", "PPU No sprite limit; OFF|ON" },
|
||||
{ "bsnes_ppu_show_overscan", "Show Overscan; OFF|ON" },
|
||||
{ "bsnes_mode7_scale", "HD Mode 7 Scale; 1x|2x|3x|4x|5x|6x|7x|8x" },
|
||||
{ "bsnes_mode7_perspective", "HD Mode 7 Perspective correction; ON|OFF" },
|
||||
{ "bsnes_mode7_supersample", "HD Mode 7 Supersampling; OFF|ON" },
|
||||
{ "bsnes_mode7_mosaic", "HD Mode 7 HD->SD Mosaic; ON|OFF" },
|
||||
{ "bsnes_dsp_fast", "DSP Fast mode; ON|OFF" },
|
||||
{ "bsnes_dsp_cubic", "DSP Cubic interpolation; ON|OFF" },
|
||||
{ "bsnes_coprocessor_delayed_sync", "Coprocessor Delayed Sync; ON|OFF" },
|
||||
{ "bsnes_coprocessor_prefer_hle", "Coprocessor Prefer HLE; ON|OFF" },
|
||||
{ nullptr },
|
||||
};
|
||||
cb(RETRO_ENVIRONMENT_SET_VARIABLES, const_cast<retro_variable *>(vars));
|
||||
}
|
||||
|
||||
RETRO_API void retro_set_environment(retro_environment_t cb)
|
||||
{
|
||||
environ_cb = cb;
|
||||
|
||||
retro_log_callback log = {};
|
||||
if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log) && log.log)
|
||||
libretro_print = log.log;
|
||||
|
||||
set_environment_info(cb);
|
||||
}
|
||||
|
||||
RETRO_API void retro_set_video_refresh(retro_video_refresh_t cb)
|
||||
{
|
||||
video_cb = cb;
|
||||
}
|
||||
|
||||
RETRO_API void retro_set_audio_sample(retro_audio_sample_t cb)
|
||||
{
|
||||
audio_cb = cb;
|
||||
}
|
||||
|
||||
RETRO_API void retro_set_audio_sample_batch(retro_audio_sample_batch_t)
|
||||
{
|
||||
}
|
||||
|
||||
RETRO_API void retro_set_input_poll(retro_input_poll_t cb)
|
||||
{
|
||||
input_poll = cb;
|
||||
}
|
||||
|
||||
RETRO_API void retro_set_input_state(retro_input_state_t cb)
|
||||
{
|
||||
input_state = cb;
|
||||
}
|
||||
|
||||
RETRO_API void retro_init()
|
||||
{
|
||||
emulator = new SuperFamicom::Interface;
|
||||
program = new Program;
|
||||
}
|
||||
|
||||
RETRO_API void retro_deinit()
|
||||
{
|
||||
delete program;
|
||||
}
|
||||
|
||||
RETRO_API unsigned retro_api_version()
|
||||
{
|
||||
return RETRO_API_VERSION;
|
||||
}
|
||||
|
||||
RETRO_API void retro_get_system_info(retro_system_info *info)
|
||||
{
|
||||
info->library_name = "bsnes";
|
||||
info->library_version = Emulator::Version;
|
||||
info->need_fullpath = false;
|
||||
info->valid_extensions = "smc|sfc";
|
||||
info->block_extract = false;
|
||||
}
|
||||
|
||||
RETRO_API void retro_get_system_av_info(struct retro_system_av_info *info)
|
||||
{
|
||||
info->geometry.base_width = 512; // accurate ppu
|
||||
info->geometry.base_height = program->overscan ? 480 : 448; // accurate ppu
|
||||
info->geometry.max_width = 2048; // 8x 256 for hd mode 7
|
||||
info->geometry.max_height = 1920; // 8x 240
|
||||
info->timing.fps = 60;
|
||||
info->timing.sample_rate = 48000;
|
||||
}
|
||||
|
||||
RETRO_API void retro_set_controller_port_device(unsigned port, unsigned device)
|
||||
{
|
||||
set_controller_ports(port, device);
|
||||
}
|
||||
|
||||
RETRO_API void retro_reset()
|
||||
{
|
||||
emulator->reset();
|
||||
}
|
||||
|
||||
RETRO_API void retro_run()
|
||||
{
|
||||
check_variables();
|
||||
emulator->run();
|
||||
}
|
||||
|
||||
RETRO_API size_t retro_serialize_size()
|
||||
{
|
||||
// To avoid having to serialize twice to query the size -> serialize.
|
||||
if (program->has_cached_serialize)
|
||||
{
|
||||
return program->cached_serialize.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
program->cached_serialize = emulator->serialize();
|
||||
program->has_cached_serialize = true;
|
||||
return program->cached_serialize.size();
|
||||
}
|
||||
}
|
||||
|
||||
RETRO_API bool retro_serialize(void *data, size_t size)
|
||||
{
|
||||
if (!program->has_cached_serialize)
|
||||
{
|
||||
program->cached_serialize = emulator->serialize();
|
||||
program->has_cached_serialize = true;
|
||||
}
|
||||
|
||||
if (program->cached_serialize.size() != size)
|
||||
return false;
|
||||
|
||||
memcpy(data, program->cached_serialize.data(), size);
|
||||
return true;
|
||||
}
|
||||
|
||||
RETRO_API bool retro_unserialize(const void *data, size_t size)
|
||||
{
|
||||
serializer s(static_cast<const uint8_t *>(data), size);
|
||||
program->has_cached_serialize = false;
|
||||
return emulator->unserialize(s);
|
||||
}
|
||||
|
||||
RETRO_API void retro_cheat_reset()
|
||||
{
|
||||
}
|
||||
|
||||
RETRO_API void retro_cheat_set(unsigned index, bool enabled, const char *code)
|
||||
{
|
||||
}
|
||||
|
||||
RETRO_API bool retro_load_game(const retro_game_info *game)
|
||||
{
|
||||
// bsnes uses 0RGB1555 internally but it is deprecated
|
||||
// let software conversion happen in frontend
|
||||
/*retro_pixel_format fmt = RETRO_PIXEL_FORMAT_0RGB1555;
|
||||
if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
|
||||
return false;*/
|
||||
|
||||
program->superFamicom.location = string(game->path);
|
||||
program->base_name = string(game->path);
|
||||
|
||||
emulator->configure("Audio/Frequency", 48000);
|
||||
|
||||
// turn into core options later
|
||||
emulator->configure("Hacks/CPU/Overclock", 100);
|
||||
emulator->configure("Hacks/SA1/Overclock", 100);
|
||||
emulator->configure("Hacks/SuperFX/Overclock", 100);
|
||||
|
||||
flush_variables();
|
||||
|
||||
program->load();
|
||||
|
||||
emulator->connect(SuperFamicom::ID::Port::Controller1, SuperFamicom::ID::Device::Gamepad);
|
||||
emulator->connect(SuperFamicom::ID::Port::Controller2, SuperFamicom::ID::Device::Gamepad);
|
||||
return true;
|
||||
}
|
||||
|
||||
RETRO_API bool retro_load_game_special(unsigned game_type,
|
||||
const struct retro_game_info *info, size_t num_info)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RETRO_API void retro_unload_game()
|
||||
{
|
||||
program->save();
|
||||
emulator->unload();
|
||||
}
|
||||
|
||||
RETRO_API unsigned retro_get_region()
|
||||
{
|
||||
return RETRO_REGION_NTSC;
|
||||
}
|
||||
|
||||
// Currently, there is no safe/sensible way to use the memory interface without severe hackery.
|
||||
// Rely on higan to load and save SRAM until there is really compelling reason not to.
|
||||
RETRO_API void *retro_get_memory_data(unsigned id)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RETRO_API size_t retro_get_memory_size(unsigned id)
|
||||
{
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
global: retro_*;
|
||||
local: *;
|
||||
};
|
|
@ -0,0 +1,370 @@
|
|||
#include <emulator/emulator.hpp>
|
||||
#include <sfc/interface/interface.hpp>
|
||||
#include <filter/filter.hpp>
|
||||
#include <lzma/lzma.hpp>
|
||||
#include <nall/directory.hpp>
|
||||
#include <nall/instance.hpp>
|
||||
#include <nall/decode/rle.hpp>
|
||||
#include <nall/decode/zip.hpp>
|
||||
#include <nall/encode/rle.hpp>
|
||||
#include <nall/encode/zip.hpp>
|
||||
#include <nall/hash/crc16.hpp>
|
||||
using namespace nall;
|
||||
|
||||
#include <heuristics/heuristics.hpp>
|
||||
#include <heuristics/heuristics.cpp>
|
||||
#include <heuristics/super-famicom.cpp>
|
||||
|
||||
#include "resources.hpp"
|
||||
|
||||
static Emulator::Interface *emulator;
|
||||
|
||||
struct Program : Emulator::Platform
|
||||
{
|
||||
Program();
|
||||
~Program();
|
||||
|
||||
auto open(uint id, string name, vfs::file::mode mode, bool required) -> shared_pointer<vfs::file> override;
|
||||
auto load(uint id, string name, string type, vector<string> options = {}) -> Emulator::Platform::Load override;
|
||||
auto videoFrame(const uint16* data, uint pitch, uint width, uint height, uint scale) -> void override;
|
||||
auto audioFrame(const float* samples, uint channels) -> void override;
|
||||
auto inputPoll(uint port, uint device, uint input) -> int16 override;
|
||||
auto inputRumble(uint port, uint device, uint input, bool enable) -> void override;
|
||||
|
||||
auto load() -> void;
|
||||
auto loadFile(string location) -> vector<uint8_t>;
|
||||
auto loadSuperFamicom(string location) -> bool;
|
||||
|
||||
auto save() -> void;
|
||||
|
||||
auto openRomSuperFamicom(string name, vfs::file::mode mode) -> shared_pointer<vfs::file>;
|
||||
|
||||
auto hackPatchMemory(vector<uint8_t>& data) -> void;
|
||||
|
||||
serializer cached_serialize;
|
||||
bool has_cached_serialize = false;
|
||||
|
||||
string base_name;
|
||||
|
||||
bool overscan = false;
|
||||
|
||||
public:
|
||||
struct Game {
|
||||
explicit operator bool() const { return (bool)location; }
|
||||
|
||||
string option;
|
||||
string location;
|
||||
string manifest;
|
||||
Markup::Node document;
|
||||
boolean patched;
|
||||
boolean verified;
|
||||
};
|
||||
|
||||
struct SuperFamicom : Game {
|
||||
string title;
|
||||
string region;
|
||||
vector<uint8_t> program;
|
||||
vector<uint8_t> data;
|
||||
vector<uint8_t> expansion;
|
||||
vector<uint8_t> firmware;
|
||||
} superFamicom;
|
||||
};
|
||||
|
||||
static Program *program = nullptr;
|
||||
|
||||
Program::Program()
|
||||
{
|
||||
Emulator::platform = this;
|
||||
}
|
||||
|
||||
Program::~Program()
|
||||
{
|
||||
delete emulator;
|
||||
}
|
||||
|
||||
auto Program::save() -> void
|
||||
{
|
||||
if(!emulator->loaded()) return;
|
||||
emulator->save();
|
||||
}
|
||||
|
||||
auto Program::open(uint id, string name, vfs::file::mode mode, bool required) -> shared_pointer<vfs::file>
|
||||
{
|
||||
shared_pointer<vfs::file> result;
|
||||
|
||||
if (name == "ipl.rom" && mode == vfs::file::mode::read) {
|
||||
result = vfs::memory::file::open(iplrom, sizeof(iplrom));
|
||||
}
|
||||
|
||||
if (name == "boards.bml" && mode == vfs::file::mode::read) {
|
||||
result = vfs::memory::file::open(boardsbml, sizeof(boardsbml));
|
||||
}
|
||||
|
||||
if (id == 1) { //Super Famicom
|
||||
if (name == "manifest.bml" && mode == vfs::file::mode::read) {
|
||||
result = vfs::memory::file::open(superFamicom.manifest.data<uint8_t>(), superFamicom.manifest.size());
|
||||
}
|
||||
else if (name == "program.rom" && mode == vfs::file::mode::read) {
|
||||
result = vfs::memory::file::open(superFamicom.program.data(), superFamicom.program.size());
|
||||
}
|
||||
else if (name == "data.rom" && mode == vfs::file::mode::read) {
|
||||
result = vfs::memory::file::open(superFamicom.data.data(), superFamicom.data.size());
|
||||
}
|
||||
else if (name == "expansion.rom" && mode == vfs::file::mode::read) {
|
||||
result = vfs::memory::file::open(superFamicom.expansion.data(), superFamicom.expansion.size());
|
||||
}
|
||||
else {
|
||||
result = openRomSuperFamicom(name, mode);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
auto Program::load() -> void {
|
||||
emulator->unload();
|
||||
emulator->load();
|
||||
emulator->power();
|
||||
}
|
||||
|
||||
auto Program::load(uint id, string name, string type, vector<string> options) -> Emulator::Platform::Load {
|
||||
if (loadSuperFamicom(superFamicom.location))
|
||||
{
|
||||
return {id, superFamicom.option};
|
||||
}
|
||||
return { id, options(0) };
|
||||
}
|
||||
|
||||
auto Program::videoFrame(const uint16* data, uint pitch, uint width, uint height, uint scale) -> void {
|
||||
if (!overscan)
|
||||
{
|
||||
uint multiplier = height / 240;
|
||||
data += 8 * (pitch >> 1) * multiplier;
|
||||
height -= 16 * multiplier;
|
||||
}
|
||||
video_cb(data, width, height, pitch);
|
||||
}
|
||||
|
||||
// Double the fun!
|
||||
static int16_t d2i16(double v)
|
||||
{
|
||||
v *= 0x8000;
|
||||
if (v > 0x7fff)
|
||||
v = 0x7fff;
|
||||
else if (v < -0x8000)
|
||||
v = -0x8000;
|
||||
return int16_t(floor(v + 0.5));
|
||||
}
|
||||
|
||||
auto Program::audioFrame(const float* samples, uint channels) -> void
|
||||
{
|
||||
int16_t left = d2i16(samples[0]);
|
||||
int16_t right = d2i16(samples[1]);
|
||||
audio_cb(left, right);
|
||||
}
|
||||
|
||||
auto pollInputDevices(uint port, uint device, uint input) -> int16
|
||||
{
|
||||
// TODO: This will need to be remapped on a per-system basis.
|
||||
unsigned libretro_port;
|
||||
unsigned libretro_id;
|
||||
unsigned libretro_device;
|
||||
unsigned libretro_index = 0;
|
||||
|
||||
static const unsigned joypad_mapping[] = {
|
||||
RETRO_DEVICE_ID_JOYPAD_UP,
|
||||
RETRO_DEVICE_ID_JOYPAD_DOWN,
|
||||
RETRO_DEVICE_ID_JOYPAD_LEFT,
|
||||
RETRO_DEVICE_ID_JOYPAD_RIGHT,
|
||||
RETRO_DEVICE_ID_JOYPAD_B,
|
||||
RETRO_DEVICE_ID_JOYPAD_A,
|
||||
RETRO_DEVICE_ID_JOYPAD_Y,
|
||||
RETRO_DEVICE_ID_JOYPAD_X,
|
||||
RETRO_DEVICE_ID_JOYPAD_L,
|
||||
RETRO_DEVICE_ID_JOYPAD_R,
|
||||
RETRO_DEVICE_ID_JOYPAD_SELECT,
|
||||
RETRO_DEVICE_ID_JOYPAD_START,
|
||||
};
|
||||
|
||||
static const unsigned mouse_mapping[] = {
|
||||
RETRO_DEVICE_ID_MOUSE_X,
|
||||
RETRO_DEVICE_ID_MOUSE_Y,
|
||||
RETRO_DEVICE_ID_MOUSE_LEFT,
|
||||
RETRO_DEVICE_ID_MOUSE_RIGHT,
|
||||
};
|
||||
|
||||
switch (port)
|
||||
{
|
||||
case SuperFamicom::ID::Port::Controller1:
|
||||
libretro_port = 0;
|
||||
break;
|
||||
case SuperFamicom::ID::Port::Controller2:
|
||||
libretro_port = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (device)
|
||||
{
|
||||
case SuperFamicom::ID::Device::Gamepad:
|
||||
libretro_device = RETRO_DEVICE_JOYPAD;
|
||||
libretro_id = joypad_mapping[input];
|
||||
break;
|
||||
|
||||
case SuperFamicom::ID::Device::Mouse:
|
||||
libretro_device = RETRO_DEVICE_MOUSE;
|
||||
libretro_id = mouse_mapping[input];
|
||||
break;
|
||||
|
||||
case SuperFamicom::ID::Device::SuperMultitap:
|
||||
libretro_device = RETRO_DEVICE_JOYPAD; // Maps to player [2, 5].
|
||||
libretro_port += input / 12;
|
||||
libretro_id = joypad_mapping[input % 12];
|
||||
break;
|
||||
|
||||
// TODO: SuperScope/Justifiers.
|
||||
// Do we care? The v94 port hasn't hooked them up. :)
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return input_state(libretro_port, libretro_device, libretro_index, libretro_id);
|
||||
}
|
||||
|
||||
auto Program::inputPoll(uint port, uint device, uint input) -> int16
|
||||
{
|
||||
return pollInputDevices(port, device, input);
|
||||
}
|
||||
|
||||
auto Program::inputRumble(uint port, uint device, uint input, bool enable) -> void
|
||||
{
|
||||
}
|
||||
|
||||
auto Program::openRomSuperFamicom(string name, vfs::file::mode mode) -> shared_pointer<vfs::file>
|
||||
{
|
||||
if(name == "program.rom" && mode == vfs::file::mode::read)
|
||||
{
|
||||
return vfs::memory::file::open(superFamicom.program.data(), superFamicom.program.size());
|
||||
}
|
||||
|
||||
if(name == "data.rom" && mode == vfs::file::mode::read)
|
||||
{
|
||||
return vfs::memory::file::open(superFamicom.data.data(), superFamicom.data.size());
|
||||
}
|
||||
|
||||
if(name == "expansion.rom" && mode == vfs::file::mode::read)
|
||||
{
|
||||
return vfs::memory::file::open(superFamicom.expansion.data(), superFamicom.expansion.size());
|
||||
}
|
||||
|
||||
if(name == "save.ram")
|
||||
{
|
||||
string save_path;
|
||||
|
||||
auto suffix = Location::suffix(base_name);
|
||||
auto base = Location::base(base_name);
|
||||
|
||||
const char *save = nullptr;
|
||||
if (environ_cb && environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &save) && save)
|
||||
save_path = { string(save).transform("\\", "/"), "/", base.trimRight(suffix, 1L), ".srm" };
|
||||
else
|
||||
save_path = { base_name.trimRight(suffix, 1L), ".srm" };
|
||||
|
||||
return vfs::fs::file::open(save_path, mode);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Program::loadFile(string location) -> vector<uint8_t>
|
||||
{
|
||||
if(Location::suffix(location).downcase() == ".zip") {
|
||||
Decode::ZIP archive;
|
||||
if(archive.open(location)) {
|
||||
for(auto& file : archive.file) {
|
||||
auto type = Location::suffix(file.name).downcase();
|
||||
if(type == ".sfc" || type == ".smc" || type == ".gb" || type == ".gbc" || type == ".bs" || type == ".st") {
|
||||
return archive.extract(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
else if(Location::suffix(location).downcase() == ".7z") {
|
||||
return LZMA::extract(location);
|
||||
}
|
||||
else {
|
||||
return file::read(location);
|
||||
}
|
||||
}
|
||||
|
||||
auto Program::loadSuperFamicom(string location) -> bool
|
||||
{
|
||||
string manifest;
|
||||
vector<uint8_t> rom;
|
||||
|
||||
manifest = file::read({Location::notsuffix(location), ".bml"});
|
||||
|
||||
rom = loadFile(location);
|
||||
if(rom.size() < 0x8000) return false;
|
||||
|
||||
//assume ROM and IPS agree on whether a copier header is present
|
||||
//superFamicom.patched = applyPatchIPS(rom, location);
|
||||
if((rom.size() & 0x7fff) == 512) {
|
||||
//remove copier header
|
||||
memory::move(&rom[0], &rom[512], rom.size() - 512);
|
||||
rom.resize(rom.size() - 512);
|
||||
}
|
||||
|
||||
auto heuristics = Heuristics::SuperFamicom(rom, location);
|
||||
auto sha256 = Hash::SHA256(rom).digest();
|
||||
|
||||
superFamicom.title = heuristics.title();
|
||||
superFamicom.region = heuristics.videoRegion();
|
||||
superFamicom.manifest = manifest ? manifest : heuristics.manifest();
|
||||
|
||||
hackPatchMemory(rom);
|
||||
superFamicom.document = BML::unserialize(superFamicom.manifest);
|
||||
superFamicom.location = location;
|
||||
|
||||
uint offset = 0;
|
||||
if(auto size = heuristics.programRomSize()) {
|
||||
superFamicom.program.resize(size);
|
||||
memory::copy(&superFamicom.program[0], &rom[offset], size);
|
||||
offset += size;
|
||||
}
|
||||
if(auto size = heuristics.dataRomSize()) {
|
||||
superFamicom.data.resize(size);
|
||||
memory::copy(&superFamicom.data[0], &rom[offset], size);
|
||||
offset += size;
|
||||
}
|
||||
if(auto size = heuristics.expansionRomSize()) {
|
||||
superFamicom.expansion.resize(size);
|
||||
memory::copy(&superFamicom.expansion[0], &rom[offset], size);
|
||||
offset += size;
|
||||
}
|
||||
if(auto size = heuristics.firmwareRomSize()) {
|
||||
superFamicom.firmware.resize(size);
|
||||
memory::copy(&superFamicom.firmware[0], &rom[offset], size);
|
||||
offset += size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
auto Program::hackPatchMemory(vector<uint8_t>& data) -> void
|
||||
{
|
||||
auto title = superFamicom.title;
|
||||
|
||||
if(title == "Satellaview BS-X" && data.size() >= 0x100000) {
|
||||
//BS-X: Sore wa Namae o Nusumareta Machi no Monogatari (JPN) (1.1)
|
||||
//disable limited play check for BS Memory flash cartridges
|
||||
//benefit: allow locked out BS Memory flash games to play without manual header patching
|
||||
//detriment: BS Memory ROM cartridges will cause the game to hang in the load menu
|
||||
if(data[0x4a9b] == 0x10) data[0x4a9b] = 0x80;
|
||||
if(data[0x4d6d] == 0x10) data[0x4d6d] = 0x80;
|
||||
if(data[0x4ded] == 0x10) data[0x4ded] = 0x80;
|
||||
if(data[0x4e9a] == 0x10) data[0x4e9a] = 0x80;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue