#include "stdafx.h" #include #include #include #include "LibretroRenderer.h" #include "LibretroSoundManager.h" #include "LibretroKeyManager.h" #include "LibretroMessageManager.h" #include "libretro.h" #include "../Core/Console.h" #include "../Core/VideoDecoder.h" #include "../Core/VideoRenderer.h" #include "../Core/MemoryManager.h" #include "../Core/BaseMapper.h" #include "../Core/EmulationSettings.h" #include "../Core/CheatManager.h" #include "../Core/HdData.h" #include "../Core/SaveStateManager.h" #include "../Core/DebuggerTypes.h" #include "../Core/GameDatabase.h" #include "../Utilities/FolderUtilities.h" #include "../Utilities/HexUtilities.h" #define DEVICE_AUTO RETRO_DEVICE_JOYPAD #define DEVICE_GAMEPAD RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 0) #define DEVICE_POWERPAD RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 1) #define DEVICE_FAMILYTRAINER RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 2) #define DEVICE_PARTYTAP RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 3) #define DEVICE_PACHINKO RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 4) #define DEVICE_EXCITINGBOXING RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 5) #define DEVICE_KONAMIHYPERSHOT RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 6) #define DEVICE_SNESGAMEPAD RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 7) #define DEVICE_VBGAMEPAD RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 8) #define DEVICE_ZAPPER RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_POINTER, 0) #define DEVICE_OEKAKIDS RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_POINTER, 1) #define DEVICE_BANDAIHYPERSHOT RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_POINTER, 2) #define DEVICE_ARKANOID RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_MOUSE, 0) #define DEVICE_HORITRACK RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_MOUSE, 1) #define DEVICE_SNESMOUSE RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_MOUSE, 2) #define DEVICE_ASCIITURBOFILE RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_NONE, 0) #define DEVICE_BATTLEBOX RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_NONE, 1) #define DEVICE_FOURPLAYERADAPTER RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_NONE, 2) static retro_log_printf_t logCallback = nullptr; static retro_environment_t retroEnv = nullptr; static unsigned _inputDevices[5] = { DEVICE_AUTO, DEVICE_AUTO, DEVICE_AUTO, DEVICE_AUTO, DEVICE_AUTO }; static bool _hdPacksEnabled = false; static string _mesenVersion = ""; static int32_t _saveStateSize = -1; static bool _shiftButtonsClockwise = false; static int32_t _audioSampleRate = 44100; //Include game database as a byte array (representing the MesenDB.txt file) #include "MesenDB.inc" static std::shared_ptr _console; static std::unique_ptr _renderer; static std::unique_ptr _soundManager; static std::unique_ptr _keyManager; static std::unique_ptr _messageManager; static constexpr const char* MesenNtscFilter = "mesen_ntsc_filter"; static constexpr const char* MesenPalette = "mesen_palette"; static constexpr const char* MesenNoSpriteLimit = "mesen_nospritelimit"; static constexpr const char* MesenOverclock = "mesen_overclock"; static constexpr const char* MesenOverclockType = "mesen_overclock_type"; static constexpr const char* MesenOverscanLeft = "mesen_overscan_left"; static constexpr const char* MesenOverscanRight = "mesen_overscan_right"; static constexpr const char* MesenOverscanTop = "mesen_overscan_up"; static constexpr const char* MesenOverscanBottom = "mesen_overscan_down"; static constexpr const char* MesenAspectRatio = "mesen_aspect_ratio"; static constexpr const char* MesenRegion = "mesen_region"; static constexpr const char* MesenRamState = "mesen_ramstate"; static constexpr const char* MesenControllerTurboSpeed = "mesen_controllerturbospeed"; static constexpr const char* MesenFdsAutoSelectDisk = "mesen_fdsautoinsertdisk"; static constexpr const char* MesenFdsFastForwardLoad = "mesen_fdsfastforwardload"; static constexpr const char* MesenHdPacks = "mesen_hdpacks"; static constexpr const char* MesenScreenRotation = "mesen_screenrotation"; static constexpr const char* MesenFakeStereo = "mesen_fake_stereo"; static constexpr const char* MesenMuteTriangleUltrasonic = "mesen_mute_triangle_ultrasonic"; static constexpr const char* MesenReduceDmcPopping = "mesen_reduce_dmc_popping"; static constexpr const char* MesenSwapDutyCycle = "mesen_swap_duty_cycle"; static constexpr const char* MesenDisableNoiseModeFlag = "mesen_disable_noise_mode_flag"; static constexpr const char* MesenShiftButtonsClockwise = "mesen_shift_buttons_clockwise"; static constexpr const char* MesenAudioSampleRate = "mesen_audio_sample_rate"; uint32_t defaultPalette[0x40] { 0xFF666666, 0xFF002A88, 0xFF1412A7, 0xFF3B00A4, 0xFF5C007E, 0xFF6E0040, 0xFF6C0600, 0xFF561D00, 0xFF333500, 0xFF0B4800, 0xFF005200, 0xFF004F08, 0xFF00404D, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFADADAD, 0xFF155FD9, 0xFF4240FF, 0xFF7527FE, 0xFFA01ACC, 0xFFB71E7B, 0xFFB53120, 0xFF994E00, 0xFF6B6D00, 0xFF388700, 0xFF0C9300, 0xFF008F32, 0xFF007C8D, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFEFF, 0xFF64B0FF, 0xFF9290FF, 0xFFC676FF, 0xFFF36AFF, 0xFFFE6ECC, 0xFFFE8170, 0xFFEA9E22, 0xFFBCBE00, 0xFF88D800, 0xFF5CE430, 0xFF45E082, 0xFF48CDDE, 0xFF4F4F4F, 0xFF000000, 0xFF000000, 0xFFFFFEFF, 0xFFC0DFFF, 0xFFD3D2FF, 0xFFE8C8FF, 0xFFFBC2FF, 0xFFFEC4EA, 0xFFFECCC5, 0xFFF7D8A5, 0xFFE4E594, 0xFFCFEF96, 0xFFBDF4AB, 0xFFB3F3CC, 0xFFB5EBF2, 0xFFB8B8B8, 0xFF000000, 0xFF000000 }; uint32_t unsaturatedPalette[0x40] { 0xFF6B6B6B, 0xFF001E87, 0xFF1F0B96, 0xFF3B0C87, 0xFF590D61, 0xFF5E0528, 0xFF551100, 0xFF461B00, 0xFF303200, 0xFF0A4800, 0xFF004E00, 0xFF004619, 0xFF003A58, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFB2B2B2, 0xFF1A53D1, 0xFF4835EE, 0xFF7123EC, 0xFF9A1EB7, 0xFFA51E62, 0xFFA52D19, 0xFF874B00, 0xFF676900, 0xFF298400, 0xFF038B00, 0xFF008240, 0xFF007891, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFF63ADFD, 0xFF908AFE, 0xFFB977FC, 0xFFE771FE, 0xFFF76FC9, 0xFFF5836A, 0xFFDD9C29, 0xFFBDB807, 0xFF84D107, 0xFF5BDC3B, 0xFF48D77D, 0xFF48CCCE, 0xFF555555, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFFC4E3FE, 0xFFD7D5FE, 0xFFE6CDFE, 0xFFF9CAFE, 0xFFFEC9F0, 0xFFFED1C7, 0xFFF7DCAC, 0xFFE8E89C, 0xFFD1F29D, 0xFFBFF4B1, 0xFFB7F5CD, 0xFFB7F0EE, 0xFFBEBEBE, 0xFF000000, 0xFF000000 }; uint32_t yuvPalette[0x40] { 0xFF666666, 0xFF002A88, 0xFF1412A7, 0xFF3B00A4, 0xFF5C007E, 0xFF6E0040, 0xFF6C0700, 0xFF561D00, 0xFF333500, 0xFF0C4800, 0xFF005200, 0xFF004C18, 0xFF003E5B, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFADADAD, 0xFF155FD9, 0xFF4240FF, 0xFF7527FE, 0xFFA01ACC, 0xFFB71E7B, 0xFFB53120, 0xFF994E00, 0xFF6B6D00, 0xFF388700, 0xFF0D9300, 0xFF008C47, 0xFF007AA0, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFF64B0FF, 0xFF9290FF, 0xFFC676FF, 0xFFF26AFF, 0xFFFF6ECC, 0xFFFF8170, 0xFFEA9E22, 0xFFBCBE00, 0xFF88D800, 0xFF5CE430, 0xFF45E082, 0xFF48CDDE, 0xFF4F4F4F, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFFC0DFFF, 0xFFD3D2FF, 0xFFE8C8FF, 0xFFFAC2FF, 0xFFFFC4EA, 0xFFFFCCC5, 0xFFF7D8A5, 0xFFE4E594, 0xFFCFEF96, 0xFFBDF4AB, 0xFFB3F3CC, 0xFFB5EBF2, 0xFFB8B8B8, 0xFF000000, 0xFF000000 }; uint32_t nestopiaRgbPalette[0x40] { 0xFF6D6D6D, 0xFF002492, 0xFF0000DB, 0xFF6D49DB, 0xFF92006D, 0xFFB6006D, 0xFFB62400, 0xFF924900, 0xFF6D4900, 0xFF244900, 0xFF006D24, 0xFF009200, 0xFF004949, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFB6B6B6, 0xFF006DDB, 0xFF0049FF, 0xFF9200FF, 0xFFB600FF, 0xFFFF0092, 0xFFFF0000, 0xFFDB6D00, 0xFF926D00, 0xFF249200, 0xFF009200, 0xFF00B66D, 0xFF009292, 0xFF242424, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFF6DB6FF, 0xFF9292FF, 0xFFDB6DFF, 0xFFFF00FF, 0xFFFF6DFF, 0xFFFF9200, 0xFFFFB600, 0xFFDBDB00, 0xFF6DDB00, 0xFF00FF00, 0xFF49FFDB, 0xFF00FFFF, 0xFF494949, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFFB6DBFF, 0xFFDBB6FF, 0xFFFFB6FF, 0xFFFF92FF, 0xFFFFB6B6, 0xFFFFDB92, 0xFFFFFF49, 0xFFFFFF6D, 0xFFB6FF49, 0xFF92FF6D, 0xFF49FFDB, 0xFF92DBFF, 0xFF929292, 0xFF000000, 0xFF000000 }; uint32_t compositeDirectPalette[0x40] { 0xFF656565, 0xFF00127D, 0xFF18008E, 0xFF360082, 0xFF56005D, 0xFF5A0018, 0xFF4F0500, 0xFF381900, 0xFF1D3100, 0xFF003D00, 0xFF004100, 0xFF003B17, 0xFF002E55, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFAFAFAF, 0xFF194EC8, 0xFF472FE3, 0xFF6B1FD7, 0xFF931BAE, 0xFF9E1A5E, 0xFF993200, 0xFF7B4B00, 0xFF5B6700, 0xFF267A00, 0xFF008200, 0xFF007A3E, 0xFF006E8A, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFF64A9FF, 0xFF8E89FF, 0xFFB676FF, 0xFFE06FFF, 0xFFEF6CC4, 0xFFF0806A, 0xFFD8982C, 0xFFB9B40A, 0xFF83CB0C, 0xFF5BD63F, 0xFF4AD17E, 0xFF4DC7CB, 0xFF4C4C4C, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFFC7E5FF, 0xFFD9D9FF, 0xFFE9D1FF, 0xFFF9CEFF, 0xFFFFCCF1, 0xFFFFD4CB, 0xFFF8DFB1, 0xFFEDEAA4, 0xFFD6F4A4, 0xFFC5F8B8, 0xFFBEF6D3, 0xFFBFF1F1, 0xFFB9B9B9, 0xFF000000, 0xFF000000 }; uint32_t nesClassicPalette[0x40] { 0xFF60615F, 0xFF000083, 0xFF1D0195, 0xFF340875, 0xFF51055E, 0xFF56000F, 0xFF4C0700, 0xFF372308, 0xFF203A0B, 0xFF0F4B0E, 0xFF194C16, 0xFF02421E, 0xFF023154, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFA9AAA8, 0xFF104BBF, 0xFF4712D8, 0xFF6300CA, 0xFF8800A9, 0xFF930B46, 0xFF8A2D04, 0xFF6F5206, 0xFF5C7114, 0xFF1B8D12, 0xFF199509, 0xFF178448, 0xFF206B8E, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFBFBFB, 0xFF6699F8, 0xFF8974F9, 0xFFAB58F8, 0xFFD557EF, 0xFFDE5FA9, 0xFFDC7F59, 0xFFC7A224, 0xFFA7BE03, 0xFF75D703, 0xFF60E34F, 0xFF3CD68D, 0xFF56C9CC, 0xFF414240, 0xFF000000, 0xFF000000, 0xFFFBFBFB, 0xFFBED4FA, 0xFFC9C7F9, 0xFFD7BEFA, 0xFFE8B8F9, 0xFFF5BAE5, 0xFFF3CAC2, 0xFFDFCDA7, 0xFFD9E09C, 0xFFC9EB9E, 0xFFC0EDB8, 0xFFB5F4C7, 0xFFB9EAE9, 0xFFABABAB, 0xFF000000, 0xFF000000 }; uint32_t originalHardwarePalette[0x40] { 0xFF6A6D6A, 0xFF00127D, 0xFF1E008A, 0xFF3B007D, 0xFF56005D, 0xFF5A0018, 0xFF4F0D00, 0xFF381E00, 0xFF203100, 0xFF003D00, 0xFF004000, 0xFF003B1E, 0xFF002E55, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFB9BCB9, 0xFF194EC8, 0xFF472FE3, 0xFF751FD7, 0xFF931EAD, 0xFF9E245E, 0xFF963800, 0xFF7B5000, 0xFF5B6700, 0xFF267A00, 0xFF007F00, 0xFF007842, 0xFF006E8A, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFF69AEFF, 0xFF9798FF, 0xFFB687FF, 0xFFE278FF, 0xFFF279C7, 0xFFF58F6F, 0xFFDDA932, 0xFFBCB70D, 0xFF88D015, 0xFF60DB49, 0xFF4FD687, 0xFF50CACE, 0xFF515451, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFFCCEAFF, 0xFFDEE2FF, 0xFFEEDAFF, 0xFFFAD7FD, 0xFFFDD7F6, 0xFFFDDCD0, 0xFFFAE8B6, 0xFFF2F1A9, 0xFFDBFBA9, 0xFFCAFFBD, 0xFFC3FBD8, 0xFFC4F6F6, 0xFFBEC1BE, 0xFF000000, 0xFF000000 }; uint32_t pvmStylePalette[0x40] { 0xFF696964, 0xFF001774, 0xFF28007D, 0xFF3E006D, 0xFF560057, 0xFF5E0013, 0xFF531A00, 0xFF3B2400, 0xFF2A3000, 0xFF143A00, 0xFF003F00, 0xFF003B1E, 0xFF003050, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFB9B9B4, 0xFF1453B9, 0xFF4D2CDA, 0xFF7A1EC8, 0xFF98189C, 0xFF9D2344, 0xFFA03E00, 0xFF8D5500, 0xFF656D00, 0xFF2C7900, 0xFF008100, 0xFF007D42, 0xFF00788A, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFF69A8FF, 0xFF9A96FF, 0xFFC28AFA, 0xFFEA7DFA, 0xFFF387B4, 0xFFF1986C, 0xFFE6B327, 0xFFD7C805, 0xFF90DF07, 0xFF64E53C, 0xFF45E27D, 0xFF48D5D9, 0xFF4B4B46, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFFD2EAFF, 0xFFE2E2FF, 0xFFF2D8FF, 0xFFF8D2FF, 0xFFF8D9EA, 0xFFFADEB9, 0xFFF9E89B, 0xFFF3F28C, 0xFFD3FA91, 0xFFB8FCA8, 0xFFAEFACA, 0xFFCAF3F3, 0xFFBEBEB9, 0xFF000000, 0xFF000000 }; uint32_t sonyCxa2025AsPalette[0x40] { 0xFF585858, 0xFF00238C, 0xFF00139B, 0xFF2D0585, 0xFF5D0052, 0xFF7A0017, 0xFF7A0800, 0xFF5F1800, 0xFF352A00, 0xFF093900, 0xFF003F00, 0xFF003C22, 0xFF00325D, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFA1A1A1, 0xFF0053EE, 0xFF153CFE, 0xFF6028E4, 0xFFA91D98, 0xFFD41E41, 0xFFD22C00, 0xFFAA4400, 0xFF6C5E00, 0xFF2D7300, 0xFF007D06, 0xFF007852, 0xFF0069A9, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFF1FA5FE, 0xFF5E89FE, 0xFFB572FE, 0xFFFE65F6, 0xFFFE6790, 0xFFFE773C, 0xFFFE9308, 0xFFC4B200, 0xFF79CA10, 0xFF3AD54A, 0xFF11D1A4, 0xFF06BFFE, 0xFF424242, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFFA0D9FE, 0xFFBDCCFE, 0xFFE1C2FE, 0xFFFEBCFB, 0xFFFEBDD0, 0xFFFEC5A9, 0xFFFED18E, 0xFFE9DE86, 0xFFC7E992, 0xFFA8EEB0, 0xFF95ECD9, 0xFF91E4FE, 0xFFACACAC, 0xFF000000, 0xFF000000 }; uint32_t wavebeamPalette[0x40] { 0xFF6B6B6B, 0xFF001B88, 0xFF21009A, 0xFF40008C, 0xFF600067, 0xFF64001E, 0xFF590800, 0xFF481600, 0xFF283600, 0xFF004500, 0xFF004908, 0xFF00421D, 0xFF003659, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFB4B4B4, 0xFF1555D3, 0xFF4337EF, 0xFF7425DF, 0xFF9C19B9, 0xFFAC0F64, 0xFFAA2C00, 0xFF8A4B00, 0xFF666B00, 0xFF218300, 0xFF008A00, 0xFF008144, 0xFF007691, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFF63B2FF, 0xFF7C9CFF, 0xFFC07DFE, 0xFFE977FF, 0xFFF572CD, 0xFFF4886B, 0xFFDDA029, 0xFFBDBD0A, 0xFF89D20E, 0xFF5CDE3E, 0xFF4BD886, 0xFF4DCFD2, 0xFF525252, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFFBCDFFF, 0xFFD2D2FF, 0xFFE1C8FF, 0xFFEFC7FF, 0xFFFFC3E1, 0xFFFFCAC6, 0xFFF2DAAD, 0xFFEBE3A0, 0xFFD2EDA2, 0xFFBCF4B4, 0xFFB5F1CE, 0xFFB6ECF1, 0xFFBFBFBF, 0xFF000000, 0xFF000000 }; extern "C" { void logMessage(retro_log_level level, const char* message) { if(logCallback) { logCallback(level, message); } } RETRO_API unsigned retro_api_version() { return RETRO_API_VERSION; } RETRO_API void retro_init() { struct retro_log_callback log; if(retroEnv(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log)) { logCallback = log.log; } else { logCallback = nullptr; } _console.reset(new Console()); _console->Init(); _renderer.reset(new LibretroRenderer(_console, retroEnv)); _soundManager.reset(new LibretroSoundManager(_console)); _keyManager.reset(new LibretroKeyManager(_console)); _messageManager.reset(new LibretroMessageManager(logCallback, retroEnv)); std::stringstream databaseData; databaseData.write((const char*)MesenDatabase, sizeof(MesenDatabase)); GameDatabase::LoadGameDb(databaseData); _console->GetSettings()->SetFlags(EmulationFlags::FdsAutoLoadDisk); _console->GetSettings()->SetFlags(EmulationFlags::AutoConfigureInput); _console->GetSettings()->SetSampleRate(_audioSampleRate); _console->GetSettings()->SetAutoSaveOptions(0, false); _console->GetSettings()->SetRewindBufferSize(0); } RETRO_API void retro_deinit() { _renderer.reset(); _soundManager.reset(); _keyManager.reset(); _messageManager.reset(); _console->SaveBatteries(); _console->Release(true); _console.reset(); } RETRO_API void retro_set_environment(retro_environment_t env) { retroEnv = env; static constexpr struct retro_variable vars[] = { { MesenNtscFilter, "NTSC filter; Disabled|Composite (Blargg)|S-Video (Blargg)|RGB (Blargg)|Monochrome (Blargg)|Bisqwit 2x|Bisqwit 4x|Bisqwit 8x" }, { MesenPalette, "Palette; Default|Composite Direct (by FirebrandX)|Nes Classic|Nestopia (RGB)|Original Hardware (by FirebrandX)|PVM Style (by FirebrandX)|Sony CXA2025AS|Unsaturated v6 (by FirebrandX)|YUV v3 (by FirebrandX)|Wavebeam (by nakedarthur)|Custom|Raw" }, { MesenOverclock, "Overclock; None|Low|Medium|High|Very High" }, { MesenOverclockType, "Overclock Type; Before NMI (Recommended)|After NMI" }, { MesenRegion, "Region; Auto|NTSC|PAL|Dendy" }, { MesenOverscanLeft, "Left Overscan; None|4px|8px|12px|16px" }, { MesenOverscanRight, "Right Overscan; None|4px|8px|12px|16px" }, { MesenOverscanTop, "Top Overscan; None|4px|8px|12px|16px" }, { MesenOverscanBottom, "Bottom Overscan; None|4px|8px|12px|16px" }, { MesenAspectRatio, "Aspect Ratio; Auto|No Stretching|NTSC|PAL|4:3|16:9" }, { MesenControllerTurboSpeed, "Controller Turbo Speed; Fast|Very Fast|Disabled|Slow|Normal" }, { MesenShiftButtonsClockwise, u8"Shift A/B/X/Y clockwise; disabled|enabled" }, { MesenHdPacks, "Enable HD Packs; enabled|disabled" }, { MesenNoSpriteLimit, "Remove sprite limit; disabled|enabled" }, { MesenFakeStereo, u8"Enable fake stereo effect; disabled|enabled" }, { MesenMuteTriangleUltrasonic, u8"Reduce popping on Triangle channel; enabled|disabled" }, { MesenReduceDmcPopping, u8"Reduce popping on DMC channel; enabled|disabled" }, { MesenSwapDutyCycle, u8"Swap Square channel duty cycles; disabled|enabled" }, { MesenDisableNoiseModeFlag, u8"Disable Noise channel mode flag; disabled|enabled" }, { MesenScreenRotation, u8"Screen Rotation; None|90 degrees|180 degrees|270 degrees" }, { MesenRamState, "Default power-on state for RAM; All 0s (Default)|All 1s|Random Values" }, { MesenFdsAutoSelectDisk, "FDS: Automatically insert disks; disabled|enabled" }, { MesenFdsFastForwardLoad, "FDS: Fast forward while loading; disabled|enabled" }, { MesenAudioSampleRate, "Sound Output Sample Rate; 96000|192000|384000|11025|22050|44100|48000" }, { NULL, NULL }, }; static constexpr struct retro_controller_description pads1[] = { { "Auto", DEVICE_AUTO }, { "Standard Controller", DEVICE_GAMEPAD }, { "Zapper", DEVICE_ZAPPER }, { "Power Pad", DEVICE_POWERPAD }, { "Arkanoid", DEVICE_ARKANOID }, { "SNES Controller", DEVICE_SNESGAMEPAD }, { "SNES Mouse", DEVICE_SNESMOUSE }, { "Virtual Boy Controller" ,DEVICE_VBGAMEPAD }, { NULL, 0 }, }; static constexpr struct retro_controller_description pads2[] = { { "Auto", DEVICE_AUTO }, { "Standard Controller", DEVICE_GAMEPAD }, { "Zapper", DEVICE_ZAPPER }, { "Power Pad", DEVICE_POWERPAD }, { "Arkanoid", DEVICE_ARKANOID }, { "SNES Controller", DEVICE_SNESGAMEPAD }, { "SNES Mouse", DEVICE_SNESMOUSE }, { "Virtual Boy Controller", DEVICE_VBGAMEPAD }, { NULL, 0 }, }; static constexpr struct retro_controller_description pads3[] = { { "Auto", DEVICE_AUTO }, { "Standard Controller", DEVICE_GAMEPAD }, { NULL, 0 }, }; static constexpr struct retro_controller_description pads4[] = { { "Auto", DEVICE_AUTO }, { "Standard Controller", DEVICE_GAMEPAD }, { NULL, 0 }, }; static constexpr struct retro_controller_description pads5[] = { { "Auto", RETRO_DEVICE_JOYPAD }, { "Arkanoid", DEVICE_ARKANOID }, { "Ascii Turbo File", DEVICE_ASCIITURBOFILE }, { "Bandai Hypershot", DEVICE_BANDAIHYPERSHOT }, { "Battle Box", DEVICE_BATTLEBOX }, { "Exciting Boxing", DEVICE_EXCITINGBOXING }, { "Family Trainer", DEVICE_FAMILYTRAINER }, { "Four Player Adapter", DEVICE_FOURPLAYERADAPTER }, { "Hori Track", DEVICE_HORITRACK }, { "Konami Hypershot", DEVICE_KONAMIHYPERSHOT }, { "Pachinko", DEVICE_PACHINKO }, { "Partytap", DEVICE_PARTYTAP }, { "Oeka Kids Tablet", DEVICE_OEKAKIDS }, { NULL, 0 }, }; static constexpr struct retro_controller_info ports[] = { { pads1, 7 }, { pads2, 7 }, { pads3, 2 }, { pads4, 2 }, { pads5, 13 }, { 0 }, }; static const struct retro_system_content_info_override content_overrides[] = { { "nes|fds|unf|unif", /* extensions */ false, /* need_fullpath */ false /* persistent_data */ }, { NULL, false, false } }; retroEnv(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)vars); retroEnv(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports); retroEnv(RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE, (void*)content_overrides); } RETRO_API void retro_set_video_refresh(retro_video_refresh_t sendFrame) { _renderer->SetVideoCallback(sendFrame); } RETRO_API void retro_set_audio_sample(retro_audio_sample_t sendAudioSample) { } RETRO_API void retro_set_audio_sample_batch(retro_audio_sample_batch_t audioSampleBatch) { _soundManager->SetSendAudioSample(audioSampleBatch); } RETRO_API void retro_set_input_poll(retro_input_poll_t pollInput) { _keyManager->SetPollInput(pollInput); } RETRO_API void retro_set_input_state(retro_input_state_t getInputState) { _keyManager->SetGetInputState(getInputState); } RETRO_API void retro_reset() { _console->Reset(true); } bool readVariable(const char* key, retro_variable &var) { var.key = key; var.value = nullptr; if(retroEnv(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value != nullptr) { return true; } return false; } uint8_t readOverscanValue(const char* key) { retro_variable var = {}; if(readVariable(key, var)) { string value = string(var.value); if(value == "4px") { return 4; } else if(value == "8px") { return 8; } else if(value == "12px") { return 12; } else if(value == "16px") { return 16; } } return 0; } void set_flag(const char* flagName, uint64_t flagValue) { struct retro_variable var = {}; if(readVariable(flagName, var)) { string value = string(var.value); if(value == "disabled") { _console->GetSettings()->ClearFlags(flagValue); } else { _console->GetSettings()->SetFlags(flagValue); } } } void load_custom_palette() { //Setup default palette in case we can't load the custom one _console->GetSettings()->SetUserRgbPalette(defaultPalette); //Try to load the custom palette from the MesenPalette.pal file string palettePath = FolderUtilities::CombinePath(FolderUtilities::GetHomeFolder(), "MesenPalette.pal"); uint8_t fileData[512 * 3] = {}; ifstream palette(palettePath, ios::binary); if(palette) { palette.seekg(0, ios::end); std::streamoff fileSize = palette.tellg(); palette.seekg(0, ios::beg); if((fileSize == 64 * 3) || (fileSize == 512 * 3)) { palette.read((char*)fileData, fileSize); uint32_t customPalette[512]; for(int i = 0; i < fileSize / 3; i++) { customPalette[i] = 0xFF000000 | fileData[i * 3 + 2] | (fileData[i * 3 + 1] << 8) | (fileData[i * 3] << 16); } _console->GetSettings()->SetUserRgbPalette(customPalette, (uint32_t)fileSize / 3); } } } void update_settings() { struct retro_variable var = { }; _console->GetSettings()->SetPictureSettings(0, 0, 0, 0, 0); _hdPacksEnabled = _console->GetSettings()->CheckFlag(EmulationFlags::UseHdPacks); set_flag(MesenNoSpriteLimit, EmulationFlags::RemoveSpriteLimit | EmulationFlags::AdaptiveSpriteLimit); set_flag(MesenHdPacks, EmulationFlags::UseHdPacks); set_flag(MesenMuteTriangleUltrasonic, EmulationFlags::SilenceTriangleHighFreq); set_flag(MesenReduceDmcPopping, EmulationFlags::ReduceDmcPopping); set_flag(MesenSwapDutyCycle, EmulationFlags::SwapDutyCycles); set_flag(MesenDisableNoiseModeFlag, EmulationFlags::DisableNoiseModeFlag); set_flag(MesenFdsAutoSelectDisk, EmulationFlags::FdsAutoInsertDisk); set_flag(MesenFdsFastForwardLoad, EmulationFlags::FdsFastForwardOnLoad); if(readVariable(MesenFakeStereo, var)) { string value = string(var.value); AudioFilterSettings settings; if(value == "enabled") { settings.Filter = StereoFilter::Delay; settings.Delay = 15; _console->GetSettings()->SetAudioFilterSettings(settings); } else { _console->GetSettings()->SetAudioFilterSettings(settings); } } if(readVariable(MesenNtscFilter, var)) { string value = string(var.value); if(value == "Disabled") { _console->GetSettings()->SetVideoFilterType(VideoFilterType::None); } else if(value == "Composite (Blargg)") { _console->GetSettings()->SetVideoFilterType(VideoFilterType::NTSC); _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, false, true); } else if(value == "S-Video (Blargg)") { _console->GetSettings()->SetVideoFilterType(VideoFilterType::NTSC); _console->GetSettings()->SetNtscFilterSettings(-1.0, 0, -1.0, 0, 0.2, 0.2, false, 0, 0, 0, false, true); } else if(value == "RGB (Blargg)") { _console->GetSettings()->SetVideoFilterType(VideoFilterType::NTSC); _console->GetSettings()->SetPictureSettings(0, 0, 0, 0, 0); _console->GetSettings()->SetNtscFilterSettings(-1.0, -1.0, -1.0, 0, 0.7, 0.2, false, 0, 0, 0, false, true); } else if(value == "Monochrome (Blargg)") { _console->GetSettings()->SetVideoFilterType(VideoFilterType::NTSC); _console->GetSettings()->SetPictureSettings(0, 0, -1.0, 0, 0); _console->GetSettings()->SetNtscFilterSettings(-0.2, -0.1, -0.2, 0, 0.7, 0.2, false, 0, 0, 0, false, true); } else if(value == "Bisqwit 2x") { _console->GetSettings()->SetVideoFilterType(VideoFilterType::BisqwitNtscQuarterRes); _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, false, true); } else if(value == "Bisqwit 4x") { _console->GetSettings()->SetVideoFilterType(VideoFilterType::BisqwitNtscHalfRes); _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, false, true); } else if(value == "Bisqwit 8x") { _console->GetSettings()->SetVideoFilterType(VideoFilterType::BisqwitNtsc); _console->GetSettings()->SetNtscFilterSettings(0, 0, 0, 0, 0, 0, false, 0, 0, 0, false, true); } } if(readVariable(MesenPalette, var)) { string value = string(var.value); if(value == "Default") { _console->GetSettings()->SetUserRgbPalette(defaultPalette); } else if(value == "Composite Direct (by FirebrandX)") { _console->GetSettings()->SetUserRgbPalette(compositeDirectPalette); } else if(value == "Nes Classic") { _console->GetSettings()->SetUserRgbPalette(nesClassicPalette); } else if(value == "Nestopia (RGB)") { _console->GetSettings()->SetUserRgbPalette(nestopiaRgbPalette); } else if(value == "Original Hardware (by FirebrandX)") { _console->GetSettings()->SetUserRgbPalette(originalHardwarePalette); } else if(value == "PVM Style (by FirebrandX)") { _console->GetSettings()->SetUserRgbPalette(pvmStylePalette); } else if(value == "Sony CXA2025AS") { _console->GetSettings()->SetUserRgbPalette(sonyCxa2025AsPalette); } else if(value == "Unsaturated v6 (by FirebrandX)") { _console->GetSettings()->SetUserRgbPalette(unsaturatedPalette); } else if(value == "YUV v3 (by FirebrandX)") { _console->GetSettings()->SetUserRgbPalette(yuvPalette); } else if(value == "Wavebeam (by nakedarthur)") { _console->GetSettings()->SetUserRgbPalette(wavebeamPalette); } else if(value == "Custom") { load_custom_palette(); } else if(value == "Raw") { //Using the raw palette replaces the NTSC filters, if one is selected _console->GetSettings()->SetVideoFilterType(VideoFilterType::Raw); } } bool beforeNmi = true; if(readVariable(MesenOverclockType, var)) { string value = string(var.value); if(value == "After NMI") { beforeNmi = false; } } if(readVariable(MesenOverclock, var)) { string value = string(var.value); int lineCount = 0; if(value == "None") { lineCount = 0; } else if(value == "Low") { lineCount = 100; } else if(value == "Medium") { lineCount = 250; } else if(value == "High") { lineCount = 500; } else if(value == "Very High") { lineCount = 1000; } if(beforeNmi) { _console->GetSettings()->SetPpuNmiConfig(lineCount, 0); } else { _console->GetSettings()->SetPpuNmiConfig(0, lineCount); } } _console->GetSettings()->SetOverscanDimensions( readOverscanValue(MesenOverscanLeft), readOverscanValue(MesenOverscanRight), readOverscanValue(MesenOverscanTop), readOverscanValue(MesenOverscanBottom) ); if(readVariable(MesenAspectRatio, var)) { string value = string(var.value); if(value == "Auto") { _console->GetSettings()->SetVideoAspectRatio(VideoAspectRatio::Auto, 1.0); } else if(value == "No Stretching") { _console->GetSettings()->SetVideoAspectRatio(VideoAspectRatio::NoStretching, 1.0); } else if(value == "NTSC") { _console->GetSettings()->SetVideoAspectRatio(VideoAspectRatio::NTSC, 1.0); } else if(value == "PAL") { _console->GetSettings()->SetVideoAspectRatio(VideoAspectRatio::PAL, 1.0); } else if(value == "4:3") { _console->GetSettings()->SetVideoAspectRatio(VideoAspectRatio::Standard, 1.0); } else if(value == "16:9") { _console->GetSettings()->SetVideoAspectRatio(VideoAspectRatio::Widescreen, 1.0); } } if(readVariable(MesenRegion, var)) { string value = string(var.value); if(value == "Auto") { _console->GetSettings()->SetNesModel(NesModel::Auto); } else if(value == "NTSC") { _console->GetSettings()->SetNesModel(NesModel::NTSC); } else if(value == "PAL") { _console->GetSettings()->SetNesModel(NesModel::PAL); } else if(value == "Dendy") { _console->GetSettings()->SetNesModel(NesModel::Dendy); } } if(readVariable(MesenRamState, var)) { string value = string(var.value); if(value == "All 0s (Default)") { _console->GetSettings()->SetRamPowerOnState(RamPowerOnState::AllZeros); } else if(value == "All 1s") { _console->GetSettings()->SetRamPowerOnState(RamPowerOnState::AllOnes); } else if(value == "Random Values") { _console->GetSettings()->SetRamPowerOnState(RamPowerOnState::Random); } } if(readVariable(MesenScreenRotation, var)) { string value = string(var.value); if(value == "None") { _console->GetSettings()->SetScreenRotation(0); } else if(value == u8"90 degrees") { _console->GetSettings()->SetScreenRotation(90); } else if(value == u8"180 degrees") { _console->GetSettings()->SetScreenRotation(180); } else if(value == u8"270 degrees") { _console->GetSettings()->SetScreenRotation(270); } } int turboSpeed = 0; bool turboEnabled = true; if(readVariable(MesenControllerTurboSpeed, var)) { string value = string(var.value); if(value == "Slow") { turboSpeed = 0; } else if(value == "Normal") { turboSpeed = 1; } else if(value == "Fast") { turboSpeed = 2; } else if(value == "Very Fast") { turboSpeed = 3; } else if(value == "Disabled") { turboEnabled = false; } } _shiftButtonsClockwise = false; if(readVariable(MesenShiftButtonsClockwise, var)) { string value = string(var.value); if(value == "enabled") { _shiftButtonsClockwise = true; } } if(readVariable(MesenAudioSampleRate, var)) { int old_value = _audioSampleRate; _audioSampleRate = atoi(var.value); if(old_value != _audioSampleRate) { _console->GetSettings()->SetSampleRate(_audioSampleRate); // switch when core actively running if(_saveStateSize != -1) { struct retro_system_av_info system_av_info; retro_get_system_av_info(&system_av_info); retroEnv(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &system_av_info); } } } auto getKeyCode = [=](int port, int retroKey) { return (port << 8) | (retroKey + 1); }; auto getKeyBindings = [=](int port) { KeyMappingSet keyMappings; keyMappings.TurboSpeed = turboSpeed; if(_console->GetSettings()->GetControllerType(port) == ControllerType::SnesController) { keyMappings.Mapping1.LButton = getKeyCode(port, RETRO_DEVICE_ID_JOYPAD_L); keyMappings.Mapping1.RButton = getKeyCode(port, RETRO_DEVICE_ID_JOYPAD_R); keyMappings.Mapping1.A = getKeyCode(port, RETRO_DEVICE_ID_JOYPAD_A); keyMappings.Mapping1.B = getKeyCode(port, RETRO_DEVICE_ID_JOYPAD_B); keyMappings.Mapping1.TurboA = getKeyCode(port, RETRO_DEVICE_ID_JOYPAD_X); keyMappings.Mapping1.TurboB = getKeyCode(port, RETRO_DEVICE_ID_JOYPAD_Y); } else { keyMappings.Mapping1.A = getKeyCode(port, _shiftButtonsClockwise ? RETRO_DEVICE_ID_JOYPAD_B : RETRO_DEVICE_ID_JOYPAD_A); keyMappings.Mapping1.B = getKeyCode(port, _shiftButtonsClockwise ? RETRO_DEVICE_ID_JOYPAD_Y : RETRO_DEVICE_ID_JOYPAD_B); if(turboEnabled) { keyMappings.Mapping1.TurboA = getKeyCode(port, _shiftButtonsClockwise ? RETRO_DEVICE_ID_JOYPAD_A : RETRO_DEVICE_ID_JOYPAD_X); keyMappings.Mapping1.TurboB = getKeyCode(port, _shiftButtonsClockwise ? RETRO_DEVICE_ID_JOYPAD_X : RETRO_DEVICE_ID_JOYPAD_Y); } } keyMappings.Mapping1.Start = getKeyCode(port, RETRO_DEVICE_ID_JOYPAD_START); keyMappings.Mapping1.Select = getKeyCode(port, RETRO_DEVICE_ID_JOYPAD_SELECT); keyMappings.Mapping1.Up = getKeyCode(port, RETRO_DEVICE_ID_JOYPAD_UP); keyMappings.Mapping1.Down = getKeyCode(port, RETRO_DEVICE_ID_JOYPAD_DOWN); keyMappings.Mapping1.Left = getKeyCode(port, RETRO_DEVICE_ID_JOYPAD_LEFT); keyMappings.Mapping1.Right = getKeyCode(port, RETRO_DEVICE_ID_JOYPAD_RIGHT); if(port == 0) { keyMappings.Mapping1.PartyTapButtons[0] = getKeyCode(4, RETRO_DEVICE_ID_JOYPAD_B); keyMappings.Mapping1.PartyTapButtons[1] = getKeyCode(4, RETRO_DEVICE_ID_JOYPAD_A); keyMappings.Mapping1.PartyTapButtons[2] = getKeyCode(4, RETRO_DEVICE_ID_JOYPAD_Y); keyMappings.Mapping1.PartyTapButtons[3] = getKeyCode(4, RETRO_DEVICE_ID_JOYPAD_X); keyMappings.Mapping1.PartyTapButtons[4] = getKeyCode(4, RETRO_DEVICE_ID_JOYPAD_L); keyMappings.Mapping1.PartyTapButtons[5] = getKeyCode(4, RETRO_DEVICE_ID_JOYPAD_R); unsigned powerPadPort = 0; if(_console->GetSettings()->GetExpansionDevice() == ExpansionPortDevice::FamilyTrainerMat) { powerPadPort = 4; } keyMappings.Mapping1.PowerPadButtons[0] = getKeyCode(powerPadPort, RETRO_DEVICE_ID_JOYPAD_B); keyMappings.Mapping1.PowerPadButtons[1] = getKeyCode(powerPadPort, RETRO_DEVICE_ID_JOYPAD_A); keyMappings.Mapping1.PowerPadButtons[2] = getKeyCode(powerPadPort, RETRO_DEVICE_ID_JOYPAD_Y); keyMappings.Mapping1.PowerPadButtons[3] = getKeyCode(powerPadPort, RETRO_DEVICE_ID_JOYPAD_X); keyMappings.Mapping1.PowerPadButtons[4] = getKeyCode(powerPadPort, RETRO_DEVICE_ID_JOYPAD_L); keyMappings.Mapping1.PowerPadButtons[5] = getKeyCode(powerPadPort, RETRO_DEVICE_ID_JOYPAD_R); keyMappings.Mapping1.PowerPadButtons[6] = getKeyCode(powerPadPort, RETRO_DEVICE_ID_JOYPAD_LEFT); keyMappings.Mapping1.PowerPadButtons[7] = getKeyCode(powerPadPort, RETRO_DEVICE_ID_JOYPAD_RIGHT); keyMappings.Mapping1.PowerPadButtons[8] = getKeyCode(powerPadPort, RETRO_DEVICE_ID_JOYPAD_UP); keyMappings.Mapping1.PowerPadButtons[9] = getKeyCode(powerPadPort, RETRO_DEVICE_ID_JOYPAD_DOWN); keyMappings.Mapping1.PowerPadButtons[10] = getKeyCode(powerPadPort, RETRO_DEVICE_ID_JOYPAD_SELECT); keyMappings.Mapping1.PowerPadButtons[11] = getKeyCode(powerPadPort, RETRO_DEVICE_ID_JOYPAD_START); keyMappings.Mapping1.PachinkoButtons[0] = getKeyCode(4, RETRO_DEVICE_ID_JOYPAD_R); keyMappings.Mapping1.PachinkoButtons[1] = getKeyCode(4, RETRO_DEVICE_ID_JOYPAD_L); keyMappings.Mapping1.ExcitingBoxingButtons[0] = getKeyCode(4, RETRO_DEVICE_ID_JOYPAD_B); //left hook keyMappings.Mapping1.ExcitingBoxingButtons[1] = getKeyCode(4, RETRO_DEVICE_ID_JOYPAD_RIGHT); //move right keyMappings.Mapping1.ExcitingBoxingButtons[2] = getKeyCode(4, RETRO_DEVICE_ID_JOYPAD_LEFT); //move left keyMappings.Mapping1.ExcitingBoxingButtons[3] = getKeyCode(4, RETRO_DEVICE_ID_JOYPAD_A); //right hook keyMappings.Mapping1.ExcitingBoxingButtons[4] = getKeyCode(4, RETRO_DEVICE_ID_JOYPAD_Y); //left jab keyMappings.Mapping1.ExcitingBoxingButtons[5] = getKeyCode(4, RETRO_DEVICE_ID_JOYPAD_L); //body keyMappings.Mapping1.ExcitingBoxingButtons[6] = getKeyCode(4, RETRO_DEVICE_ID_JOYPAD_X); //right jab keyMappings.Mapping1.ExcitingBoxingButtons[7] = getKeyCode(4, RETRO_DEVICE_ID_JOYPAD_R); //straight } else if(port == 1) { keyMappings.Mapping1.Microphone = getKeyCode(0, RETRO_DEVICE_ID_JOYPAD_L3); keyMappings.Mapping1.PowerPadButtons[0] = getKeyCode(1, RETRO_DEVICE_ID_JOYPAD_B); keyMappings.Mapping1.PowerPadButtons[1] = getKeyCode(1, RETRO_DEVICE_ID_JOYPAD_A); keyMappings.Mapping1.PowerPadButtons[2] = getKeyCode(1, RETRO_DEVICE_ID_JOYPAD_Y); keyMappings.Mapping1.PowerPadButtons[3] = getKeyCode(1, RETRO_DEVICE_ID_JOYPAD_X); keyMappings.Mapping1.PowerPadButtons[4] = getKeyCode(1, RETRO_DEVICE_ID_JOYPAD_L); keyMappings.Mapping1.PowerPadButtons[5] = getKeyCode(1, RETRO_DEVICE_ID_JOYPAD_R); keyMappings.Mapping1.PowerPadButtons[6] = getKeyCode(1, RETRO_DEVICE_ID_JOYPAD_LEFT); keyMappings.Mapping1.PowerPadButtons[7] = getKeyCode(1, RETRO_DEVICE_ID_JOYPAD_RIGHT); keyMappings.Mapping1.PowerPadButtons[8] = getKeyCode(1, RETRO_DEVICE_ID_JOYPAD_UP); keyMappings.Mapping1.PowerPadButtons[9] = getKeyCode(1, RETRO_DEVICE_ID_JOYPAD_DOWN); keyMappings.Mapping1.PowerPadButtons[10] = getKeyCode(1, RETRO_DEVICE_ID_JOYPAD_SELECT); keyMappings.Mapping1.PowerPadButtons[11] = getKeyCode(1, RETRO_DEVICE_ID_JOYPAD_START); } return keyMappings; }; _console->GetSettings()->SetControllerKeys(0, getKeyBindings(0)); _console->GetSettings()->SetControllerKeys(1, getKeyBindings(1)); _console->GetSettings()->SetControllerKeys(2, getKeyBindings(2)); _console->GetSettings()->SetControllerKeys(3, getKeyBindings(3)); retro_system_av_info avInfo = {}; _renderer->GetSystemAudioVideoInfo(avInfo); retroEnv(RETRO_ENVIRONMENT_SET_GEOMETRY, &avInfo); } RETRO_API void retro_run() { if(_console->GetSettings()->CheckFlag(EmulationFlags::ForceMaxSpeed)) { //Skip frames to speed up emulation while still outputting at 50/60 fps (needed for FDS fast forward while loading) _renderer->SetSkipMode(true); _soundManager->SetSkipMode(true); for(int i = 0; i < 9; i++) { //Attempt to speed up to 1000% speed _console->RunSingleFrame(); } _renderer->SetSkipMode(false); _soundManager->SetSkipMode(false); } bool updated = false; if(retroEnv(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated) { update_settings(); bool hdPacksEnabled = _console->GetSettings()->CheckFlag(EmulationFlags::UseHdPacks); if(hdPacksEnabled != _hdPacksEnabled) { //Try to load/unload HD pack when the flag is toggled _console->UpdateHdPackMode(); _hdPacksEnabled = hdPacksEnabled; } } _console->RunSingleFrame(); if(updated) { //Update geometry after running the frame, in case the console's region changed (affects "auto" aspect ratio) retro_system_av_info avInfo = {}; _renderer->GetSystemAudioVideoInfo(avInfo); retroEnv(RETRO_ENVIRONMENT_SET_GEOMETRY, &avInfo); } } RETRO_API size_t retro_serialize_size() { return _saveStateSize; } RETRO_API bool retro_serialize(void *data, size_t size) { std::stringstream ss; _console->GetSaveStateManager()->SaveState(ss); string saveStateData = ss.str(); memset(data, 0, size); memcpy(data, saveStateData.c_str(), std::min(size, saveStateData.size())); return true; } RETRO_API bool retro_unserialize(const void *data, size_t size) { std::stringstream ss; ss.write((char*)data, size); bool result = _console->GetSaveStateManager()->LoadState(ss, false); if(result) _console->GetSettings()->SetSampleRate(_audioSampleRate); return result; } RETRO_API void retro_cheat_reset() { _console->GetCheatManager()->ClearCodes(); } RETRO_API void retro_cheat_set(unsigned index, bool enabled, const char *codeStr) { static const string validGgLetters = "APZLGITYEOXUKSVN"; static const string validParLetters = "0123456789ABCDEF"; string code = codeStr; std::transform(code.begin(), code.end(), code.begin(), ::toupper); if(code.size() == 7 && code[4] == ':') { string address = code.substr(0, 4); string value = code.substr(5, 2); _console->GetCheatManager()->AddCustomCode(HexUtilities::FromHex(address), HexUtilities::FromHex(value)); } else if(code.size() == 10 && code[4] == '?' && code[7] == ':') { string address = code.substr(0, 4); string comparison = code.substr(5, 2); string value = code.substr(8, 2); _console->GetCheatManager()->AddCustomCode(HexUtilities::FromHex(address), HexUtilities::FromHex(value), HexUtilities::FromHex(comparison)); } else if(code.size() == 6 || code.size() == 8) { //This is either a GG or PAR code bool isValidGgCode = true; bool isValidParCode = code.size() == 8; for(size_t i = 0; i < code.size(); i++) { if(validGgLetters.find(code[i]) == string::npos) { isValidGgCode = false; } if(validParLetters.find(code[i]) == string::npos) { isValidParCode = false; } } if(isValidGgCode) { _console->GetCheatManager()->AddGameGenieCode(code); } else if(isValidParCode) { _console->GetCheatManager()->AddProActionRockyCode(HexUtilities::FromHex(code)); } } } void update_input_descriptors() { vector desc; auto addDesc = [&desc](unsigned port, unsigned button, const char* name) { retro_input_descriptor d = { port, RETRO_DEVICE_JOYPAD, 0, button, name }; desc.push_back(d); }; auto setupPlayerButtons = [addDesc](int port) { unsigned device = _inputDevices[port]; if(device == DEVICE_AUTO) { if(port <= 3) { switch(_console->GetSettings()->GetControllerType(port)) { case ControllerType::StandardController: device = DEVICE_GAMEPAD; break; case ControllerType::PowerPad: device = DEVICE_POWERPAD; break; case ControllerType::SnesController: device = DEVICE_SNESGAMEPAD; break; case ControllerType::SnesMouse: device = DEVICE_SNESMOUSE; break; case ControllerType::Zapper: device = DEVICE_ZAPPER; break; case ControllerType::ArkanoidController: device = DEVICE_ARKANOID; break; case ControllerType::VbController: device = DEVICE_VBGAMEPAD; break; default: return; } } else if(port == 4) { switch(_console->GetSettings()->GetExpansionDevice()) { case ExpansionPortDevice::ArkanoidController: device = DEVICE_ARKANOID; break; case ExpansionPortDevice::BandaiHyperShot: device = DEVICE_BANDAIHYPERSHOT; break; case ExpansionPortDevice::ExcitingBoxing: device = DEVICE_EXCITINGBOXING; break; case ExpansionPortDevice::FamilyTrainerMat: device = DEVICE_FAMILYTRAINER; break; case ExpansionPortDevice::HoriTrack: device = DEVICE_HORITRACK; break; case ExpansionPortDevice::KonamiHyperShot: device = DEVICE_KONAMIHYPERSHOT; break; case ExpansionPortDevice::OekaKidsTablet: device = DEVICE_OEKAKIDS; break; case ExpansionPortDevice::Pachinko: device = DEVICE_PACHINKO; break; case ExpansionPortDevice::PartyTap: device = DEVICE_PARTYTAP; break; case ExpansionPortDevice::Zapper: device = DEVICE_ZAPPER; break; case ExpansionPortDevice::BattleBox: device = DEVICE_BATTLEBOX; break; case ExpansionPortDevice::AsciiTurboFile: device = DEVICE_ASCIITURBOFILE; break; case ExpansionPortDevice::FourPlayerAdapter: device = DEVICE_FOURPLAYERADAPTER; break; default: return; } } } if(device == DEVICE_GAMEPAD || device == DEVICE_SNESGAMEPAD) { addDesc(port, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right"); if(device == DEVICE_SNESGAMEPAD) { addDesc(port, RETRO_DEVICE_ID_JOYPAD_A, "A"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_B, "B"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_X, "X"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_Y, "Y"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_L, "L"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_R, "R"); } else { if(_shiftButtonsClockwise) { addDesc(port, RETRO_DEVICE_ID_JOYPAD_B, "A"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_Y, "B"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_A, "Turbo A"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_X, "Turbo B"); } else { addDesc(port, RETRO_DEVICE_ID_JOYPAD_A, "A"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_B, "B"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_X, "Turbo A"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_Y, "Turbo B"); } if(port == 0) { addDesc(port, RETRO_DEVICE_ID_JOYPAD_L, "(FDS) Insert Next Disk"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_R, "(FDS) Switch Disk Side"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_L2, "(VS) Insert Coin 1"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_R2, "(VS) Insert Coin 2"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_L3, "(Famicom) Microphone (P2)"); } } addDesc(port, RETRO_DEVICE_ID_JOYPAD_START, "Start"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select"); } else if(device == DEVICE_EXCITINGBOXING) { addDesc(port, RETRO_DEVICE_ID_JOYPAD_B, "Left Hook"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_A, "Right Hook"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_Y, "Left Jab"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_X, "Right Jab"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_L, "Body"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_R, "Straight"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_LEFT, "Move Left"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Move Right"); } else if(device == DEVICE_PARTYTAP) { addDesc(port, RETRO_DEVICE_ID_JOYPAD_B, "Partytap P1"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_A, "Partytap P2"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_Y, "Partytap P3"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_X, "Partytap P4"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_L, "Partytap P5"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_R, "Partytap P6"); } else if(device == DEVICE_FAMILYTRAINER || device == DEVICE_POWERPAD) { addDesc(port, RETRO_DEVICE_ID_JOYPAD_B, "Powerpad B1"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_A, "Powerpad B2"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_Y, "Powerpad B3"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_X, "Powerpad B4"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_L, "Powerpad B5"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_R, "Powerpad B6"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_LEFT, "Powerpad B7"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Powerpad B8"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_UP, "Powerpad B9"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_DOWN, "Powerpad B10"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_SELECT, "Powerpad B11"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_START, "Powerpad B12"); } else if(device == DEVICE_PACHINKO) { addDesc(port, RETRO_DEVICE_ID_JOYPAD_L, "Release Trigger"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_R, "Press Trigger"); } else if(device == DEVICE_VBGAMEPAD) { addDesc(port, RETRO_DEVICE_ID_JOYPAD_B, "Virtual Boy D-Pad 2 Down"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_Y, "Virtual Boy D-Pad 2 Left"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_SELECT, "Virtual Boy Select"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_START, "Virtual Boy Start"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_UP, "Virtual Boy D-Pad 1 Up"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_DOWN, "Virtual Boy D-Pad 1 Down"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_LEFT, "Virtual Boy D-Pad 1 Left"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Virtual Boy D-Pad 1 Right"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_A, "Virtual Boy D-Pad 2 Right"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_X, "Virtual Boy D-Pad 2 Up"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_L, "Virtual Boy L"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_R, "Virtual Boy R"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_L2, "Virtual Boy B"); addDesc(port, RETRO_DEVICE_ID_JOYPAD_R2, "Virtual Boy A"); } }; setupPlayerButtons(0); setupPlayerButtons(1); setupPlayerButtons(2); setupPlayerButtons(3); setupPlayerButtons(4); retro_input_descriptor end = { 0 }; desc.push_back(end); retroEnv(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc.data()); } void update_core_controllers() { //Setup all "auto" ports RomInfo romInfo = _console->GetRomInfo(); if(romInfo.IsInDatabase || romInfo.IsNes20Header) { _console->GetSettings()->InitializeInputDevices(romInfo.InputType, romInfo.System, true); } else { _console->GetSettings()->InitializeInputDevices(GameInputType::StandardControllers, GameSystem::NesNtsc, true); } for(int port = 0; port < 5; port++) { if(_inputDevices[port] != DEVICE_AUTO) { if(port <= 3) { ControllerType type = ControllerType::StandardController; switch(_inputDevices[port]) { case RETRO_DEVICE_NONE: type = ControllerType::None; break; case DEVICE_GAMEPAD: type = ControllerType::StandardController; break; case DEVICE_ZAPPER: type = ControllerType::Zapper; break; case DEVICE_POWERPAD: type = ControllerType::PowerPad; break; case DEVICE_ARKANOID: type = ControllerType::ArkanoidController; break; case DEVICE_SNESGAMEPAD: type = ControllerType::SnesController; break; case DEVICE_SNESMOUSE: type = ControllerType::SnesMouse; break; case DEVICE_VBGAMEPAD: type = ControllerType::VbController; break; } _console->GetSettings()->SetControllerType(port, type); } else { ExpansionPortDevice type = ExpansionPortDevice::None; switch(_inputDevices[port]) { case RETRO_DEVICE_NONE: type = ExpansionPortDevice::None; break; case DEVICE_FAMILYTRAINER: type = ExpansionPortDevice::FamilyTrainerMat; break; case DEVICE_PARTYTAP: type = ExpansionPortDevice::PartyTap; break; case DEVICE_PACHINKO: type = ExpansionPortDevice::Pachinko; break; case DEVICE_EXCITINGBOXING: type = ExpansionPortDevice::ExcitingBoxing; break; case DEVICE_KONAMIHYPERSHOT: type = ExpansionPortDevice::KonamiHyperShot; break; case DEVICE_OEKAKIDS: type = ExpansionPortDevice::OekaKidsTablet; break; case DEVICE_BANDAIHYPERSHOT: type = ExpansionPortDevice::BandaiHyperShot; break; case DEVICE_ARKANOID: type = ExpansionPortDevice::ArkanoidController; break; case DEVICE_HORITRACK: type = ExpansionPortDevice::HoriTrack; break; case DEVICE_ASCIITURBOFILE: type = ExpansionPortDevice::AsciiTurboFile; break; case DEVICE_BATTLEBOX: type = ExpansionPortDevice::BattleBox; break; case DEVICE_FOURPLAYERADAPTER: type = ExpansionPortDevice::FourPlayerAdapter; break; } _console->GetSettings()->SetExpansionDevice(type); } } } bool hasFourScore = false; bool isFamicom = (_console->GetSettings()->GetExpansionDevice() != ExpansionPortDevice::None || romInfo.System == GameSystem::Famicom || romInfo.System == GameSystem::FDS || romInfo.System == GameSystem::Dendy); if(isFamicom) { _console->GetSettings()->SetConsoleType(ConsoleType::Famicom); if(_console->GetSettings()->GetExpansionDevice() == ExpansionPortDevice::FourPlayerAdapter) { hasFourScore = true; } } else { _console->GetSettings()->SetConsoleType(ConsoleType::Nes); if(_console->GetSettings()->GetControllerType(2) != ControllerType::None || _console->GetSettings()->GetControllerType(3) != ControllerType::None) { hasFourScore = true; } } _console->GetSettings()->SetFlagState(EmulationFlags::HasFourScore, hasFourScore); } void retro_set_memory_maps() { //Expose internal RAM and work/save RAM for retroachievements retro_memory_descriptor descriptors[256] = {}; retro_memory_map memoryMap = {}; int count = 0; for(int i = 0; i <= 0xFFFF; i += 0x100) { uint8_t* ram = _console->GetRamBuffer(i); if(ram) { descriptors[count].ptr = ram; descriptors[count].start = i; descriptors[count].len = 0x100; count++; } } memoryMap.descriptors = descriptors; memoryMap.num_descriptors = count; retroEnv(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &memoryMap); } RETRO_API void retro_set_controller_port_device(unsigned port, unsigned device) { if(port < 5 && _inputDevices[port] != device) { _inputDevices[port] = device; update_core_controllers(); update_input_descriptors(); } } RETRO_API bool retro_load_game(const struct retro_game_info *game) { char *systemFolder; if(!retroEnv(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &systemFolder) || !systemFolder) { return false; } char *saveFolder; if(!retroEnv(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &saveFolder)) { logMessage(RETRO_LOG_ERROR, "Could not find save directory.\n"); } enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888; if(!retroEnv(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) { logMessage(RETRO_LOG_ERROR, "XRGB8888 is not supported.\n"); return false; } //Expect the following structure: // /system/disksys.rom // /system/HdPacks/* // /saves/*.sav FolderUtilities::SetHomeFolder(systemFolder); FolderUtilities::SetFolderOverrides(saveFolder, "", ""); update_settings(); //Plug in 2 standard controllers by default, game database will switch the controller types for recognized games _console->GetSettings()->SetMasterVolume(10.0); _console->GetSettings()->SetControllerType(0, ControllerType::StandardController); _console->GetSettings()->SetControllerType(1, ControllerType::StandardController); _console->GetSettings()->SetControllerType(2, ControllerType::None); _console->GetSettings()->SetControllerType(3, ControllerType::None); // Attempt to fetch extended game info const struct retro_game_info_ext *gameExt = NULL; const void *gameData = NULL; size_t gameSize = 0; string gamePath(""); if (retroEnv(RETRO_ENVIRONMENT_GET_GAME_INFO_EXT, &gameExt)) { gameData = gameExt->data; gameSize = gameExt->size; if (gameExt->file_in_archive) { // We don't have a 'physical' file in this // case, but the core still needs a filename // in order to detect associated content // (i.e. HdPacks). We therefore fake it, using // the content directory, canonical content // name, and content file extension #if defined(_WIN32) char slash = '\\'; #else char slash = '/'; #endif gamePath = string(gameExt->dir) + string(1, slash) + string(gameExt->name) + "." + string(gameExt->ext); } else { gamePath = gameExt->full_path; } } else { // No extended game info; all we have is the // content fullpath from the retro_game_info // struct gamePath = game->path; } // Load content VirtualFile romData(gameData, gameSize, gamePath); bool result = _console->Initialize(romData); if(result) { //Set default dipswitches for some VS System games switch(_console->GetRomInfo().Hash.PrgCrc32) { case 0x8850924B: _console->GetSettings()->SetDipSwitches(32); break; //VS Tetris case 0xE1AA8214: _console->GetSettings()->SetDipSwitches(32); break; //StarLuster default: _console->GetSettings()->SetDipSwitches(0); break; } update_core_controllers(); update_input_descriptors(); //Savestates in Mesen may change size over time //Retroarch doesn't like this for netplay or rewinding - it requires the states to always be the exact same size //So we need to send a large enough size to Retroarch to ensure Mesen's state will always fit within that buffer. std::stringstream ss; _console->GetSaveStateManager()->SaveState(ss); //Round up to the next 1kb multiple _saveStateSize = ((ss.str().size() * 2) + 0x400) & ~0x3FF; retro_set_memory_maps(); } return result; } 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() { _console->Stop(); } RETRO_API unsigned retro_get_region() { NesModel model = _console->GetModel(); return model == NesModel::NTSC ? RETRO_REGION_NTSC : RETRO_REGION_PAL; } RETRO_API void retro_get_system_info(struct retro_system_info *info) { _mesenVersion = EmulationSettings::GetMesenVersionString(); info->library_name = "Mesen"; info->library_version = _mesenVersion.c_str(); // need_fullpath is required since HdPacks are // identified via the rom file name info->need_fullpath = true; info->valid_extensions = "nes|fds|unf|unif"; info->block_extract = false; } RETRO_API void retro_get_system_av_info(struct retro_system_av_info *info) { uint32_t hscale = 1; uint32_t vscale = 1; switch(_console->GetSettings()->GetVideoFilterType()) { case VideoFilterType::NTSC: hscale = 2; break; case VideoFilterType::BisqwitNtscQuarterRes: hscale = 2; break; case VideoFilterType::BisqwitNtscHalfRes: hscale = 4; break; case VideoFilterType::BisqwitNtsc: hscale = 8; break; default: hscale = 1; break; } shared_ptr hdData = _console->GetHdData(); if(hdData) { hscale = hdData->Scale; vscale = hdData->Scale; } if(hscale <= 2) { _renderer->GetSystemAudioVideoInfo(*info, NES_NTSC_OUT_WIDTH(256), 240 * vscale); } else { _renderer->GetSystemAudioVideoInfo(*info, 256 * hscale, 240 * vscale); } } RETRO_API void *retro_get_memory_data(unsigned id) { BaseMapper* mapper = _console->GetMapper(); switch(id) { case RETRO_MEMORY_SAVE_RAM: return mapper->GetSaveRam(); case RETRO_MEMORY_SYSTEM_RAM: return _console->GetMemoryManager()->GetInternalRAM(); } return nullptr; } RETRO_API size_t retro_get_memory_size(unsigned id) { BaseMapper* mapper = _console->GetMapper(); switch(id) { case RETRO_MEMORY_SAVE_RAM: return mapper->GetMemorySize(DebugMemoryType::SaveRam); case RETRO_MEMORY_SYSTEM_RAM: return MemoryManager::InternalRAMSize; } return 0; } }