Play-/Source/ui_libretro/main_libretro.cpp

538 lines
14 KiB
C++
Raw Normal View History

2019-12-31 21:02:35 +01:00
#include "libretro.h"
#include "Log.h"
#include "AppConfig.h"
#include "PS2VM.h"
#include "PS2VM_Preferences.h"
#include "GSH_OpenGL_Libretro.h"
#include "SH_LibreAudio.h"
#include "PH_Libretro_Input.h"
2019-06-28 21:58:58 +01:00
#include "PathUtils.h"
#include "PtrStream.h"
#include "MemStream.h"
2019-12-31 21:02:35 +01:00
#include "filesystem_def.h"
#include <vector>
#include <cstdlib>
#define LOG_NAME "LIBRETRO"
static CPS2VM* m_virtualMachine = nullptr;
static bool first_run = false;
retro_video_refresh_t g_video_cb;
retro_environment_t g_environ_cb;
retro_input_poll_t g_input_poll_cb;
retro_input_state_t g_input_state_cb;
2019-06-27 18:39:42 +01:00
retro_audio_sample_batch_t g_set_audio_sample_batch_cb;
2019-12-31 21:02:35 +01:00
std::map<int, int> g_ds2_to_retro_btn_map;
2019-06-25 19:04:04 +01:00
struct retro_hw_render_callback g_hw_render
{
};
2019-12-31 21:02:35 +01:00
int g_res_factor = 1;
CGSHandler::PRESENTATION_MODE g_presentation_mode = CGSHandler::PRESENTATION_MODE::PRESENTATION_MODE_FIT;
bool g_forceBilinearTextures = false;
bool g_audioEnabled = true;
static std::vector<struct retro_variable> m_vars =
2019-06-25 19:04:04 +01:00
{
{"play_res_multi", "Resolution Multiplier; 1x|2x|4x|8x"},
{"play_presentation_mode", "Presentation Mode; Fit Screen|Fill Screen|Original Size"},
{"play_bilinear_filtering", "Force Bilinear Filtering; false|true"},
{"audio_enable", "Enable Audio; true|false"},
{NULL, NULL},
2019-12-31 21:02:35 +01:00
};
unsigned retro_api_version()
{
return RETRO_API_VERSION;
}
void SetupVideoHandler()
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
auto gsHandler = m_virtualMachine->GetGSHandler();
if(!gsHandler)
{
m_virtualMachine->CreateGSHandler(CGSH_OpenGL_Libretro::GetFactoryFunction());
}
else
{
auto retro_gs = static_cast<CGSH_OpenGL_Libretro*>(gsHandler);
retro_gs->Reset();
}
}
static void retro_context_destroy()
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
}
static void retro_context_reset()
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
if(m_virtualMachine)
{
SetupVideoHandler();
}
}
void SetupSoundHandler()
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
if(m_virtualMachine)
{
m_virtualMachine->CreateSoundHandler(&CSH_LibreAudio::HandlerFactory);
}
}
void SetupInputHandler()
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
if(!m_virtualMachine->GetPadHandler())
{
static struct retro_input_descriptor descDS2[] =
2019-06-25 19:04:04 +01:00
{
{0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Stick X"},
{0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Stick Y"},
{0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Stick X"},
{0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Stick Y"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right"},
{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_Y, "Square"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Tringle"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Circle"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Cross"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L1"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, "L2"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3, "L3"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R1"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, "R2"},
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3, "R3"},
{0},
};
2019-12-31 21:02:35 +01:00
g_environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, descDS2);
static const struct retro_controller_description controllers[] = {
2019-06-25 19:04:04 +01:00
{"PS2 DualShock2", RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 0)},
2019-12-31 21:02:35 +01:00
};
static const struct retro_controller_info ports[] = {
2019-06-25 19:04:04 +01:00
{controllers, 1},
{NULL, 0},
2019-12-31 21:02:35 +01:00
};
g_environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
for(unsigned int i = 0; i < PS2::CControllerInfo::MAX_BUTTONS; i++)
{
auto ds2_button = static_cast<PS2::CControllerInfo::BUTTON>(i);
auto retro_button = descDS2[i].id;
g_ds2_to_retro_btn_map[ds2_button] = retro_button;
}
m_virtualMachine->CreatePadHandler(CPH_Libretro_Input::GetFactoryFunction());
}
}
2019-06-25 19:04:04 +01:00
void retro_get_system_info(struct retro_system_info* info)
2019-12-31 21:02:35 +01:00
{
*info = {};
info->library_name = "Play!";
info->library_version = PLAY_VERSION;
info->need_fullpath = true;
2019-06-26 16:45:51 +01:00
info->valid_extensions = "elf|iso|cso|isz|bin";
2019-12-31 21:02:35 +01:00
}
void retro_get_system_av_info(struct retro_system_av_info* info)
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
*info = {};
info->timing.fps = 60.0;
2019-06-27 18:39:42 +01:00
info->timing.sample_rate = 44100;
info->geometry.base_width = 640;
info->geometry.base_height = 448;
info->geometry.max_width = 640 * 8;
info->geometry.max_height = 448 * 8;
2019-12-31 21:02:35 +01:00
info->geometry.aspect_ratio = 4.0 / 3.0;
}
void retro_set_video_refresh(retro_video_refresh_t cb)
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
g_video_cb = cb;
}
void retro_set_environment(retro_environment_t cb)
{
g_environ_cb = cb;
}
void retro_set_input_poll(retro_input_poll_t cb)
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
g_input_poll_cb = cb;
}
void retro_set_input_state(retro_input_state_t cb)
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
g_input_state_cb = cb;
}
void retro_set_controller_port_device(unsigned port, unsigned device)
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
}
2019-06-27 18:39:42 +01:00
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)
2019-12-31 21:02:35 +01:00
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
2019-06-27 18:39:42 +01:00
g_set_audio_sample_batch_cb = cb;
2019-12-31 21:02:35 +01:00
}
void retro_set_audio_sample(retro_audio_sample_t)
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
}
unsigned retro_get_region(void)
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
return RETRO_REGION_NTSC;
}
size_t retro_serialize_size(void)
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
2019-06-28 21:58:58 +01:00
return 40 * 1024 * 1024;
2019-12-31 21:02:35 +01:00
}
2019-06-25 19:04:04 +01:00
bool retro_serialize(void* data, size_t size)
2019-12-31 21:02:35 +01:00
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
2019-06-28 21:58:58 +01:00
try
{
Framework::CMemStream stateStream;
Framework::CZipArchiveWriter archive;
m_virtualMachine->m_ee->SaveState(archive);
m_virtualMachine->m_iop->SaveState(archive);
m_virtualMachine->m_ee->m_gs->SaveState(archive);
archive.Write(stateStream);
stateStream.Seek(0, Framework::STREAM_SEEK_DIRECTION::STREAM_SEEK_SET);
stateStream.Read(data, size);
}
catch(...)
{
return false;
}
return true;
2019-12-31 21:02:35 +01:00
}
2019-06-25 19:04:04 +01:00
bool retro_unserialize(const void* data, size_t size)
2019-12-31 21:02:35 +01:00
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
2019-06-28 21:58:58 +01:00
try
{
Framework::CPtrStream stateStream(data, size);
Framework::CZipArchiveReader archive(stateStream);
try
{
m_virtualMachine->m_ee->LoadState(archive);
m_virtualMachine->m_iop->LoadState(archive);
m_virtualMachine->m_ee->m_gs->LoadState(archive);
}
catch(...)
{
//Any error that occurs in the previous block is critical
throw;
}
}
catch(...)
{
return false;
}
m_virtualMachine->OnMachineStateChange();
return true;
2019-12-31 21:02:35 +01:00
}
2019-06-25 19:04:04 +01:00
void* retro_get_memory_data(unsigned id)
2019-12-31 21:02:35 +01:00
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
2019-06-28 21:35:52 +01:00
if(id == RETRO_MEMORY_SYSTEM_RAM)
{
return m_virtualMachine->m_ee->m_ram;
}
2019-12-31 21:02:35 +01:00
return NULL;
}
size_t retro_get_memory_size(unsigned id)
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
2019-06-28 21:35:52 +01:00
if(id == RETRO_MEMORY_SYSTEM_RAM)
{
return PS2::EE_RAM_SIZE;
}
2019-12-31 21:02:35 +01:00
return 0;
}
void retro_cheat_reset(void)
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
}
2019-06-25 19:04:04 +01:00
void retro_cheat_set(unsigned index, bool enabled, const char* code)
2019-12-31 21:02:35 +01:00
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
(void)index;
(void)enabled;
(void)code;
}
void updateVars()
{
for(int i = 0; i < m_vars.size() - 1; ++i)
{
auto item = m_vars[i];
if(!item.key)
continue;
struct retro_variable var = {nullptr};
var.key = item.key;
if(g_environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
{
bool videoUpdate = false;
switch(i)
{
2019-06-25 19:04:04 +01:00
case 0:
{
std::string val = var.value;
auto res_factor = std::atoi(val.substr(0, -1).c_str());
if(res_factor != g_res_factor)
{
g_res_factor = res_factor;
CAppConfig::GetInstance().SetPreferenceInteger(PREF_CGSH_OPENGL_RESOLUTION_FACTOR, res_factor);
videoUpdate = true;
}
}
break;
case 1:
{
CGSHandler::PRESENTATION_MODE presentation_mode = CGSHandler::PRESENTATION_MODE::PRESENTATION_MODE_FIT;
std::string val(var.value);
if(val == "Fill Screen")
presentation_mode = CGSHandler::PRESENTATION_MODE::PRESENTATION_MODE_FILL;
else if(val == "Original Size")
presentation_mode = CGSHandler::PRESENTATION_MODE::PRESENTATION_MODE_ORIGINAL;
if(presentation_mode != g_presentation_mode)
{
g_presentation_mode = presentation_mode;
CAppConfig::GetInstance().SetPreferenceInteger(PREF_CGSHANDLER_PRESENTATION_MODE, presentation_mode);
videoUpdate = true;
}
}
break;
case 2:
{
bool forceBilinearTextures = (std::string(var.value) == "true");
if(forceBilinearTextures != g_forceBilinearTextures)
{
g_forceBilinearTextures = forceBilinearTextures;
CAppConfig::GetInstance().SetPreferenceBoolean(PREF_CGSH_OPENGL_FORCEBILINEARTEXTURES, forceBilinearTextures);
videoUpdate = true;
}
}
break;
case 3:
bool audioEnabled = (std::string(var.value) == "true");
if(audioEnabled != g_audioEnabled)
{
g_audioEnabled = audioEnabled;
}
break;
2019-12-31 21:02:35 +01:00
}
if(videoUpdate)
{
if(m_virtualMachine)
if(m_virtualMachine->GetGSHandler())
static_cast<CGSH_OpenGL_Libretro*>(m_virtualMachine->GetGSHandler())->UpdatePresentation();
}
}
}
}
void checkVarsUpdates()
{
static bool updates = true;
if(!updates)
g_environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updates);
if(updates)
{
updateVars();
}
updates = false;
}
void retro_run()
{
// CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
checkVarsUpdates();
2019-12-31 21:02:35 +01:00
if(!first_run)
{
if(m_virtualMachine)
{
// m_virtualMachine->Pause();
m_virtualMachine->Reset();
m_virtualMachine->m_ee->m_os->BootFromCDROM();
m_virtualMachine->Resume();
first_run = true;
CLog::GetInstance().Print(LOG_NAME, "%s\n", "Start Game");
}
}
if(m_virtualMachine)
{
auto pad = m_virtualMachine->GetPadHandler();
if(pad)
static_cast<CPH_Libretro_Input*>(pad)->UpdateInputState();
2019-12-31 21:02:35 +01:00
if(m_virtualMachine->GetGSHandler())
m_virtualMachine->GetGSHandler()->ProcessSingleFrame();
}
2019-12-31 21:02:35 +01:00
}
void retro_reset(void)
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
if(m_virtualMachine)
{
2019-06-25 12:39:28 +01:00
if(!m_virtualMachine->GetGSHandler())
SetupVideoHandler();
2019-12-31 21:02:35 +01:00
// m_virtualMachine->Pause();
m_virtualMachine->Reset();
m_virtualMachine->m_ee->m_os->BootFromCDROM();
m_virtualMachine->Resume();
CLog::GetInstance().Print(LOG_NAME, "%s\n", "Reset Game");
}
first_run = false;
}
bool retro_load_game(const retro_game_info* info)
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
fs::path filePath = info->path;
CAppConfig::GetInstance().SetPreferencePath(PREF_PS2_CDROM0_PATH, filePath);
CAppConfig::GetInstance().Save();
first_run = false;
auto rgb = RETRO_PIXEL_FORMAT_XRGB8888;
g_environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &rgb);
2019-06-25 12:35:40 +01:00
#ifdef GLES_COMPATIBILITY
g_hw_render.context_type = RETRO_HW_CONTEXT_OPENGLES3;
#else
2019-12-31 21:02:35 +01:00
g_hw_render.context_type = RETRO_HW_CONTEXT_OPENGL_CORE;
2019-06-25 12:35:40 +01:00
#endif
2019-12-31 21:02:35 +01:00
g_hw_render.version_major = 3;
g_hw_render.version_minor = 2;
g_hw_render.context_reset = retro_context_reset;
g_hw_render.context_destroy = retro_context_destroy;
g_hw_render.cache_context = false;
g_hw_render.bottom_left_origin = true;
g_hw_render.depth = true;
g_environ_cb(RETRO_ENVIRONMENT_SET_HW_SHARED_CONTEXT, nullptr);
g_environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER, &g_hw_render);
g_environ_cb(RETRO_ENVIRONMENT_SET_HW_SHARED_CONTEXT, nullptr);
2019-06-25 19:04:04 +01:00
g_environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)m_vars.data());
2019-12-31 21:02:35 +01:00
return true;
}
void retro_unload_game(void)
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
}
2019-06-25 19:04:04 +01:00
bool retro_load_game_special(unsigned game_type, const struct retro_game_info* info, size_t num_info)
2019-12-31 21:02:35 +01:00
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
return false;
}
void retro_init()
{
#ifdef __ANDROID__
Framework::PathUtils::SetFilesDirPath(getenv("EXTERNAL_STORAGE"));
#endif
2019-12-31 21:02:35 +01:00
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
m_virtualMachine = new CPS2VM();
m_virtualMachine->Initialize();
SetupInputHandler();
SetupSoundHandler();
first_run = false;
}
void retro_deinit()
{
CLog::GetInstance().Print(LOG_NAME, "%s\n", __FUNCTION__);
if(m_virtualMachine)
{
// Note: since we're forced GS into running on this thread
// we need to clear its queue, to prevent it freezing the reset of the system during delete
auto gsHandler = m_virtualMachine->GetGSHandler();
if(gsHandler)
static_cast<CGSH_OpenGL_Libretro*>(gsHandler)->Release();
m_virtualMachine->Pause();
m_virtualMachine->DestroyPadHandler();
m_virtualMachine->DestroyGSHandler();
m_virtualMachine->DestroySoundHandler();
m_virtualMachine->Destroy();
delete m_virtualMachine;
m_virtualMachine = nullptr;
}
}