mirror of
https://github.com/libretro/beetle-wswan-libretro.git
synced 2024-11-23 07:59:40 +00:00
1319 lines
35 KiB
C
1319 lines
35 KiB
C
#include <sys/types.h>
|
|
|
|
#include <libretro.h>
|
|
#include <retro_math.h>
|
|
|
|
#include "libretro_core_options.h"
|
|
|
|
#include "mednafen/settings.h"
|
|
#include "mednafen/git.h"
|
|
#include "mednafen/wswan/wswan.h"
|
|
#include "mednafen/mempatcher.h"
|
|
#include "mednafen/wswan/gfx.h"
|
|
#include "mednafen/wswan/interrupt.h"
|
|
#include "mednafen/wswan/wswan-memory.h"
|
|
#include "mednafen/wswan/start.inc"
|
|
#include "mednafen/wswan/sound.h"
|
|
#include "mednafen/wswan/v30mz.h"
|
|
#include "mednafen/wswan/rtc.h"
|
|
#include "mednafen/wswan/eeprom.h"
|
|
|
|
#define MEDNAFEN_CORE_NAME_MODULE "wswan"
|
|
#define MEDNAFEN_CORE_NAME "Beetle WonderSwan"
|
|
#define MEDNAFEN_CORE_VERSION "v0.9.35.1"
|
|
#define MEDNAFEN_CORE_EXTENSIONS "ws|wsc|pc2"
|
|
#define MEDNAFEN_CORE_TIMING_FPS 75.47
|
|
#define MEDNAFEN_CORE_GEOMETRY_BASE_W (EmulatedWSwan.nominal_width)
|
|
#define MEDNAFEN_CORE_GEOMETRY_BASE_H (EmulatedWSwan.nominal_height)
|
|
#define MEDNAFEN_CORE_GEOMETRY_MAX_W 224
|
|
#define MEDNAFEN_CORE_GEOMETRY_MAX_H 144
|
|
#define MEDNAFEN_CORE_GEOMETRY_ASPECT_RATIO (14.0 / 9.0)
|
|
#define FB_WIDTH 224
|
|
#define FB_HEIGHT 144
|
|
#define FB_MAX_HEIGHT FB_HEIGHT
|
|
|
|
/* Forward declarations */
|
|
void MDFN_LoadGameCheats(void *override_ptr);
|
|
void MDFN_FlushGameCheats(int nosave);
|
|
|
|
/* core options */
|
|
static int RETRO_SAMPLE_RATE = 44100;
|
|
|
|
static int RETRO_PIX_BYTES = 2;
|
|
static int RETRO_PIX_DEPTH = 15;
|
|
|
|
struct retro_perf_callback perf_cb;
|
|
retro_get_cpu_features_t perf_get_cpu_features_cb = NULL;
|
|
retro_log_printf_t log_cb;
|
|
static retro_video_refresh_t video_cb;
|
|
static retro_audio_sample_t audio_cb;
|
|
static retro_audio_sample_batch_t audio_batch_cb;
|
|
static retro_environment_t environ_cb;
|
|
static retro_input_poll_t input_poll_cb;
|
|
static retro_input_state_t input_state_cb;
|
|
|
|
static bool libretro_supports_bitmasks = false;
|
|
|
|
static bool overscan;
|
|
static double last_sound_rate;
|
|
|
|
static bool rotate_tall = false;
|
|
static bool rotate_tall_override = false;
|
|
static bool select_pressed_last_frame = false;
|
|
static bool hw_rotate_enabled = false;
|
|
|
|
static unsigned rotate_joymap = 0;
|
|
|
|
static MDFN_Surface *surf = NULL;
|
|
static uint16_t *rotate_buf = NULL;
|
|
|
|
#define ROTATE_PIXEL_BUF(typename_t, src, width, height, dst) \
|
|
{ \
|
|
typename_t *in_ptr = (typename_t*)src; \
|
|
typename_t *out_ptr = (typename_t*)dst; \
|
|
size_t x, y; \
|
|
for (x = 0; x < width; x++) \
|
|
for (y = 0; y < height; y++) \
|
|
*(out_ptr + y + (((width - 1) - x) * height)) = *(in_ptr + x + (y * width)); \
|
|
}
|
|
|
|
/* Mono palettes */
|
|
|
|
struct ws_mono_palette
|
|
{
|
|
const char *name;
|
|
uint32 start;
|
|
uint32 end;
|
|
};
|
|
|
|
struct ws_mono_palette ws_mono_palettes[] = {
|
|
{ "default", 0x000000, 0xFFFFFF },
|
|
{ "wonderswan", 0x3E3D20, 0x9B9D66 },
|
|
{ "wondeswan_color", 0x1B201E, 0xD7D49D },
|
|
{ "swancrystal", 0x151108, 0xFFFCCA },
|
|
{ "gb_dmg", 0x00420C, 0x578200 },
|
|
{ "gb_pocket", 0x2A3325, 0xA7B19A },
|
|
{ "gb_light", 0x00778D, 0x01CBDF },
|
|
{ "blossom_pink", 0x180F0F, 0xF09898 },
|
|
{ "bubbles_blue", 0x0D1418, 0x88D0F0 },
|
|
{ "buttercup_green", 0x12160D, 0xB8E088 },
|
|
{ "digivice", 0x000000, 0x8C8C73 },
|
|
{ "game_com", 0x000000, 0xA7BF6B },
|
|
{ "gameking", 0x184221, 0x8CCE94 },
|
|
{ "game_master", 0x2D2D2B, 0x829FA6 },
|
|
{ "golden_wild", 0x120F0A, 0xB99F65 },
|
|
{ "greenscale", 0x0C360C, 0x9CBE0C },
|
|
{ "hokage_orange", 0x170D08, 0xEA8352 },
|
|
{ "labo_fawn", 0x15110B, 0xD7AA73 },
|
|
{ "legendary_super_saiyan", 0x101509, 0xA5DB5A },
|
|
{ "microvision", 0x303030, 0xA0A0A0 },
|
|
{ "million_live_gold", 0x141109, 0xCDB261 },
|
|
{ "odyssey_gold", 0x131000, 0xC2A000 },
|
|
{ "shiny_sky_blue", 0x0E1216, 0x8CB6DF },
|
|
{ "slime_blue", 0x040E14, 0x2F8CCC },
|
|
{ "ti_83", 0x181810, 0x9CA684 },
|
|
{ "travel_wood", 0x482810, 0xF8D8B0 },
|
|
{ "virtual_boy", 0x000000, 0xE30000 },
|
|
{ NULL, 0, 0 },
|
|
};
|
|
|
|
static uint32 mono_pal_start = 0x000000;
|
|
static uint32 mono_pal_end = 0xFFFFFF;
|
|
|
|
static bool find_mono_palette(const char* name,
|
|
uint32 *start, uint32 *end)
|
|
{
|
|
bool palette_found = false;
|
|
unsigned i;
|
|
|
|
for (i = 0; ws_mono_palettes[i].name; i++)
|
|
{
|
|
if (!strcmp(ws_mono_palettes[i].name, name))
|
|
{
|
|
*start = ws_mono_palettes[i].start;
|
|
*end = ws_mono_palettes[i].end;
|
|
palette_found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!palette_found)
|
|
{
|
|
*start = 0x000000;
|
|
*end = 0xFFFFFF;
|
|
}
|
|
|
|
return palette_found;
|
|
}
|
|
|
|
/* Frameskipping */
|
|
|
|
static unsigned frameskip_type = 0;
|
|
static unsigned frameskip_threshold = 0;
|
|
static uint16_t frameskip_counter = 0;
|
|
|
|
static bool retro_audio_buff_active = false;
|
|
static unsigned retro_audio_buff_occupancy = 0;
|
|
static bool retro_audio_buff_underrun = false;
|
|
/* Maximum number of consecutive frames that
|
|
* can be skipped */
|
|
#define FRAMESKIP_MAX 30
|
|
|
|
static unsigned audio_latency = 0;
|
|
static bool update_audio_latency = false;
|
|
|
|
static void retro_audio_buff_status_cb(
|
|
bool active, unsigned occupancy, bool underrun_likely)
|
|
{
|
|
retro_audio_buff_active = active;
|
|
retro_audio_buff_occupancy = occupancy;
|
|
retro_audio_buff_underrun = underrun_likely;
|
|
}
|
|
|
|
static void init_frameskip(void)
|
|
{
|
|
if (frameskip_type > 0)
|
|
{
|
|
struct retro_audio_buffer_status_callback buf_status_cb;
|
|
|
|
buf_status_cb.callback = retro_audio_buff_status_cb;
|
|
if (!environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK,
|
|
&buf_status_cb))
|
|
{
|
|
if (log_cb)
|
|
log_cb(RETRO_LOG_WARN, "Frameskip disabled - frontend does not support audio buffer status monitoring.\n");
|
|
|
|
retro_audio_buff_active = false;
|
|
retro_audio_buff_occupancy = 0;
|
|
retro_audio_buff_underrun = false;
|
|
audio_latency = 0;
|
|
}
|
|
else
|
|
{
|
|
/* Frameskip is enabled - increase frontend
|
|
* audio latency to minimise potential
|
|
* buffer underruns */
|
|
float frame_time_msec = 1000.0f / ((float)MEDNAFEN_CORE_TIMING_FPS);
|
|
|
|
/* Set latency to 8x current frame time...
|
|
* (for 60Hz cores we normally use a 6x
|
|
* multiplier - but the WonderSwan runs
|
|
* at an unusually high frame rate, which
|
|
* seems to place greater demands on the
|
|
* frontend. Increasing the multiplier to
|
|
* 8x gives the frontend more room to
|
|
* manoeuvre, and improves the efficacy of
|
|
* the 'Auto' frameskip setting) */
|
|
audio_latency = (unsigned)((8.0f * frame_time_msec) + 0.5f);
|
|
|
|
/* ...then round up to nearest multiple of 32 */
|
|
audio_latency = (audio_latency + 0x1F) & ~0x1F;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, NULL);
|
|
audio_latency = 0;
|
|
}
|
|
|
|
update_audio_latency = true;
|
|
}
|
|
|
|
/* Cygne
|
|
*
|
|
* Copyright notice for this file:
|
|
* Copyright (C) 2002 Dox dox@space.pl
|
|
*
|
|
* This program 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.
|
|
*
|
|
* This program 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 this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
int wsc = 1; /*color/mono*/
|
|
uint32 rom_size;
|
|
|
|
uint16 WSButtonStatus;
|
|
|
|
static uint8 WSRCurrentSong;
|
|
|
|
MDFNGI EmulatedWSwan =
|
|
{
|
|
MDFN_MASTERCLOCK_FIXED(3072000),
|
|
0,
|
|
224, /* lcm_width */
|
|
144, /* lcm_height */
|
|
|
|
224, /* Nominal width */
|
|
144, /* Nominal height */
|
|
|
|
224, /* Framebuffer width */
|
|
144, /* Framebuffer height */
|
|
|
|
2, /* Number of output sound channels */
|
|
};
|
|
|
|
|
|
static void Reset(void)
|
|
{
|
|
int u0;
|
|
|
|
v30mz_reset(); /* Reset CPU */
|
|
WSwan_MemoryReset();
|
|
WSwan_GfxReset();
|
|
WSwan_SoundReset();
|
|
WSwan_InterruptReset();
|
|
WSwan_RTCReset();
|
|
WSwan_EEPROMReset();
|
|
|
|
for(u0=0;u0<0xc9;u0++)
|
|
{
|
|
if(u0 != 0xC4 && u0 != 0xC5 && u0 != 0xBA && u0 != 0xBB)
|
|
WSwan_writeport(u0,startio[u0]);
|
|
}
|
|
|
|
v30mz_set_reg(NEC_SS,0);
|
|
v30mz_set_reg(NEC_SP,0x2000);
|
|
}
|
|
|
|
static uint8 *chee;
|
|
|
|
static void Emulate(EmulateSpecStruct *espec, int16_t *sndbuf)
|
|
{
|
|
uint16 butt_data;
|
|
|
|
espec->DisplayRect.x = 0;
|
|
espec->DisplayRect.y = 0;
|
|
espec->DisplayRect.w = 224;
|
|
espec->DisplayRect.h = 144;
|
|
|
|
if(espec->VideoFormatChanged)
|
|
WSwan_SetPixelFormat(espec->surface->depth,
|
|
mono_pal_start, mono_pal_end);
|
|
|
|
if(espec->SoundFormatChanged)
|
|
WSwan_SetSoundRate(RETRO_SAMPLE_RATE);
|
|
|
|
butt_data = chee[0] | (chee[1] << 8);
|
|
|
|
WSButtonStatus = butt_data;
|
|
|
|
MDFNMP_ApplyPeriodicCheats();
|
|
|
|
while(!wsExecuteLine(espec->surface, espec->skip));
|
|
|
|
espec->SoundBufSize = WSwan_SoundFlush(sndbuf, espec->SoundBufMaxSize);
|
|
|
|
espec->MasterCycles = v30mz_timestamp;
|
|
v30mz_timestamp = 0;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
const uint8 id;
|
|
const char *name;
|
|
} DLEntry;
|
|
|
|
static const DLEntry Developers[] =
|
|
{
|
|
{ 0x01, "Bandai" },
|
|
{ 0x02, "Taito" },
|
|
{ 0x03, "Tomy" },
|
|
{ 0x04, "Koei" },
|
|
{ 0x05, "Data East" },
|
|
{ 0x06, "Asmik" }, /* Asmik Ace? */
|
|
{ 0x07, "Media Entertainment" },
|
|
{ 0x08, "Nichibutsu" },
|
|
{ 0x0A, "Coconuts Japan" },
|
|
{ 0x0B, "Sammy" },
|
|
{ 0x0C, "Sunsoft" },
|
|
{ 0x0D, "Mebius" },
|
|
{ 0x0E, "Banpresto" },
|
|
{ 0x10, "Jaleco" },
|
|
{ 0x11, "Imagineer" },
|
|
{ 0x12, "Konami" },
|
|
{ 0x16, "Kobunsha" },
|
|
{ 0x17, "Bottom Up" },
|
|
{ 0x18, "Kaga Tech" },
|
|
{ 0x19, "Sunrise" },
|
|
{ 0x1A, "Cyber Front" },
|
|
{ 0x1B, "Mega House" },
|
|
{ 0x1D, "Interbec" },
|
|
{ 0x1E, "Nihon Application" },
|
|
{ 0x1F, "Bandai Visual" },
|
|
{ 0x20, "Athena" },
|
|
{ 0x21, "KID" },
|
|
{ 0x22, "HAL Corporation" },
|
|
{ 0x23, "Yuki Enterprise" },
|
|
{ 0x24, "Omega Micott" },
|
|
{ 0x25, "Layup" },
|
|
{ 0x26, "Kadokawa Shoten" },
|
|
{ 0x27, "Shall Luck" },
|
|
{ 0x28, "Squaresoft" },
|
|
{ 0x2B, "Tom Create" },
|
|
{ 0x2D, "Namco" },
|
|
{ 0x2E, "Movic" }, /* ???? */
|
|
{ 0x2F, "E3 Staff" }, /* ???? */
|
|
{ 0x31, "Vanguard" },
|
|
{ 0x32, "Megatron" },
|
|
{ 0x33, "Wiz" },
|
|
{ 0x36, "Capcom" }
|
|
};
|
|
|
|
static uint32 SRAMSize;
|
|
|
|
static int Load(const uint8_t *data, size_t size)
|
|
{
|
|
uint32 pow_size = 0;
|
|
uint32 real_rom_size = 0;
|
|
uint8 header[10];
|
|
|
|
if(size < 65536)
|
|
return(0);
|
|
|
|
real_rom_size = (size + 0xFFFF) & ~0xFFFF;
|
|
pow_size = next_pow2(real_rom_size);
|
|
rom_size = pow_size + (pow_size == 0);
|
|
|
|
wsCartROM = (uint8 *)calloc(1, rom_size);
|
|
|
|
/* This real_rom_size vs rom_size funny business
|
|
* is intended primarily for handling
|
|
* WSR files. */
|
|
if(real_rom_size < rom_size)
|
|
memset(wsCartROM, 0xFF, rom_size - real_rom_size);
|
|
|
|
memcpy(wsCartROM + (rom_size - real_rom_size), data, size);
|
|
|
|
memcpy(header, wsCartROM + rom_size - 10, 10);
|
|
|
|
SRAMSize = 0;
|
|
eeprom_size = 0;
|
|
|
|
switch(header[5])
|
|
{
|
|
case 0x01: SRAMSize = 8 * 1024; break;
|
|
case 0x02: SRAMSize = 32 * 1024; break;
|
|
case 0x03: SRAMSize = 128 * 1024; break;
|
|
case 0x04: SRAMSize = 256 * 1024; break; /* Dicing Knight!, Judgement Silver */
|
|
case 0x05: SRAMSize = 512 * 1024; break; /* Wonder Gate */
|
|
|
|
case 0x10: eeprom_size = 128; break;
|
|
case 0x20: eeprom_size = 2 *1024; break;
|
|
case 0x50: eeprom_size = 1024; break;
|
|
}
|
|
|
|
if((header[8] | (header[9] << 8)) == 0x8de1 && (header[0]==0x01)&&(header[2]==0x27)) /* Detective Conan */
|
|
{
|
|
/* WS cpu is using cache/pipeline or there's protected ROM bank where pointing CS */
|
|
wsCartROM[0xfffe8]=0xea;
|
|
wsCartROM[0xfffe9]=0x00;
|
|
wsCartROM[0xfffea]=0x00;
|
|
wsCartROM[0xfffeb]=0x00;
|
|
wsCartROM[0xfffec]=0x20;
|
|
}
|
|
|
|
if(header[6] & 0x1)
|
|
EmulatedWSwan.rotated = MDFN_ROTATE90;
|
|
|
|
MDFNMP_Init(16384, (1 << 20) / 1024);
|
|
|
|
v30mz_init(WSwan_readmem20, WSwan_writemem20, WSwan_readport, WSwan_writeport);
|
|
WSwan_MemoryInit(MDFN_GetSettingB("wswan.language"), wsc, SRAMSize, false); /* EEPROM and SRAM are loaded in this func. */
|
|
WSwan_GfxInit();
|
|
EmulatedWSwan.fps = (uint32)((uint64)3072000 * 65536 * 256 / (159*256));
|
|
|
|
WSwan_SoundInit();
|
|
|
|
wsMakeTiles();
|
|
|
|
Reset();
|
|
|
|
return(1);
|
|
}
|
|
|
|
static void CloseGame(void)
|
|
{
|
|
WSwan_MemoryKill();
|
|
|
|
WSwan_SoundKill();
|
|
|
|
if(wsCartROM)
|
|
{
|
|
free(wsCartROM);
|
|
wsCartROM = NULL;
|
|
}
|
|
}
|
|
|
|
static void SetInput(int port, const char *type, void *ptr)
|
|
{
|
|
if(!port) chee = (uint8 *)ptr;
|
|
}
|
|
|
|
int StateAction(StateMem *sm, int load, int data_only)
|
|
{
|
|
if(!v30mz_StateAction(sm, load, data_only))
|
|
return(0);
|
|
|
|
/* Call MemoryStateAction before others StateActions... */
|
|
if(!WSwan_MemoryStateAction(sm, load, data_only))
|
|
return(0);
|
|
|
|
if(!WSwan_GfxStateAction(sm, load, data_only))
|
|
return(0);
|
|
|
|
if(!WSwan_RTCStateAction(sm, load, data_only))
|
|
return(0);
|
|
|
|
if(!WSwan_InterruptStateAction(sm, load, data_only))
|
|
return(0);
|
|
|
|
if(!WSwan_SoundStateAction(sm, load, data_only))
|
|
return(0);
|
|
|
|
if(!WSwan_EEPROMStateAction(sm, load, data_only))
|
|
return(0);
|
|
|
|
return(1);
|
|
}
|
|
|
|
static void DoSimpleCommand(int cmd)
|
|
{
|
|
switch(cmd)
|
|
{
|
|
case MDFN_MSC_POWER:
|
|
case MDFN_MSC_RESET:
|
|
Reset();
|
|
break;
|
|
}
|
|
}
|
|
|
|
static const InputDeviceInputInfoStruct IDII[] =
|
|
{
|
|
{ "up-x", "UP ↑, X Cursors", 0, IDIT_BUTTON, "down-x", { "right-x", "down-x", "left-x" } },
|
|
{ "right-x", "RIGHT →, X Cursors", 3, IDIT_BUTTON, "left-x", { "down-x", "left-x", "up-x" } },
|
|
{ "down-x", "DOWN ↓, X Cursors", 1, IDIT_BUTTON, "up-x", { "left-x", "up-x", "right-x" } },
|
|
{ "left-x", "LEFT ←, X Cursors", 2, IDIT_BUTTON, "right-x", { "up-x", "right-x", "down-x" } },
|
|
|
|
{ "up-y", "UP ↑, Y Cur: MUST NOT = X CURSORS", 4, IDIT_BUTTON, "down-y", { "right-y", "down-y", "left-y" } },
|
|
{ "right-y", "RIGHT →, Y Cur: MUST NOT = X CURSORS", 7, IDIT_BUTTON, "left-y", { "down-y", "left-y", "up-y" } },
|
|
{ "down-y", "DOWN ↓, Y Cur: MUST NOT = X CURSORS", 5, IDIT_BUTTON, "up-y", { "left-y", "up-y", "right-y" } },
|
|
{ "left-y", "LEFT ←, Y Cur: MUST NOT = X CURSORS", 6, IDIT_BUTTON, "right-y", { "up-y", "right-y", "down-y" } },
|
|
|
|
{ "start", "Start", 8, IDIT_BUTTON, NULL },
|
|
{ "a", "A", 10, IDIT_BUTTON_CAN_RAPID, NULL },
|
|
{ "b", "B", 9, IDIT_BUTTON_CAN_RAPID, NULL },
|
|
};
|
|
|
|
static InputDeviceInfoStruct InputDeviceInfo[] =
|
|
{
|
|
{
|
|
"gamepad",
|
|
"Gamepad",
|
|
NULL,
|
|
NULL,
|
|
sizeof(IDII) / sizeof(InputDeviceInputInfoStruct),
|
|
IDII,
|
|
}
|
|
};
|
|
|
|
static const InputPortInfoStruct PortInfo[] =
|
|
{
|
|
{ "builtin", "Built-In", sizeof(InputDeviceInfo) / sizeof(InputDeviceInfoStruct), InputDeviceInfo, "gamepad" }
|
|
};
|
|
|
|
static InputInfoStruct InputInfo =
|
|
{
|
|
sizeof(PortInfo) / sizeof(InputPortInfoStruct),
|
|
PortInfo
|
|
};
|
|
|
|
static bool update_video, update_audio;
|
|
|
|
static bool MDFNI_LoadGame(
|
|
const char *force_module, const uint8_t *data,
|
|
size_t size)
|
|
{
|
|
if(Load(data, size) <= 0)
|
|
return false;
|
|
|
|
MDFN_LoadGameCheats(NULL);
|
|
MDFNMP_InstallReadPatches();
|
|
|
|
return true;
|
|
}
|
|
|
|
static void MDFNI_CloseGame(void)
|
|
{
|
|
MDFN_FlushGameCheats(0);
|
|
|
|
CloseGame();
|
|
|
|
MDFNMP_Kill();
|
|
}
|
|
|
|
static void check_system_specs(void)
|
|
{
|
|
/* TODO - should theoretically be level 4, but apparently
|
|
* doesn't run at fullspeed on PSP (yet) */
|
|
unsigned level = 4;
|
|
environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level);
|
|
}
|
|
|
|
static void check_depth(void)
|
|
{
|
|
if (RETRO_PIX_DEPTH == 24)
|
|
{
|
|
enum retro_pixel_format rgb888 = RETRO_PIXEL_FORMAT_XRGB8888;
|
|
|
|
if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &rgb888))
|
|
{
|
|
if (log_cb) log_cb(RETRO_LOG_ERROR, "Pixel format XRGB8888 not supported by platform.\n");
|
|
|
|
RETRO_PIX_BYTES = 2;
|
|
RETRO_PIX_DEPTH = 15;
|
|
}
|
|
}
|
|
|
|
#if defined(FRONTEND_SUPPORTS_RGB565)
|
|
if (RETRO_PIX_BYTES == 2)
|
|
{
|
|
enum retro_pixel_format rgb565 = RETRO_PIXEL_FORMAT_RGB565;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &rgb565))
|
|
{
|
|
if (log_cb) log_cb(RETRO_LOG_INFO, "Frontend supports RGB565 - will use that instead of XRGB1555.\n");
|
|
|
|
RETRO_PIX_DEPTH = 16;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void rotate_display(void)
|
|
{
|
|
if (hw_rotate_enabled)
|
|
{
|
|
struct retro_game_geometry new_geom = {
|
|
FB_WIDTH,
|
|
FB_HEIGHT,
|
|
FB_WIDTH,
|
|
FB_HEIGHT,
|
|
rotate_tall ? (9.0 / 14.0) : (14.0 / 9.0)
|
|
};
|
|
unsigned rot_angle = rotate_tall ?
|
|
1 : /* 90 degrees */
|
|
0; /* 0 degrees */
|
|
|
|
environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, (void*)&new_geom);
|
|
environ_cb(RETRO_ENVIRONMENT_SET_ROTATION, (void*)&rot_angle);
|
|
}
|
|
else
|
|
{
|
|
struct retro_game_geometry new_geom = {
|
|
rotate_tall ? FB_HEIGHT : FB_WIDTH,
|
|
rotate_tall ? FB_WIDTH : FB_HEIGHT,
|
|
FB_WIDTH,
|
|
FB_WIDTH,
|
|
rotate_tall ? (9.0 / 14.0) : (14.0 / 9.0)
|
|
};
|
|
|
|
environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, (void*)&new_geom);
|
|
}
|
|
}
|
|
|
|
static void check_variables(int startup)
|
|
{
|
|
struct retro_variable var = {0};
|
|
uint32 prev_mono_pal_start;
|
|
uint32 prev_mono_pal_end;
|
|
unsigned prev_frameskip_type;
|
|
|
|
var.key = "wswan_rotate_display",
|
|
var.value = NULL;
|
|
|
|
rotate_tall_override = false;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
if (!strcmp(var.value, "enabled"))
|
|
rotate_tall_override = true;
|
|
|
|
if (rotate_tall_override && !rotate_tall)
|
|
{
|
|
rotate_tall = true;
|
|
rotate_display();
|
|
}
|
|
|
|
var.key = "wswan_rotate_keymap",
|
|
var.value = NULL;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
rotate_joymap = 0;
|
|
else if (!strcmp(var.value, "enabled"))
|
|
rotate_joymap = 1;
|
|
else if (!strcmp(var.value, "auto"))
|
|
rotate_joymap = 2;
|
|
}
|
|
|
|
var.key = "wswan_mono_palette",
|
|
var.value = NULL;
|
|
|
|
prev_mono_pal_start = mono_pal_start;
|
|
prev_mono_pal_end = mono_pal_end;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
find_mono_palette(var.value, &mono_pal_start, &mono_pal_end);
|
|
|
|
if ((mono_pal_start != prev_mono_pal_start) ||
|
|
(mono_pal_end != prev_mono_pal_end))
|
|
WSwan_SetMonoPalette(RETRO_PIX_DEPTH, mono_pal_start, mono_pal_end);
|
|
|
|
var.key = "wswan_frameskip";
|
|
var.value = NULL;
|
|
|
|
prev_frameskip_type = frameskip_type;
|
|
frameskip_type = 0;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (strcmp(var.value, "auto") == 0)
|
|
frameskip_type = 1;
|
|
else if (strcmp(var.value, "manual") == 0)
|
|
frameskip_type = 2;
|
|
}
|
|
|
|
var.key = "wswan_frameskip_threshold";
|
|
var.value = NULL;
|
|
|
|
frameskip_threshold = 33;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
frameskip_threshold = strtol(var.value, NULL, 10);
|
|
|
|
/* (Re)Initialise frameskipping, if required */
|
|
if (startup || (frameskip_type != prev_frameskip_type))
|
|
init_frameskip();
|
|
|
|
var.key = "wswan_sound_sample_rate";
|
|
var.value = NULL;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
int old_value = RETRO_SAMPLE_RATE;
|
|
|
|
RETRO_SAMPLE_RATE = atoi(var.value);
|
|
|
|
if (old_value != RETRO_SAMPLE_RATE)
|
|
update_audio = true;
|
|
}
|
|
|
|
var.key = "wswan_gfx_colors";
|
|
var.value = NULL;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && startup)
|
|
{
|
|
unsigned old_value = RETRO_PIX_BYTES;
|
|
|
|
if (!strcmp(var.value, "16bit"))
|
|
{
|
|
RETRO_PIX_BYTES = 2;
|
|
RETRO_PIX_DEPTH = 16;
|
|
}
|
|
else if (!strcmp(var.value, "24bit"))
|
|
{
|
|
RETRO_PIX_BYTES = 4;
|
|
RETRO_PIX_DEPTH = 24;
|
|
}
|
|
|
|
if (old_value != RETRO_PIX_BYTES)
|
|
update_video = true;
|
|
}
|
|
}
|
|
|
|
void retro_init(void)
|
|
{
|
|
struct retro_log_callback log;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
|
|
log_cb = log.log;
|
|
else
|
|
log_cb = NULL;
|
|
|
|
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;
|
|
|
|
frameskip_type = 0;
|
|
frameskip_threshold = 0;
|
|
frameskip_counter = 0;
|
|
retro_audio_buff_active = false;
|
|
retro_audio_buff_occupancy = 0;
|
|
retro_audio_buff_underrun = false;
|
|
audio_latency = 0;
|
|
update_audio_latency = false;
|
|
|
|
check_system_specs();
|
|
check_variables(true);
|
|
check_depth();
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL))
|
|
libretro_supports_bitmasks = true;
|
|
}
|
|
|
|
void retro_reset(void)
|
|
{
|
|
DoSimpleCommand(MDFN_MSC_RESET);
|
|
}
|
|
|
|
bool retro_load_game_special(unsigned a, const struct retro_game_info *b, size_t c)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static void set_volume (uint32_t *ptr, unsigned number)
|
|
{
|
|
switch(number)
|
|
{
|
|
default:
|
|
*ptr = number;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define MAX_PLAYERS 1
|
|
#define MAX_BUTTONS 11
|
|
static uint16_t input_buf;
|
|
|
|
bool retro_load_game(const struct retro_game_info *info)
|
|
{
|
|
const unsigned rot_angle = 0;
|
|
|
|
struct retro_input_descriptor desc[] = {
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "X Cursor Left" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "X Cursor Up" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "X Cursor Down" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "X Cursor Right" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "Y Cursor Left" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, "Y Cursor Up" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, "Y Cursor Down" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Y Cursor Right" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Rotate screen + active D-Pad" },
|
|
|
|
{ 0 },
|
|
};
|
|
|
|
if (!info)
|
|
return false;
|
|
|
|
environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
|
|
|
|
overscan = false;
|
|
environ_cb(RETRO_ENVIRONMENT_GET_OVERSCAN, &overscan);
|
|
|
|
if (!MDFNI_LoadGame(MEDNAFEN_CORE_NAME_MODULE,
|
|
(const uint8_t*)info->data, info->size))
|
|
return false;
|
|
|
|
SetInput(0, "gamepad", &input_buf);
|
|
|
|
surf = (MDFN_Surface*)calloc(1, sizeof(*surf));
|
|
|
|
if (!surf)
|
|
return false;
|
|
|
|
surf->width = FB_WIDTH;
|
|
surf->height = FB_HEIGHT;
|
|
surf->pitch = FB_WIDTH;
|
|
surf->depth = RETRO_PIX_DEPTH;
|
|
|
|
surf->pixels = (uint16_t*)calloc(1, FB_WIDTH * FB_HEIGHT * sizeof(uint32_t));
|
|
|
|
if (!surf->pixels)
|
|
{
|
|
free(surf);
|
|
surf = NULL;
|
|
return false;
|
|
}
|
|
|
|
/* Check whether 'hardware' rotation (via frontend
|
|
* gfx driver) is supported */
|
|
hw_rotate_enabled = environ_cb(RETRO_ENVIRONMENT_SET_ROTATION, (void*)&rot_angle);
|
|
|
|
if (!hw_rotate_enabled && !rotate_buf)
|
|
{
|
|
rotate_buf = (uint16_t*)calloc(1, FB_WIDTH * FB_HEIGHT * sizeof(uint32_t));
|
|
if (!rotate_buf)
|
|
{
|
|
free(surf->pixels);
|
|
free(surf);
|
|
surf = NULL;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
rotate_tall = false;
|
|
select_pressed_last_frame = false;
|
|
rotate_joymap = 0;
|
|
|
|
check_variables(false);
|
|
|
|
WSwan_SetPixelFormat(RETRO_PIX_DEPTH,
|
|
mono_pal_start, mono_pal_end);
|
|
|
|
update_video = false;
|
|
update_audio = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
void retro_unload_game(void)
|
|
{
|
|
MDFNI_CloseGame();
|
|
|
|
if (surf)
|
|
{
|
|
if (surf->pixels)
|
|
free(surf->pixels);
|
|
free(surf);
|
|
}
|
|
surf = NULL;
|
|
|
|
if (rotate_buf)
|
|
free(rotate_buf);
|
|
rotate_buf = NULL;
|
|
}
|
|
|
|
static void update_input(void)
|
|
{
|
|
static unsigned map[2][11] = {
|
|
{
|
|
RETRO_DEVICE_ID_JOYPAD_UP, /* X Cursor horizontal-layout games */
|
|
RETRO_DEVICE_ID_JOYPAD_RIGHT, /* X Cursor horizontal-layout games */
|
|
RETRO_DEVICE_ID_JOYPAD_DOWN, /* X Cursor horizontal-layout games */
|
|
RETRO_DEVICE_ID_JOYPAD_LEFT, /* X Cursor horizontal-layout games */
|
|
RETRO_DEVICE_ID_JOYPAD_R2, /* Y Cursor UP vertical-layout games */
|
|
RETRO_DEVICE_ID_JOYPAD_R, /* Y Cursor RIGHT vertical-layout games */
|
|
RETRO_DEVICE_ID_JOYPAD_L2, /* Y Cursor DOWN vertical-layout games */
|
|
RETRO_DEVICE_ID_JOYPAD_L, /* Y Cursor LEFT vertical-layout games */
|
|
RETRO_DEVICE_ID_JOYPAD_START,
|
|
RETRO_DEVICE_ID_JOYPAD_A,
|
|
RETRO_DEVICE_ID_JOYPAD_B,
|
|
},
|
|
{
|
|
RETRO_DEVICE_ID_JOYPAD_Y, /* X Cursor horizontal-layout games */
|
|
RETRO_DEVICE_ID_JOYPAD_X, /* X Cursor horizontal-layout games */
|
|
RETRO_DEVICE_ID_JOYPAD_A, /* X Cursor horizontal-layout games */
|
|
RETRO_DEVICE_ID_JOYPAD_B, /* X Cursor horizontal-layout games */
|
|
RETRO_DEVICE_ID_JOYPAD_LEFT, /* Y Cursor UP vertical-layout games */
|
|
RETRO_DEVICE_ID_JOYPAD_UP, /* Y Cursor RIGHT vertical-layout games */
|
|
RETRO_DEVICE_ID_JOYPAD_RIGHT, /* Y Cursor DOWN vertical-layout games */
|
|
RETRO_DEVICE_ID_JOYPAD_DOWN, /* Y Cursor LEFT vertical-layout games */
|
|
RETRO_DEVICE_ID_JOYPAD_START,
|
|
RETRO_DEVICE_ID_JOYPAD_L,
|
|
RETRO_DEVICE_ID_JOYPAD_R,
|
|
}
|
|
};
|
|
unsigned i;
|
|
int16_t bitmask = 0;
|
|
bool select_button;
|
|
bool joymap;
|
|
#ifdef MSB_FIRST
|
|
union {
|
|
uint8_t b[2];
|
|
uint16_t s;
|
|
} u;
|
|
#endif
|
|
|
|
input_buf = 0;
|
|
|
|
if (libretro_supports_bitmasks)
|
|
bitmask = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
|
|
else
|
|
{
|
|
for (i = 0; i < (RETRO_DEVICE_ID_JOYPAD_R3+1); i++)
|
|
bitmask |= input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, i) ? 1 << i : 0;
|
|
}
|
|
|
|
select_button = bitmask & (1 << RETRO_DEVICE_ID_JOYPAD_SELECT);
|
|
if (!rotate_tall_override &&
|
|
select_button &&
|
|
!select_pressed_last_frame)
|
|
{
|
|
rotate_tall = !rotate_tall;
|
|
rotate_display();
|
|
}
|
|
select_pressed_last_frame = select_button;
|
|
|
|
joymap = (rotate_joymap == 2) ? rotate_tall : (rotate_joymap ? true : false);
|
|
|
|
for (i = 0; i < MAX_BUTTONS; i++)
|
|
input_buf |= map[joymap][i] != -1u && ((1 << map[joymap][i]) & bitmask) ? (1 << i) : 0;
|
|
|
|
#ifdef MSB_FIRST
|
|
u.s = input_buf;
|
|
input_buf = u.b[0] | u.b[1] << 8;
|
|
#endif
|
|
}
|
|
|
|
static uint64_t video_frames, audio_frames;
|
|
|
|
void retro_run(void)
|
|
{
|
|
int total;
|
|
unsigned width, height;
|
|
static MDFN_Rect rects[FB_MAX_HEIGHT];
|
|
static int16_t sound_buf[0x10000];
|
|
int32 SoundBufSize;
|
|
EmulateSpecStruct spec;
|
|
bool updated = false;
|
|
int skip_frame = 0;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
|
|
check_variables(false);
|
|
|
|
input_poll_cb();
|
|
|
|
update_input();
|
|
|
|
/* Check whether current frame should
|
|
* be skipped */
|
|
if ((frameskip_type > 0) && retro_audio_buff_active)
|
|
{
|
|
switch (frameskip_type)
|
|
{
|
|
case 1: /* auto */
|
|
skip_frame = retro_audio_buff_underrun ? 1 : 0;
|
|
break;
|
|
case 2: /* manual */
|
|
skip_frame = (retro_audio_buff_occupancy < frameskip_threshold) ? 1 : 0;
|
|
break;
|
|
default:
|
|
skip_frame = 0;
|
|
break;
|
|
}
|
|
|
|
if (!skip_frame ||
|
|
(frameskip_counter >= FRAMESKIP_MAX))
|
|
{
|
|
skip_frame = 0;
|
|
frameskip_counter = 0;
|
|
}
|
|
else
|
|
frameskip_counter++;
|
|
}
|
|
|
|
/* If frameskip settings have changed, update
|
|
* frontend audio latency */
|
|
if (update_audio_latency)
|
|
{
|
|
environ_cb(RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY,
|
|
&audio_latency);
|
|
update_audio_latency = false;
|
|
}
|
|
|
|
rects[0].w = ~0;
|
|
|
|
spec.surface = surf;
|
|
spec.VideoFormatChanged = update_video;
|
|
spec.DisplayRect.x = 0;
|
|
spec.DisplayRect.y = 0;
|
|
spec.DisplayRect.w = 0;
|
|
spec.DisplayRect.h = 0;
|
|
spec.LineWidths = rects;
|
|
spec.skip = skip_frame;
|
|
spec.SoundFormatChanged = update_audio;
|
|
spec.SoundBufMaxSize = sizeof(sound_buf) >> 1;
|
|
spec.SoundBufSize = 0;
|
|
spec.SoundBufSizeALMS = 0;
|
|
spec.MasterCycles = 0;
|
|
spec.MasterCyclesALMS = 0;
|
|
|
|
if (update_video || update_audio)
|
|
{
|
|
struct retro_system_av_info system_av_info;
|
|
|
|
if (update_video)
|
|
{
|
|
memset(&system_av_info, 0, sizeof(system_av_info));
|
|
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &system_av_info);
|
|
}
|
|
|
|
retro_get_system_av_info(&system_av_info);
|
|
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &system_av_info);
|
|
|
|
if (update_video)
|
|
rotate_display();
|
|
|
|
surf->depth = RETRO_PIX_DEPTH;
|
|
|
|
update_video = false;
|
|
update_audio = false;
|
|
}
|
|
|
|
Emulate(&spec, sound_buf);
|
|
|
|
SoundBufSize = spec.SoundBufSize - spec.SoundBufSizeALMS;
|
|
spec.SoundBufSize = spec.SoundBufSizeALMS + SoundBufSize;
|
|
|
|
width = spec.DisplayRect.w;
|
|
height = spec.DisplayRect.h;
|
|
|
|
if (hw_rotate_enabled || !rotate_tall)
|
|
{
|
|
if (!skip_frame)
|
|
video_cb(surf->pixels, width, height, FB_WIDTH * RETRO_PIX_BYTES);
|
|
else
|
|
video_cb(NULL, width, height, FB_WIDTH * RETRO_PIX_BYTES);
|
|
}
|
|
else
|
|
{
|
|
if (!skip_frame)
|
|
{
|
|
/* Perform software-based display rotation */
|
|
if (RETRO_PIX_BYTES == 4)
|
|
ROTATE_PIXEL_BUF(
|
|
uint32_t, surf->pixels, width, height, rotate_buf)
|
|
else
|
|
ROTATE_PIXEL_BUF(
|
|
uint16_t, surf->pixels, width, height, rotate_buf)
|
|
|
|
video_cb(rotate_buf, height, width, FB_HEIGHT * RETRO_PIX_BYTES);
|
|
}
|
|
else
|
|
video_cb(NULL, height, width, FB_HEIGHT * RETRO_PIX_BYTES);
|
|
}
|
|
|
|
video_frames++;
|
|
audio_frames += spec.SoundBufSize;
|
|
|
|
for (total = 0; total < spec.SoundBufSize; )
|
|
total += audio_batch_cb(sound_buf + total*2,
|
|
spec.SoundBufSize - total);
|
|
}
|
|
|
|
void retro_get_system_info(struct retro_system_info *info)
|
|
{
|
|
memset(info, 0, sizeof(*info));
|
|
info->library_name = MEDNAFEN_CORE_NAME;
|
|
#ifndef GIT_VERSION
|
|
#define GIT_VERSION ""
|
|
#endif
|
|
info->library_version = MEDNAFEN_CORE_VERSION GIT_VERSION;
|
|
info->need_fullpath = false;
|
|
info->valid_extensions = MEDNAFEN_CORE_EXTENSIONS;
|
|
info->block_extract = false;
|
|
}
|
|
|
|
void retro_get_system_av_info(struct retro_system_av_info *info)
|
|
{
|
|
memset(info, 0, sizeof(*info));
|
|
|
|
info->timing.fps = MEDNAFEN_CORE_TIMING_FPS;
|
|
info->timing.sample_rate = RETRO_SAMPLE_RATE;
|
|
|
|
if (hw_rotate_enabled || !rotate_tall)
|
|
{
|
|
info->geometry.base_width = MEDNAFEN_CORE_GEOMETRY_BASE_W;
|
|
info->geometry.base_height = MEDNAFEN_CORE_GEOMETRY_BASE_H;
|
|
}
|
|
else
|
|
{
|
|
info->geometry.base_width = MEDNAFEN_CORE_GEOMETRY_BASE_H;
|
|
info->geometry.base_height = MEDNAFEN_CORE_GEOMETRY_BASE_W;
|
|
}
|
|
|
|
info->geometry.max_width = MEDNAFEN_CORE_GEOMETRY_MAX_W;
|
|
if (hw_rotate_enabled)
|
|
info->geometry.max_height = MEDNAFEN_CORE_GEOMETRY_MAX_H;
|
|
else
|
|
info->geometry.max_height = MEDNAFEN_CORE_GEOMETRY_MAX_W;
|
|
|
|
if (rotate_tall)
|
|
info->geometry.aspect_ratio = 1.0f / MEDNAFEN_CORE_GEOMETRY_ASPECT_RATIO;
|
|
else
|
|
info->geometry.aspect_ratio = MEDNAFEN_CORE_GEOMETRY_ASPECT_RATIO;
|
|
|
|
check_depth();
|
|
}
|
|
|
|
void retro_deinit(void)
|
|
{
|
|
if (surf)
|
|
{
|
|
if (surf->pixels)
|
|
free(surf->pixels);
|
|
free(surf);
|
|
}
|
|
surf = NULL;
|
|
|
|
if (rotate_buf)
|
|
free(rotate_buf);
|
|
rotate_buf = NULL;
|
|
|
|
if (log_cb)
|
|
{
|
|
log_cb(RETRO_LOG_INFO, "[%s]: Samples / Frame: %.5f\n",
|
|
MEDNAFEN_CORE_NAME, (double)audio_frames / video_frames);
|
|
log_cb(RETRO_LOG_INFO, "[%s]: Estimated FPS: %.5f\n",
|
|
MEDNAFEN_CORE_NAME, (double)video_frames * 44100 / audio_frames);
|
|
}
|
|
|
|
libretro_supports_bitmasks = false;
|
|
}
|
|
|
|
unsigned retro_get_region(void)
|
|
{
|
|
return RETRO_REGION_NTSC; /* FIXME: Regions for other cores. */
|
|
}
|
|
|
|
unsigned retro_api_version(void)
|
|
{
|
|
return RETRO_API_VERSION;
|
|
}
|
|
|
|
void retro_set_controller_port_device(unsigned in_port, unsigned device) { }
|
|
|
|
void retro_set_environment(retro_environment_t cb)
|
|
{
|
|
environ_cb = cb;
|
|
|
|
libretro_set_core_options(environ_cb);
|
|
}
|
|
|
|
void retro_set_audio_sample(retro_audio_sample_t cb)
|
|
{
|
|
}
|
|
|
|
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)
|
|
{
|
|
input_poll_cb = cb;
|
|
}
|
|
|
|
void retro_set_input_state(retro_input_state_t cb)
|
|
{
|
|
input_state_cb = cb;
|
|
}
|
|
|
|
void retro_set_video_refresh(retro_video_refresh_t cb)
|
|
{
|
|
video_cb = cb;
|
|
}
|
|
|
|
size_t retro_serialize_size(void)
|
|
{
|
|
StateMem st;
|
|
|
|
st.data = NULL;
|
|
st.loc = 0;
|
|
st.len = 0;
|
|
st.malloced = 0;
|
|
st.initial_malloc = 0;
|
|
|
|
if (!MDFNSS_SaveSM(&st, 0, 0, NULL, NULL, NULL))
|
|
return 0;
|
|
|
|
free(st.data);
|
|
|
|
return st.len;
|
|
}
|
|
|
|
bool retro_serialize(void *data, size_t size)
|
|
{
|
|
StateMem st;
|
|
bool ret = false;
|
|
uint8_t *_dat = (uint8_t*)malloc(size);
|
|
|
|
if (!_dat)
|
|
return false;
|
|
|
|
/* Mednafen can realloc the buffer so we need to ensure this is safe. */
|
|
st.data = _dat;
|
|
st.loc = 0;
|
|
st.len = 0;
|
|
st.malloced = size;
|
|
st.initial_malloc = 0;
|
|
|
|
ret = MDFNSS_SaveSM(&st, 0, 0, NULL, NULL, NULL);
|
|
|
|
memcpy(data, st.data, size);
|
|
free(st.data);
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool retro_unserialize(const void *data, size_t size)
|
|
{
|
|
StateMem st;
|
|
|
|
st.data = (uint8_t*)data;
|
|
st.loc = 0;
|
|
st.len = size;
|
|
st.malloced = 0;
|
|
st.initial_malloc = 0;
|
|
|
|
return MDFNSS_LoadSM(&st, 0, 0);
|
|
}
|
|
|
|
void *retro_get_memory_data(unsigned type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case RETRO_MEMORY_SAVE_RAM:
|
|
if (eeprom_size)
|
|
return (uint8_t*)wsEEPROM;
|
|
else if (SRAMSize)
|
|
return wsSRAM;
|
|
break;
|
|
case RETRO_MEMORY_SYSTEM_RAM:
|
|
return (uint8_t*)wsRAM;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
size_t retro_get_memory_size(unsigned type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case RETRO_MEMORY_SAVE_RAM:
|
|
if (eeprom_size)
|
|
return eeprom_size;
|
|
else if (SRAMSize)
|
|
return SRAMSize;
|
|
break;
|
|
case RETRO_MEMORY_SYSTEM_RAM:
|
|
return wsRAMSize;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void retro_cheat_reset(void) { }
|
|
void retro_cheat_set(unsigned a, bool b, const char *c) { }
|
|
|
|
void MDFND_MidSync(const EmulateSpecStruct *a) { }
|
|
void MDFN_MidLineUpdate(EmulateSpecStruct *espec, int y) { }
|