mirror of
https://github.com/libretro/supafaust.git
synced 2026-10-17 15:24:55 +00:00
833 lines
21 KiB
C++
833 lines
21 KiB
C++
#include <mednafen/mednafen.h>
|
|
#include <mednafen/string/string.h>
|
|
#include <mednafen/MemoryStream.h>
|
|
#include <mednafen/ExtMemStream.h>
|
|
#include <mednafen/FileStream.h>
|
|
#include <mednafen/MThreading.h>
|
|
|
|
#include "libretro.h"
|
|
|
|
using namespace Mednafen;
|
|
//
|
|
//
|
|
//
|
|
static struct
|
|
{
|
|
retro_environment_t environment;
|
|
retro_video_refresh_t video_refresh;
|
|
retro_audio_sample_t audio_sample;
|
|
retro_audio_sample_batch_t audio_sample_batch;
|
|
retro_input_poll_t input_poll;
|
|
retro_input_state_t input_state;
|
|
//
|
|
retro_log_printf_t log;
|
|
} cb;
|
|
|
|
static int Initialized = 0;
|
|
static bool libretro_supports_bitmasks = false;
|
|
|
|
// MDFN_NOTICE_STATUS, MDFN_NOTICE_WARNING, MDFN_NOTICE_ERROR
|
|
void Mednafen::MDFND_OutputNotice(MDFN_NoticeType t, const char* s)
|
|
{
|
|
if(cb.log && Initialized)
|
|
{
|
|
retro_log_level rll = RETRO_LOG_DEBUG;
|
|
|
|
switch(t)
|
|
{
|
|
case MDFN_NOTICE_STATUS: rll = RETRO_LOG_INFO; break;
|
|
case MDFN_NOTICE_WARNING: rll = RETRO_LOG_WARN; break;
|
|
case MDFN_NOTICE_ERROR: rll = RETRO_LOG_ERROR; break;
|
|
}
|
|
cb.log(rll, "%s", s);
|
|
}
|
|
else
|
|
printf("%s\n", s);
|
|
}
|
|
|
|
// Output from MDFN_printf(); fairly verbose informational messages.
|
|
void Mednafen::MDFND_OutputInfo(const char* s)
|
|
{
|
|
if(cb.log)
|
|
cb.log(RETRO_LOG_INFO, "%s", s);
|
|
else
|
|
fputs(s, stdout);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
static MDFNGI *cgi = nullptr;
|
|
static std::unique_ptr<int32[]> lw;
|
|
static std::unique_ptr<MDFN_Surface> surf;
|
|
static uint8* port_data[16] = { 0 };
|
|
static size_t ports_active;
|
|
|
|
static double SoundRate;
|
|
static int32 SoundBufMaxSize;
|
|
static std::unique_ptr<int16[]> SoundBuf;
|
|
|
|
static size_t SaveStateSize;
|
|
|
|
static std::vector<std::string> Cheats;
|
|
static bool CheatsChanged;
|
|
|
|
static uint64 affinity_save;
|
|
//
|
|
//
|
|
//
|
|
static MDFN_COLD void Cleanup(void)
|
|
{
|
|
if(cgi)
|
|
{
|
|
MDFNI_CloseGame();
|
|
cgi = nullptr;
|
|
}
|
|
|
|
lw.reset(nullptr);
|
|
surf.reset(nullptr);
|
|
|
|
for(size_t i = 0; i < 16; i++)
|
|
port_data[i] = nullptr;
|
|
|
|
SoundBufMaxSize = 0;
|
|
SoundBuf.reset(nullptr);
|
|
|
|
if(affinity_save)
|
|
{
|
|
MThreading::Thread_SetAffinity(nullptr, affinity_save);
|
|
//
|
|
affinity_save = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
static const struct retro_variable options[] =
|
|
{
|
|
{ "supafaust_pixel_format", "Pixel format; rgb565|xrgb8888|0rgb1555" },
|
|
{ "supafaust_correct_aspect", "Correct pixel aspect ratio; enabled|disabled|force_ntsc|force_pal" },
|
|
{ "supafaust_h_filter", "Horizontal blend/double filter; phr256blend_auto512|phr256blend_512|512_blend|512|phr256blend" },
|
|
{ "supafaust_deinterlacer", "Deinterlacer; bob_offset|weave|blend" },
|
|
|
|
{ "supafaust_slstart", "First displayed scanline in NTSC mode; 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16" },
|
|
{ "supafaust_slend", "Last displayed scanline in NTSC mode; 223|222|221|220|219|218|217|216|215|214|213|212|211|210|209|208|207" },
|
|
|
|
{ "supafaust_slstartp", "First displayed scanline in PAL mode; 0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24" },
|
|
{ "supafaust_slendp", "Last displayed scanline in PAL mode; 238|237|236|235|234|233|232|231|230|229|228|227|226|225|224|223|222|221|220|219|218|217|216|215|214" },
|
|
|
|
{ "supafaust_region", "Region/Type of SNES to emulate; auto|ntsc|pal|ntsc_lie_auto|pal_lie_auto|ntsc_lie_pal|pal_lie_ntsc" },
|
|
|
|
{ "supafaust_frame_begin_vblank", "Begin frame in SNES VBlank to lower latency; enabled|disabled" },
|
|
{ "supafaust_run_ahead", "Enable 1-frame run-ahead; disabled|video|video+audio" },
|
|
|
|
{ "supafaust_multitap", "Enable multitap; disabled|port1|port2|port1+port2" },
|
|
|
|
{ "supafaust_cx4_clock_rate", "CX4 clock rate %; 100|125|150|175|200|250|300|400|500" },
|
|
{ "supafaust_superfx_clock_rate", "SuperFX clock rate %; 100|125|150|175|200|250|300|400|500|95" },
|
|
{ "supafaust_superfx_icache", "Emulate SuperFX instruction cache; disabled|enabled" },
|
|
|
|
{ "supafaust_audio_rate", "Internal resampler(output rate); disabled|44100|48000|96000" },
|
|
|
|
{ "supafaust_thread_affinity_emu", "Emulation thread affinity mask; 0x2|0x0|0x1|0x3|0x4|0x5|0x6|0x7|0x8|0x9|0xa|0xb|0xc|0xd|0xe|0xf" }, // Affinity is set in retro_load_game() and restored in retro_unload_game(). Set to 0 to disable changing affinity.
|
|
{ "supafaust_thread_affinity_ppu", "PPU render thread affinity mask; 0x1|0x0|0x2|0x3|0x4|0x5|0x6|0x7|0x8|0x9|0xa|0xb|0xc|0xd|0xe|0xf" },
|
|
|
|
{ "supafaust_renderer", "PPU renderer; mt|st" },
|
|
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
static std::map<std::string, std::string> options_defaults;
|
|
|
|
MDFN_COLD RETRO_API void retro_set_environment(retro_environment_t v)
|
|
{
|
|
cb.environment = v;
|
|
cb.environment(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)options);
|
|
}
|
|
|
|
MDFN_COLD RETRO_API void retro_set_video_refresh(retro_video_refresh_t v)
|
|
{
|
|
cb.video_refresh = v;
|
|
}
|
|
|
|
MDFN_COLD RETRO_API void retro_set_audio_sample(retro_audio_sample_t v)
|
|
{
|
|
cb.audio_sample = v;
|
|
}
|
|
|
|
MDFN_COLD RETRO_API void retro_set_audio_sample_batch(retro_audio_sample_batch_t v)
|
|
{
|
|
cb.audio_sample_batch = v;
|
|
}
|
|
|
|
MDFN_COLD RETRO_API void retro_set_input_poll(retro_input_poll_t v)
|
|
{
|
|
cb.input_poll = v;
|
|
}
|
|
|
|
MDFN_COLD RETRO_API void retro_set_input_state(retro_input_state_t v)
|
|
{
|
|
cb.input_state = v;
|
|
}
|
|
|
|
static const char* get_option(const char* lr_name)
|
|
{
|
|
struct retro_variable rv;
|
|
|
|
rv.key = lr_name;
|
|
rv.value = NULL;
|
|
|
|
cb.environment(RETRO_ENVIRONMENT_GET_VARIABLE, &rv);
|
|
if(!rv.value)
|
|
{
|
|
rv.value = options_defaults[lr_name].c_str();
|
|
fprintf(stderr, "RETRO_ENVIRONMENT_GET_VARIABLE for \"%s\" failed, using default of \"%s\".\n", lr_name, rv.value);
|
|
}
|
|
|
|
return rv.value;
|
|
}
|
|
|
|
static long long get_option_signed(const char* lr_name)
|
|
{
|
|
const char* v = get_option(lr_name);
|
|
long long ret;
|
|
char* eptr = NULL;
|
|
|
|
if(!MDFN_strazicmp(v, "disabled"))
|
|
return 0;
|
|
else if(!MDFN_strazicmp(v, "enabled"))
|
|
return 1;
|
|
|
|
ret = strtoll(v, &eptr, 0);
|
|
|
|
if(eptr == v || !eptr || *eptr != 0)
|
|
{
|
|
fprintf(stderr, "Invalid integer for option \"%s\": %s", lr_name, v);
|
|
abort();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static unsigned long long get_option_unsigned(const char* lr_name)
|
|
{
|
|
const char* v = get_option(lr_name);
|
|
unsigned long long ret;
|
|
char* eptr = NULL;
|
|
|
|
if(!MDFN_strazicmp(v, "disabled"))
|
|
return 0;
|
|
else if(!MDFN_strazicmp(v, "enabled"))
|
|
return 1;
|
|
|
|
ret = strtoull(v, &eptr, 0);
|
|
|
|
if(eptr == v || !eptr || *eptr != 0)
|
|
{
|
|
fprintf(stderr, "Invalid integer for option \"%s\": %s", lr_name, v);
|
|
abort();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void pipe_option(const char* lr_name, const char* mdfn_name)
|
|
{
|
|
MDFNI_SetSetting(mdfn_name, get_option(lr_name));
|
|
}
|
|
|
|
static void pipe_option_bool(const char* lr_name, const char* mdfn_name)
|
|
{
|
|
int v = get_option_signed(lr_name);
|
|
|
|
MDFNI_SetSetting(mdfn_name, v ? "1" : "0");
|
|
}
|
|
|
|
static void check_variables(bool loaded)
|
|
{
|
|
pipe_option("supafaust_slstart", "snes_faust.slstart");
|
|
pipe_option("supafaust_slend", "snes_faust.slend");
|
|
pipe_option("supafaust_slstartp", "snes_faust.slstartp");
|
|
pipe_option("supafaust_slendp", "snes_faust.slendp");
|
|
}
|
|
|
|
MDFN_COLD RETRO_API void retro_init(void)
|
|
{
|
|
assert(!Initialized);
|
|
//
|
|
MDFNI_Initialize();
|
|
|
|
//
|
|
assert(cb.environment);
|
|
|
|
{
|
|
//
|
|
retro_log_callback lcb;
|
|
|
|
memset(&lcb, 0, sizeof(lcb));
|
|
if(cb.environment(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &lcb))
|
|
{
|
|
cb.log = lcb.log;
|
|
}
|
|
}
|
|
//
|
|
if (cb.environment(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL))
|
|
{
|
|
libretro_supports_bitmasks = true;
|
|
}
|
|
//
|
|
{
|
|
uint64 squirks = RETRO_SERIALIZATION_QUIRK_ENDIAN_DEPENDENT | RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT;
|
|
|
|
cb.environment(RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS, &squirks);
|
|
}
|
|
//
|
|
Initialized = 1;
|
|
}
|
|
|
|
MDFN_COLD RETRO_API void retro_deinit(void)
|
|
{
|
|
assert(Initialized == 1);
|
|
//
|
|
MDFNI_Kill();
|
|
//
|
|
libretro_supports_bitmasks = false;
|
|
//
|
|
Initialized = -1;
|
|
}
|
|
|
|
RETRO_API unsigned retro_api_version(void)
|
|
{
|
|
return RETRO_API_VERSION;
|
|
}
|
|
|
|
MDFN_COLD RETRO_API void retro_get_system_info(retro_system_info* info)
|
|
{
|
|
assert(info);
|
|
//
|
|
memset(info, 0, sizeof(*info));
|
|
info->library_name = "Beetle Supafaust";
|
|
info->library_version = MEDNAFEN_VERSION GIT_VERSION;
|
|
info->valid_extensions = "smc|swc|sfc|fig";
|
|
info->need_fullpath = false;
|
|
info->block_extract = false;
|
|
}
|
|
|
|
MDFN_COLD RETRO_API void retro_get_system_av_info(retro_system_av_info* info)
|
|
{
|
|
assert(cgi);
|
|
assert(info);
|
|
//
|
|
info->geometry.base_width = cgi->nominal_width;
|
|
info->geometry.base_height = cgi->nominal_height;
|
|
info->geometry.max_width = cgi->fb_width;
|
|
info->geometry.max_height = cgi->fb_height;
|
|
|
|
info->geometry.aspect_ratio = (float)cgi->nominal_width / cgi->nominal_height;
|
|
//
|
|
info->timing.fps = (double)cgi->fps / (65536 * 256);
|
|
info->timing.sample_rate = SoundRate;
|
|
}
|
|
|
|
RETRO_API void retro_set_controller_port_device(unsigned port, unsigned device)
|
|
{
|
|
|
|
}
|
|
|
|
RETRO_API void retro_reset(void)
|
|
{
|
|
MDFNI_Reset();
|
|
}
|
|
|
|
static void UpdateInput(void)
|
|
{
|
|
cb.input_poll();
|
|
|
|
for(size_t port = 0; port < ports_active; port++)
|
|
{
|
|
uint16 bs;
|
|
if (libretro_supports_bitmasks)
|
|
{
|
|
bs = cb.input_state(port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
|
|
}
|
|
else
|
|
{
|
|
bs = 0;
|
|
//
|
|
for(unsigned i = RETRO_DEVICE_ID_JOYPAD_B; i < (RETRO_DEVICE_ID_JOYPAD_R3 + 1); i++)
|
|
bs |= (bool)cb.input_state(port, RETRO_DEVICE_JOYPAD, 0, i) << i;
|
|
//
|
|
}
|
|
MDFN_en16lsb<false>(port_data[port], bs);
|
|
}
|
|
}
|
|
|
|
void Mednafen::MDFND_MidSync(const EmulateSpecStruct* espec, const unsigned flags)
|
|
{
|
|
const int16* const sbuf = espec->SoundBuf + espec->SoundBufSizeALMS;
|
|
const int32 scount = espec->SoundBufSize - espec->SoundBufSizeALMS;
|
|
|
|
if(scount)
|
|
cb.audio_sample_batch(sbuf, scount);
|
|
//
|
|
if(flags & MIDSYNC_FLAG_UPDATE_INPUT)
|
|
UpdateInput();
|
|
}
|
|
|
|
static INLINE MDFN_PixelFormat RPFtoMPF(const retro_pixel_format rpf)
|
|
{
|
|
MDFN_PixelFormat ret;
|
|
|
|
if(rpf == RETRO_PIXEL_FORMAT_RGB565)
|
|
{
|
|
ret = MDFN_PixelFormat::RGB16_565;
|
|
}
|
|
else if(rpf == RETRO_PIXEL_FORMAT_0RGB1555)
|
|
{
|
|
ret = MDFN_PixelFormat::IRGB16_1555;
|
|
}
|
|
else if(rpf == RETRO_PIXEL_FORMAT_XRGB8888)
|
|
{
|
|
ret = MDFN_PixelFormat::ARGB32_8888;
|
|
}
|
|
else
|
|
{
|
|
assert(0);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static NO_INLINE void DoFrame(MDFN_Surface* s)
|
|
{
|
|
UpdateInput();
|
|
//
|
|
EmulateSpecStruct espec;
|
|
//int lr_avenable = 0x3;
|
|
|
|
//if(!cb.environment(RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE, &lr_avenable))
|
|
// lr_avenable = 0x3;
|
|
|
|
lw[0] = ~0; // Must be ~0 and not another value.
|
|
espec.surface = s;
|
|
espec.LineWidths = lw.get();
|
|
espec.skip = false; //!(lr_avenable & 0x1); // Skips drawing the frame if true; espec.surface and espec.LineWidths must still be valid when true, however.
|
|
|
|
espec.SoundRate = SoundRate;
|
|
espec.SoundBuf = SoundBuf.get(); // Should have enough room for 100ms of sound to be on the safe side.
|
|
espec.SoundBufMaxSize = SoundBufMaxSize;
|
|
|
|
MDFNI_Emulate(&espec);
|
|
//
|
|
//
|
|
//
|
|
if(espec.surface->format.opp == 2)
|
|
{
|
|
cb.video_refresh(espec.surface->pix<uint16>() + espec.DisplayRect.x + espec.DisplayRect.y * espec.surface->pitchinpix,
|
|
(lw[0] == ~0) ? espec.DisplayRect.w : lw[espec.DisplayRect.y],
|
|
espec.DisplayRect.h,
|
|
espec.surface->pitchinpix * sizeof(uint16));
|
|
}
|
|
else
|
|
{
|
|
cb.video_refresh(espec.surface->pix<uint32>() + espec.DisplayRect.x + espec.DisplayRect.y * espec.surface->pitchinpix,
|
|
(lw[0] == ~0) ? espec.DisplayRect.w : lw[espec.DisplayRect.y],
|
|
espec.DisplayRect.h,
|
|
espec.surface->pitchinpix * sizeof(uint32));
|
|
}
|
|
}
|
|
|
|
RETRO_API void retro_run(void)
|
|
{
|
|
if(MDFN_UNLIKELY(CheatsChanged))
|
|
{
|
|
MDFNI_DelCheats();
|
|
//
|
|
MemoryPatch mp;
|
|
|
|
for(size_t i = 0; i < Cheats.size(); i++)
|
|
{
|
|
for(auto const& cfie : cgi->CheatInfo.CheatFormatInfo)
|
|
{
|
|
if(!cfie.DecodeCheat(Cheats[i], &mp))
|
|
{
|
|
MDFNI_AddCheat(mp);
|
|
//
|
|
mp = MemoryPatch();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
CheatsChanged = false;
|
|
}
|
|
//
|
|
//
|
|
//
|
|
DoFrame(surf.get());
|
|
|
|
bool updated = false;
|
|
if (cb.environment(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
|
|
{
|
|
check_variables(true);
|
|
}
|
|
}
|
|
|
|
RETRO_API size_t retro_serialize_size(void)
|
|
{
|
|
return SaveStateSize;
|
|
}
|
|
|
|
RETRO_API bool retro_serialize(void* data, size_t size)
|
|
{
|
|
ExtMemStream ssms(data, size);
|
|
MDFNSS_SaveSM(&ssms, true);
|
|
return true;
|
|
}
|
|
|
|
RETRO_API bool retro_unserialize(const void* data, size_t size)
|
|
{
|
|
ExtMemStream ssms(data, size);
|
|
MDFNSS_LoadSM(&ssms, true);
|
|
return true;
|
|
}
|
|
|
|
MDFN_COLD RETRO_API bool retro_load_game(const retro_game_info* game)
|
|
{
|
|
assert(game);
|
|
assert(game->data);
|
|
//
|
|
MDFN_PixelFormat nf = MDFN_PixelFormat::ARGB32_8888;
|
|
|
|
ports_active = 2;
|
|
//
|
|
options_defaults.clear();
|
|
for(const struct retro_variable* opt = options; opt->key; opt++)
|
|
{
|
|
std::string v;
|
|
|
|
for(size_t i = 0; opt->value[i]; i++)
|
|
{
|
|
if(opt->value[i] == ';')
|
|
{
|
|
do { i++; } while(MDFN_isspace(opt->value[i]));
|
|
//
|
|
size_t j;
|
|
for(j = i; opt->value[j] && opt->value[j] != '|'; j++)
|
|
{
|
|
//
|
|
}
|
|
options_defaults[opt->key] = std::string(opt->value + i, j - i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
pipe_option("supafaust_correct_aspect", "snes_faust.correct_aspect");
|
|
pipe_option("supafaust_h_filter", "snes_faust.h_filter");
|
|
pipe_option("supafaust_deinterlacer", "video.deinterlacer");
|
|
pipe_option("supafaust_slstart", "snes_faust.slstart");
|
|
pipe_option("supafaust_slend", "snes_faust.slend");
|
|
pipe_option("supafaust_slstartp", "snes_faust.slstartp");
|
|
pipe_option("supafaust_slendp", "snes_faust.slendp");
|
|
pipe_option("supafaust_region", "snes_faust.region");
|
|
pipe_option("supafaust_thread_affinity_ppu", "snes_faust.affinity.ppu");
|
|
pipe_option("supafaust_renderer", "snes_faust.renderer");
|
|
pipe_option_bool("supafaust_frame_begin_vblank", "snes_faust.frame_begin_vblank");
|
|
//
|
|
//
|
|
{
|
|
const char* const v = get_option("supafaust_pixel_format");
|
|
retro_pixel_format rpf;
|
|
|
|
if(!MDFN_strazicmp(v, "rgb565"))
|
|
rpf = RETRO_PIXEL_FORMAT_RGB565;
|
|
else if(!MDFN_strazicmp(v, "xrgb8888"))
|
|
rpf = RETRO_PIXEL_FORMAT_XRGB8888;
|
|
else
|
|
rpf = RETRO_PIXEL_FORMAT_0RGB1555;
|
|
|
|
nf = RPFtoMPF(rpf);
|
|
|
|
cb.environment(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &rpf);
|
|
}
|
|
//
|
|
//
|
|
{
|
|
static const struct
|
|
{
|
|
const char* lrv;
|
|
const char* spex;
|
|
const char* spex_sound;
|
|
} ttab[] =
|
|
{
|
|
{ "disabled", "0", "0" },
|
|
{ "video", "1", "0" },
|
|
{ "video+audio", "1", "1" },
|
|
{ NULL }
|
|
};
|
|
|
|
const char* const v = get_option("supafaust_run_ahead");
|
|
|
|
for(auto const& tte : ttab)
|
|
{
|
|
if(!tte.lrv)
|
|
{
|
|
fprintf(stderr, "Bad value for option supafaust_run_ahead.");
|
|
abort();
|
|
}
|
|
|
|
if(!MDFN_strazicmp(v, tte.lrv))
|
|
{
|
|
MDFNI_SetSetting("snes_faust.spex", tte.spex);
|
|
MDFNI_SetSetting("snes_faust.spex.sound", tte.spex_sound);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
//
|
|
{
|
|
struct retro_input_descriptor input_descriptors[] = {
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Y" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
|
|
{ 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_LEFT, "D-Pad Left" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
|
|
{ 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_L, "L" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" },
|
|
|
|
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
|
|
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Y" },
|
|
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
|
|
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
|
|
{ 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_LEFT, "D-Pad Left" },
|
|
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
|
|
{ 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_L, "L" },
|
|
{ 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" },
|
|
|
|
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
|
|
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Y" },
|
|
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
|
|
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
|
|
{ 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_LEFT, "D-Pad Left" },
|
|
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
|
|
{ 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_L, "L" },
|
|
{ 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" },
|
|
|
|
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
|
|
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Y" },
|
|
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
|
|
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
|
|
{ 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_LEFT, "D-Pad Left" },
|
|
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
|
|
{ 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_L, "L" },
|
|
{ 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" },
|
|
|
|
{0},
|
|
};
|
|
|
|
cb.environment(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, input_descriptors);
|
|
}
|
|
//
|
|
//
|
|
{
|
|
static const struct
|
|
{
|
|
const char* lrv;
|
|
const char* sport1;
|
|
const char* sport2;
|
|
size_t ports_active;
|
|
} ttab[] =
|
|
{
|
|
{ "disabled", "0", "0", 2 },
|
|
{ "port1", "1", "0", 5 },
|
|
{ "port2", "0", "1", 5 },
|
|
{ "port1+port2", "1", "1", 8 },
|
|
|
|
{ NULL }
|
|
};
|
|
|
|
const char* const v = get_option("supafaust_multitap");
|
|
|
|
for(auto const& tte : ttab)
|
|
{
|
|
if(!tte.lrv)
|
|
{
|
|
fprintf(stderr, "Bad value for option supafaust_multitap.");
|
|
abort();
|
|
}
|
|
|
|
if(!MDFN_strazicmp(v, tte.lrv))
|
|
{
|
|
MDFNI_SetSetting("snes_faust.input.sport1.multitap", tte.sport1);
|
|
MDFNI_SetSetting("snes_faust.input.sport2.multitap", tte.sport2);
|
|
ports_active = tte.ports_active;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pipe_option("supafaust_cx4_clock_rate", "snes_faust.cx4.clock_rate");
|
|
pipe_option("supafaust_superfx_clock_rate", "snes_faust.superfx.clock_rate");
|
|
pipe_option_bool("supafaust_superfx_icache", "snes_faust.superfx.icache");
|
|
|
|
MDFNI_SetSetting("snes_faust.renderer", "mt");
|
|
//
|
|
//
|
|
//
|
|
std::string dir, fbase, ext = ".sfc";
|
|
|
|
if (game->path)
|
|
NVFS.get_file_path_components(game->path, &dir, &fbase, &ext);
|
|
|
|
ExtMemStream gs(game->data, game->size);
|
|
GameFile gf({&NVFS, dir, &gs, MDFN_strazlower(ext.size() ? ext.substr(1) : ext), fbase, { &NVFS, dir, fbase }});
|
|
cgi = MDFNI_LoadGame(&gf);
|
|
//
|
|
//
|
|
for(size_t i = 0; i < ports_active; i++)
|
|
{
|
|
port_data[i] = MDFNI_SetInput(i/*port*/, 1/*device type id, gamepad*/);
|
|
}
|
|
|
|
SoundRate = get_option_signed("supafaust_audio_rate");
|
|
if(!SoundRate)
|
|
SoundRate = cgi->IdealSoundRate;
|
|
assert(SoundRate);
|
|
SoundBufMaxSize = ceil((SoundRate + 9) / 10);
|
|
SoundBuf.reset(new int16[SoundBufMaxSize * cgi->soundchan]);
|
|
lw.reset(new int32[cgi->fb_height]);
|
|
memset(lw.get(), 0, sizeof(int32) * cgi->fb_height);
|
|
surf.reset(new MDFN_Surface(nullptr, cgi->fb_width, cgi->fb_height, cgi->fb_width, nf));
|
|
//
|
|
{
|
|
MemoryStream ssms(65536);
|
|
MDFNSS_SaveSM(&ssms, true);
|
|
|
|
SaveStateSize = ssms.size(); // FIXME for other than snes_faust, may grow.
|
|
SaveStateSize += 64*2*4*2; // OwlBuffer-related kludge, leftover, 2 channels(l/r), 4 bytes per sample, *2 again for MSU1
|
|
}
|
|
//
|
|
{
|
|
const uint64 affinity = get_option_unsigned("supafaust_thread_affinity_emu");
|
|
|
|
if(affinity != 0)
|
|
{
|
|
affinity_save = MThreading::Thread_SetAffinity(nullptr, affinity);
|
|
}
|
|
}
|
|
//
|
|
options_defaults.clear();
|
|
|
|
return true;
|
|
}
|
|
|
|
MDFN_COLD RETRO_API bool retro_load_game_special(unsigned game_type, const retro_game_info* info, size_t num_info)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
MDFN_COLD RETRO_API void retro_unload_game(void)
|
|
{
|
|
Cleanup();
|
|
}
|
|
|
|
RETRO_API unsigned retro_get_region(void)
|
|
{
|
|
assert(cgi);
|
|
//
|
|
unsigned ret;
|
|
|
|
switch(cgi->VideoSystem)
|
|
{
|
|
default:
|
|
case VIDSYS_NONE:
|
|
case VIDSYS_NTSC:
|
|
case VIDSYS_PAL_M:
|
|
ret = RETRO_REGION_NTSC;
|
|
break;
|
|
|
|
case VIDSYS_PAL:
|
|
case VIDSYS_SECAM:
|
|
ret = RETRO_REGION_PAL;
|
|
break;
|
|
|
|
/*
|
|
case VIDSYS_PAL_M:
|
|
case VIDSYS_SECAM:
|
|
ret = ~0U;
|
|
break;
|
|
*/
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
namespace MDFN_IEN_SNES_FAUST
|
|
{
|
|
MDFN_COLD uint8* GetNV(uint32* size);
|
|
}
|
|
|
|
RETRO_API void* retro_get_memory_data(unsigned id)
|
|
{
|
|
assert(cgi);
|
|
if(/*!cgi->GetNV ||*/ id != RETRO_MEMORY_SAVE_RAM)
|
|
return nullptr;
|
|
//
|
|
uint32 size;
|
|
|
|
return MDFN_IEN_SNES_FAUST::GetNV(&size); //cgi->GetNV(&size);
|
|
}
|
|
|
|
RETRO_API size_t retro_get_memory_size(unsigned id)
|
|
{
|
|
assert(cgi);
|
|
if(/*!cgi->GetNV ||*/ id != RETRO_MEMORY_SAVE_RAM)
|
|
return 0;
|
|
//
|
|
uint32 size = 0;
|
|
|
|
MDFN_IEN_SNES_FAUST::GetNV(&size); //cgi->GetNV(&size);
|
|
|
|
return size;
|
|
}
|
|
|
|
MDFN_COLD RETRO_API void retro_cheat_reset(void)
|
|
{
|
|
Cheats.clear();
|
|
CheatsChanged = true;
|
|
}
|
|
|
|
MDFN_COLD RETRO_API void retro_cheat_set(unsigned index, bool enabled, const char* code)
|
|
{
|
|
if(index >= Cheats.size())
|
|
Cheats.resize(index + 1);
|
|
//
|
|
if(!enabled)
|
|
Cheats[index] = "";
|
|
else
|
|
Cheats[index] = code;
|
|
//
|
|
CheatsChanged = true;
|
|
}
|