mirror of
https://github.com/libretro/parallel-gx1.git
synced 2026-11-07 04:26:01 -10:00
In dual core the CPU thread called into the frontend while the frontend's own loop was running: every button and axis read went to input_cb from SI polling, each field's audio went to the batch callback, and rumble went to the rumble interface. retro_run now reads every input of the libretro devices once, right after poll_cb, and emulation reads those values. Rumble the emulation asks for is kept until the end of retro_run, which hands it over together with the audio the frame produced.
779 lines
27 KiB
C++
779 lines
27 KiB
C++
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <libretro.h>
|
|
#include <string>
|
|
|
|
#include <fstream>
|
|
#include <vector>
|
|
|
|
#include "AudioCommon/AudioCommon.h"
|
|
#include "AudioCommon/SoundStream.h"
|
|
#include "Common/ChunkFile.h"
|
|
#include "Common/Event.h"
|
|
#include "Common/FrameClock.h"
|
|
#include "Common/IniFile.h"
|
|
#include "Common/FileUtil.h"
|
|
#include "Common/GL/GLContext.h"
|
|
#include "Common/Logging/LogManager.h"
|
|
#include "Common/ParallelFor.h"
|
|
#include "Common/Thread.h"
|
|
#include "Common/scmrev.h"
|
|
#include "Common/Version.h"
|
|
#include "Core/ActionReplay.h"
|
|
#include "Core/BootManager.h"
|
|
#include "Core/Config/GraphicsSettings.h"
|
|
#include "Core/Config/MainSettings.h"
|
|
#include "Core/Config/SYSCONFSettings.h"
|
|
#include "Core/ConfigManager.h"
|
|
#include "Core/Core.h"
|
|
#include "Core/GeckoCode.h"
|
|
#include "Core/GeckoCodeConfig.h"
|
|
#include "Core/HW/CPU.h"
|
|
#include "Core/HW/Memmap.h"
|
|
#include "Core/HW/ProcessorInterface.h"
|
|
#include "Core/HW/VideoInterface.h"
|
|
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
|
#include "Core/IOS/USB/Emulated/Microphone.h"
|
|
#include "Core/PatchEngine.h"
|
|
#include "Core/State.h"
|
|
#include "Core/System.h"
|
|
#include "DolphinLibretro/Audio.h"
|
|
#include "DolphinLibretro/Input.h"
|
|
#include "DolphinLibretro/Common/Options.h"
|
|
#include "DolphinLibretro/Video.h"
|
|
#include "DolphinLibretro/VR.h"
|
|
#include "VideoBackends/OGL/OGLTexture.h"
|
|
#include "VideoBackends/OGL/OGLGfx.h"
|
|
#include "VideoCommon/AsyncRequests.h"
|
|
#include "VideoCommon/Fifo.h"
|
|
#include "VideoCommon/Present.h"
|
|
#include "VideoCommon/TextureConfig.h"
|
|
#include "VideoCommon/VideoCommon.h"
|
|
#include "VideoCommon/VideoConfig.h"
|
|
#include "VideoCommon/Widescreen.h"
|
|
#include "Core/Boot/Boot.h"
|
|
|
|
#ifdef PERF_TEST
|
|
static struct retro_perf_callback perf_cb;
|
|
|
|
#define RETRO_PERFORMANCE_INIT(name) \
|
|
retro_perf_tick_t current_ticks; \
|
|
static struct retro_perf_counter name = {#name}; \
|
|
if (!name.registered) \
|
|
perf_cb.perf_register(&(name)); \
|
|
current_ticks = name.total
|
|
|
|
#define RETRO_PERFORMANCE_START(name) perf_cb.perf_start(&(name))
|
|
#define RETRO_PERFORMANCE_STOP(name) \
|
|
perf_cb.perf_stop(&(name)); \
|
|
current_ticks = name.total - current_ticks;
|
|
#else
|
|
#define RETRO_PERFORMANCE_INIT(name)
|
|
#define RETRO_PERFORMANCE_START(name)
|
|
#define RETRO_PERFORMANCE_STOP(name)
|
|
#endif
|
|
|
|
namespace Libretro
|
|
{
|
|
extern retro_environment_t environ_cb;
|
|
static bool widescreen;
|
|
// The aspect ratio last reported to the frontend (FrameAspect)
|
|
static float frame_aspect;
|
|
// Whether the geometry last reported to the frontend was sized for VR
|
|
static bool vr_frame;
|
|
double g_core_refresh_rate{0};
|
|
// Reset per game (see retro_run): the first refresh-rate settle after boot must not
|
|
// trigger a mode-family reinit, only a real in-game 50<->60 switch should.
|
|
static bool s_refresh_rate_settled = false;
|
|
extern void reload_cheats_from_ini();
|
|
extern unsigned msg_interface_version;
|
|
|
|
// Dolphin stretches its picture over the frame and the frontend gets the
|
|
// Aspect Ratio mode's ratio, so the picture is not scaled twice; Stretch and
|
|
// VR give 4:3 or 16:9 by whether the game runs widescreen.
|
|
static float FrameAspect()
|
|
{
|
|
const auto aspect_mode = static_cast<AspectMode>(Options::GetCached<int>(
|
|
Options::gfx_settings::ASPECT_RATIO, static_cast<int>(AspectMode::Stretch)));
|
|
if (aspect_mode == AspectMode::Stretch || VR::IsEnabled() || !g_presenter || !g_widescreen)
|
|
return widescreen ? 16.0f / 9.0f : 4.0f / 3.0f;
|
|
const float aspect = g_presenter->CalculateDrawAspectRatio(aspect_mode, false);
|
|
return aspect > 0.0f ? aspect : (widescreen ? 16.0f / 9.0f : 4.0f / 3.0f);
|
|
}
|
|
|
|
static void ApplyAspectMode()
|
|
{
|
|
if (VR::IsEnabled())
|
|
VR::ApplyConfig();
|
|
else
|
|
Config::SetCurrent(Config::GFX_ASPECT_RATIO, AspectMode::Stretch);
|
|
}
|
|
} // namespace Libretro
|
|
|
|
extern "C" {
|
|
|
|
void retro_set_environment(retro_environment_t cb)
|
|
{
|
|
Libretro::environ_cb = cb;
|
|
#ifdef PERF_TEST
|
|
environ_cb(RETRO_ENVIRONMENT_GET_PERF_INTERFACE, &perf_cb);
|
|
#endif
|
|
static const struct retro_subsystem_memory_info gbp_gba_memory[] = {
|
|
{"srm", RETRO_MEMORY_SAVE_RAM},
|
|
};
|
|
static const struct retro_subsystem_rom_info gbp_roms[] = {
|
|
{"GBA ROM", "gba|gbc|gb|zip|7z",
|
|
true,
|
|
true,
|
|
false,
|
|
gbp_gba_memory, 1},
|
|
{"GameCube Disc", "rvz|gcm|iso|ciso|wbfs|gcz|tgc|m3u",
|
|
true,
|
|
false,
|
|
true,
|
|
nullptr, 0},
|
|
};
|
|
static const struct retro_subsystem_info subsystems[] = {
|
|
{
|
|
"Game Boy Player",
|
|
"GBP",
|
|
gbp_roms, 2,
|
|
Libretro::g_gbplayer_subsystem_id
|
|
},
|
|
{nullptr, nullptr, nullptr, 0, 0}};
|
|
cb(RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO, (void*)subsystems);
|
|
|
|
// Declared before any game loads, so a frontend can list them without one
|
|
Libretro::Options::SetVariables();
|
|
}
|
|
|
|
void retro_init(void)
|
|
{
|
|
uint64_t serialization_quirks = RETRO_SERIALIZATION_QUIRK_MUST_INITIALIZE;
|
|
Libretro::environ_cb(RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS, &serialization_quirks);
|
|
|
|
enum retro_pixel_format xrgb888 = RETRO_PIXEL_FORMAT_XRGB8888;
|
|
Libretro::environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &xrgb888);
|
|
|
|
Libretro::environ_cb(RETRO_ENVIRONMENT_GET_MESSAGE_INTERFACE_VERSION,
|
|
&Libretro::msg_interface_version);
|
|
}
|
|
|
|
void retro_deinit(void)
|
|
{
|
|
Libretro::g_emuthread_launched = false;
|
|
parallel_for_deinit();
|
|
#ifdef PERF_TEST
|
|
perf_cb.perf_log();
|
|
#endif
|
|
}
|
|
|
|
void retro_get_system_info(retro_system_info* info)
|
|
{
|
|
// Build a semver string from SCM_DESC_STR (e.g. "2512-226" -> "2512.0.226")
|
|
// with the short git hash as build metadata (e.g. "+13192dec3d")
|
|
static std::string version_str = []() {
|
|
std::string desc = SCM_DESC_STR;
|
|
std::string major, commits;
|
|
auto dash = desc.find('-');
|
|
if (dash != std::string::npos)
|
|
{
|
|
major = desc.substr(0, dash);
|
|
auto dash2 = desc.find('-', dash + 1);
|
|
commits = (dash2 != std::string::npos)
|
|
? desc.substr(dash + 1, dash2 - dash - 1)
|
|
: desc.substr(dash + 1);
|
|
}
|
|
else
|
|
{
|
|
major = desc;
|
|
commits = "0";
|
|
}
|
|
std::string hash = SCM_REV_STR;
|
|
if (hash.size() > 10)
|
|
hash = hash.substr(0, 10);
|
|
return major + ".0." + commits + "+" + hash;
|
|
}();
|
|
|
|
info->need_fullpath = true;
|
|
info->valid_extensions = "elf|dol|gcm|iso|tgc|wbfs|ciso|gcz|wad|wia|rvz|m3u";
|
|
info->library_version = version_str.c_str();
|
|
info->library_name = "ParaLLEl GX1";
|
|
info->block_extract = true;
|
|
}
|
|
|
|
void retro_get_system_av_info(retro_system_av_info* info)
|
|
{
|
|
int efbScale = Libretro::Options::GetCached<int>(
|
|
Libretro::Options::gfx_settings::EFB_SCALE);
|
|
|
|
int base_height = Libretro::Video::GetAdjustedBaseHeight();
|
|
|
|
// Fixed max (>= any runtime height) so base can grow via SET_GEOMETRY alone.
|
|
int max_height = base_height > 576 ? base_height : 576;
|
|
|
|
info->geometry.base_width = Libretro::Video::GetAdjustedBaseWidth() * efbScale;
|
|
info->geometry.base_height = base_height * efbScale;
|
|
|
|
info->geometry.max_width = Libretro::Video::GetAdjustedBaseWidth() * efbScale;
|
|
info->geometry.max_height = max_height * efbScale;
|
|
|
|
if (g_widescreen)
|
|
Libretro::widescreen = g_widescreen->IsGameWidescreen() || g_Config.bWidescreenHack;
|
|
else if (Core::System::GetInstance().IsWii())
|
|
Libretro::widescreen = Config::Get(Config::SYSCONF_WIDESCREEN);
|
|
|
|
info->geometry.aspect_ratio = Libretro::frame_aspect = Libretro::FrameAspect();
|
|
|
|
// The packed frame holds both eyes side by side
|
|
Libretro::vr_frame = Libretro::VR::IsEnabled();
|
|
if (Libretro::vr_frame)
|
|
info->geometry.aspect_ratio *= 2.0;
|
|
|
|
Core::System& system = Core::System::GetInstance();
|
|
double fps = system.GetVideoInterface().GetTargetRefreshRate();
|
|
if (fps <= 0.0)
|
|
fps = (retro_get_region() == RETRO_REGION_NTSC) ? (60.0 / 1.001) : 50.0;
|
|
info->timing.fps = fps;
|
|
info->timing.sample_rate = Libretro::Audio::GetActiveSampleRate();
|
|
}
|
|
|
|
void retro_reset(void)
|
|
{
|
|
Core::System::GetInstance().GetProcessorInterface().ResetButton_Tap();
|
|
}
|
|
|
|
void retro_run(void)
|
|
{
|
|
// One retro_run is one frame of the core's timebase, as long as the frontend was told.
|
|
const double frame_hz = Libretro::g_core_refresh_rate > 1.0 ? Libretro::g_core_refresh_rate : 60.0;
|
|
frame_clock_advance(static_cast<unsigned>(std::lround(1e6 / frame_hz)));
|
|
|
|
Libretro::Input::InitSensors();
|
|
Libretro::Options::CheckForUpdatedVariables();
|
|
Libretro::FrameTiming::CheckForFastForwarding();
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::main_interface::LOG_LEVEL))
|
|
{
|
|
#if defined(_DEBUG)
|
|
Common::Log::LogManager::GetInstance()->SetConfigLogLevel(Common::Log::LogLevel::LDEBUG);
|
|
#else
|
|
Common::Log::LogManager::GetInstance()->SetConfigLogLevel(
|
|
static_cast<Common::Log::LogLevel>(
|
|
Libretro::Options::GetCached<int>(
|
|
Libretro::Options::main_interface::LOG_LEVEL, static_cast<int>(Common::Log::LogLevel::LINFO))));
|
|
#endif
|
|
}
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::core::CPU_CLOCK_RATE))
|
|
{
|
|
double cpuClock = Libretro::Options::GetCached<double>(
|
|
Libretro::Options::core::CPU_CLOCK_RATE);
|
|
Config::SetCurrent(Config::MAIN_OVERCLOCK, cpuClock);
|
|
Config::SetCurrent(Config::MAIN_OVERCLOCK_ENABLE, cpuClock != 1.0);
|
|
}
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_settings::WIDESCREEN_HACK))
|
|
{
|
|
g_Config.bWidescreenHack = Libretro::Options::GetCached<bool>(
|
|
Libretro::Options::gfx_settings::WIDESCREEN_HACK);
|
|
}
|
|
|
|
// Crop to 4:3 when advertising 480-line geometry (NTSC / PAL60), tracked per frame.
|
|
g_Config.bCropToAspectRatio = (Libretro::Video::GetAdjustedBaseHeight() == 480);
|
|
|
|
Libretro::Input::Update();
|
|
|
|
Core::System& system = Core::System::GetInstance();
|
|
|
|
if (Core::GetState(system) == Core::State::Starting &&
|
|
!Libretro::g_emuthread_launched)
|
|
{
|
|
WindowSystemInfo wsi(WindowSystemType::Libretro, nullptr, nullptr, nullptr);
|
|
if (system.IsDualCoreMode())
|
|
{
|
|
Core::StartEmuThread(system, std::move(Core::g_boot_params), wsi);
|
|
|
|
// Wait until CPU thread has reached Run()
|
|
system.GetCPU().WaitForCPURunState();
|
|
}
|
|
else
|
|
Core::EmuThread(system, std::move(Core::g_boot_params), wsi);
|
|
|
|
Libretro::g_emuthread_launched = true;
|
|
|
|
if(Config::Get(Config::MAIN_GFX_BACKEND) == "Software Renderer")
|
|
{
|
|
g_gfx.reset();
|
|
g_gfx = std::make_unique<Libretro::Video::SWGfx>();
|
|
}
|
|
else if (Config::Get(Config::MAIN_GFX_BACKEND) == "Null")
|
|
{
|
|
g_gfx.reset();
|
|
g_gfx = std::make_unique<Libretro::Video::NullGfx>();
|
|
}
|
|
|
|
Libretro::g_core_refresh_rate = system.GetVideoInterface().GetTargetRefreshRate();
|
|
Libretro::s_refresh_rate_settled = false;
|
|
|
|
// Expose GameCube and Wii memory maps to libretro
|
|
{
|
|
auto& memory = system.GetMemory();
|
|
struct retro_memory_descriptor descs[2] = {};
|
|
unsigned num_descs = 0;
|
|
|
|
descs[num_descs].ptr = memory.GetRAM();
|
|
descs[num_descs].start = 0x80000000;
|
|
descs[num_descs].len = memory.GetRamSizeReal();
|
|
descs[num_descs].flags = RETRO_MEMDESC_BIGENDIAN | RETRO_MEMORY_SYSTEM_RAM;
|
|
descs[num_descs].addrspace = "MEM1";
|
|
|
|
num_descs++;
|
|
|
|
if (system.IsWii())
|
|
{
|
|
descs[num_descs].ptr = memory.GetEXRAM();
|
|
descs[num_descs].start = 0x90000000;
|
|
descs[num_descs].len = memory.GetExRamSizeReal();
|
|
descs[num_descs].flags = RETRO_MEMDESC_BIGENDIAN | RETRO_MEMORY_SYSTEM_RAM;
|
|
descs[num_descs].addrspace = "MEM2";
|
|
num_descs++;
|
|
}
|
|
|
|
struct retro_memory_map mmap = {descs, num_descs};
|
|
Libretro::environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &mmap);
|
|
}
|
|
}
|
|
|
|
if(!Libretro::g_emuthread_launched)
|
|
{
|
|
DEBUG_LOG_FMT(BOOT, "retro_run() - waiting for g_emuthread_launched");
|
|
return;
|
|
}
|
|
|
|
if (g_gfx && Config::Get(Config::MAIN_GFX_BACKEND) == "OGL")
|
|
{
|
|
static_cast<OGL::OGLGfx*>(g_gfx.get())
|
|
->SetSystemFrameBuffer((GLuint)Libretro::Video::hw_render.get_current_framebuffer());
|
|
}
|
|
|
|
// Turning VR on or off doubles or halves the frame width, so resize it as for EFB scale.
|
|
bool vr_resized = false;
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_settings::VR_MODE))
|
|
{
|
|
const bool vr_enabled = Libretro::VR::IsEnabled();
|
|
if (vr_enabled || Libretro::vr_frame)
|
|
Libretro::VR::ApplyConfig();
|
|
vr_resized = vr_enabled != Libretro::vr_frame;
|
|
}
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_settings::EFB_SCALE) || vr_resized)
|
|
{
|
|
g_Config.iEFBScale = Libretro::Options::GetCached<int>(
|
|
Libretro::Options::gfx_settings::EFB_SCALE);
|
|
|
|
unsigned cmd = RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO;
|
|
if (Libretro::Video::hw_render.context_type == RETRO_HW_CONTEXT_D3D11 ||
|
|
Libretro::Video::hw_render.context_type == RETRO_HW_CONTEXT_D3D12)
|
|
cmd = RETRO_ENVIRONMENT_SET_GEOMETRY;
|
|
retro_system_av_info info;
|
|
retro_get_system_av_info(&info);
|
|
Libretro::environ_cb(cmd, &info);
|
|
}
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_settings::CROP_OVERSCAN))
|
|
{
|
|
retro_system_av_info info;
|
|
retro_get_system_av_info(&info);
|
|
// SET_GEOMETRY, not SET_SYSTEM_AV_INFO: the reinit would drop KMS to 240p.
|
|
Libretro::environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &info);
|
|
}
|
|
|
|
if ((g_widescreen &&
|
|
Libretro::widescreen != (g_widescreen->IsGameWidescreen() || g_Config.bWidescreenHack)) ||
|
|
std::fabs(Libretro::FrameAspect() - Libretro::frame_aspect) > 0.001f)
|
|
{
|
|
retro_system_av_info info;
|
|
retro_get_system_av_info(&info);
|
|
Libretro::environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &info);
|
|
}
|
|
|
|
// target refresh rate has changed - e.g. user has chosen 60 Hz for a PAL game
|
|
double new_rate = system.GetVideoInterface().GetTargetRefreshRate();
|
|
|
|
if (round(Libretro::g_core_refresh_rate * 1e6) != round(new_rate * 1e6))
|
|
{
|
|
const double old_rate = Libretro::g_core_refresh_rate;
|
|
|
|
NOTICE_LOG_FMT(VIDEO, "Target refresh rate changed: {:.4f} Hz -> {:.4f} Hz",
|
|
old_rate, new_rate);
|
|
|
|
Libretro::g_core_refresh_rate = new_rate;
|
|
|
|
// Re-pace the audio-callback frame timing to the new rate (PAL60 50->60).
|
|
if (Libretro::FrameTiming::IsEnabled() && new_rate > 1.0)
|
|
Libretro::FrameTiming::target_frame_duration_usec.store(
|
|
static_cast<retro_usec_t>(lround(1e6 / new_rate)), std::memory_order_relaxed);
|
|
|
|
retro_system_av_info info;
|
|
retro_get_system_av_info(&info);
|
|
|
|
// 50<->60 switch needs a new CRT mode family: only SET_SYSTEM_AV_INFO carries
|
|
// the fps switchres uses, then SET_GEOMETRY rebuilds the surface its reinit drops.
|
|
// First settle after boot keeps the initial mode, so geometry-only (no reinit flash).
|
|
const bool crosses_50_60 =
|
|
Libretro::s_refresh_rate_settled && ((old_rate > 55.0) != (new_rate > 55.0));
|
|
Libretro::s_refresh_rate_settled = true;
|
|
|
|
if (crosses_50_60)
|
|
Libretro::environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &info);
|
|
Libretro::environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &info);
|
|
}
|
|
|
|
WiimoteUpdateFlags flags;
|
|
|
|
if (system.IsWii())
|
|
{
|
|
flags.irMode = Libretro::Options::IsUpdated(Libretro::Options::wiimote::IR_MODE);
|
|
flags.irOffset = Libretro::Options::IsUpdated(Libretro::Options::wiimote::IR_OFFSET);
|
|
flags.irYaw = Libretro::Options::IsUpdated(Libretro::Options::wiimote::IR_YAW);
|
|
flags.irPitch = Libretro::Options::IsUpdated(Libretro::Options::wiimote::IR_PITCH);
|
|
flags.irDeadzone = Libretro::Options::IsUpdated(Libretro::Options::wiimote::IR_DEADZONE);
|
|
flags.irModifier = Libretro::Options::IsUpdated(Libretro::Options::wiimote::IR_MODIFIER);
|
|
flags.swingModifier = Libretro::Options::IsUpdated(Libretro::Options::wiimote::SWING_MODIFIER);
|
|
flags.swingAngle = Libretro::Options::IsUpdated(Libretro::Options::wiimote::SWING_ANGLE);
|
|
flags.sideways = Libretro::Options::IsUpdated(Libretro::Options::wiimote::HOTKEY_SIDEWAYS_TOGGLE);
|
|
flags.upright = Libretro::Options::IsUpdated(Libretro::Options::wiimote::HOTKEY_UPRIGHT_TOGGLE);
|
|
flags.irPassthrough = Libretro::Options::IsUpdated(Libretro::Options::wiimote::IR_PASSTHROUGH);
|
|
}
|
|
|
|
flags.rumble = Libretro::Options::IsUpdated(Libretro::Options::sysconf::ENABLE_RUMBLE);
|
|
flags.gcMicBtn = Libretro::Options::IsUpdated(Libretro::Options::sysconf_gc::HOTKEY_ACTIVATE_MICROPHONE);
|
|
|
|
if (flags.any())
|
|
Libretro::Input::ResetControllers(flags);
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_settings::ASPECT_RATIO))
|
|
{
|
|
// VR sets the aspect ratio together with its stereoscopy settings
|
|
Libretro::ApplyAspectMode();
|
|
retro_system_av_info info;
|
|
retro_get_system_av_info(&info);
|
|
Libretro::environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &info);
|
|
}
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::EFB_TO_TEXTURE))
|
|
Config::SetBase(Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::EFB_TO_TEXTURE, true));
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::EFB_ACCESS_ENABLE))
|
|
Config::SetBase(Config::GFX_HACK_EFB_ACCESS_ENABLE, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::EFB_ACCESS_ENABLE, false));
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::EFB_ACCESS_DEFER_INVALIDATION))
|
|
Config::SetBase(Config::GFX_HACK_EFB_DEFER_INVALIDATION, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::EFB_ACCESS_DEFER_INVALIDATION, false));
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::EFB_ACCESS_TILE_SIZE))
|
|
Config::SetBase(Config::GFX_HACK_EFB_ACCESS_TILE_SIZE, Libretro::Options::GetCached<int>(Libretro::Options::gfx_hacks::EFB_ACCESS_TILE_SIZE, 64));
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::BBOX_ENABLED))
|
|
Config::SetBase(Config::GFX_HACK_BBOX_ENABLE, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::BBOX_ENABLED, false));
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::XFB_TO_TEXTURE_ENABLE))
|
|
Config::SetBase(Config::GFX_HACK_SKIP_XFB_COPY_TO_RAM, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::XFB_TO_TEXTURE_ENABLE, true));
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::EFB_TO_VRAM))
|
|
Config::SetBase(Config::GFX_HACK_DISABLE_COPY_TO_VRAM, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::EFB_TO_VRAM, false));
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::DEFER_EFB_COPIES))
|
|
Config::SetBase(Config::GFX_HACK_DEFER_EFB_COPIES, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::DEFER_EFB_COPIES, true));
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::IMMEDIATE_XFB))
|
|
Config::SetBase(Config::GFX_HACK_IMMEDIATE_XFB, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::IMMEDIATE_XFB, false));
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::SKIP_DUPE_FRAMES))
|
|
Config::SetBase(Config::GFX_HACK_SKIP_DUPLICATE_XFBS, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::SKIP_DUPE_FRAMES, true));
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::EFB_SCALED_COPY))
|
|
Config::SetBase(Config::GFX_HACK_COPY_EFB_SCALED, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::EFB_SCALED_COPY, true));
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::EFB_EMULATE_FORMAT_CHANGES))
|
|
Config::SetBase(Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::EFB_EMULATE_FORMAT_CHANGES, false));
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::VERTEX_ROUNDING))
|
|
Config::SetBase(Config::GFX_HACK_VERTEX_ROUNDING, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::VERTEX_ROUNDING, false));
|
|
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::FAST_TEXTURE_SAMPLING))
|
|
Config::SetBase(Config::GFX_HACK_FAST_TEXTURE_SAMPLING, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::FAST_TEXTURE_SAMPLING, true));
|
|
|
|
#ifdef __APPLE__
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_hacks::NO_MIPMAPPING))
|
|
Config::SetBase(Config::GFX_HACK_NO_MIPMAPPING, Libretro::Options::GetCached<bool>(Libretro::Options::gfx_hacks::NO_MIPMAPPING, false));
|
|
#endif
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::gfx_settings::TEXTURE_CACHE_ACCURACY))
|
|
Config::SetBase(Config::GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES, Libretro::Options::GetCached<int>(Libretro::Options::gfx_settings::TEXTURE_CACHE_ACCURACY, 128));
|
|
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::sysconf::WII_SPEAK_MUTED))
|
|
Config::SetCurrent(Config::MAIN_WII_SPEAK_MUTED,
|
|
Libretro::Options::GetCached<bool>(Libretro::Options::sysconf::WII_SPEAK_MUTED));
|
|
|
|
const bool wiimoteDeviceUpdated =
|
|
Libretro::Options::IsUpdated(Libretro::Options::wiimote::DEFAULT_DEVICE);
|
|
if (Libretro::Options::IsUpdated(Libretro::Options::sysconf::WIIMOTE_CONTINUOUS_SCANNING) ||
|
|
wiimoteDeviceUpdated)
|
|
{
|
|
Config::SetCurrent(Config::MAIN_WIIMOTE_CONTINUOUS_SCANNING,
|
|
Libretro::Options::GetCached<bool>(Libretro::Options::sysconf::WIIMOTE_CONTINUOUS_SCANNING) ||
|
|
Libretro::Options::GetCached<std::string>(Libretro::Options::wiimote::DEFAULT_DEVICE) == "real");
|
|
WiimoteReal::Initialize(Wiimote::InitializeMode::DO_NOT_WAIT_FOR_WIIMOTES);
|
|
}
|
|
|
|
if (wiimoteDeviceUpdated)
|
|
Libretro::Input::ResetControllers(WiimoteUpdateFlags{});
|
|
|
|
if (Config::Get(Config::MAIN_BLUETOOTH_PASSTHROUGH_ENABLED))
|
|
Libretro::Input::BluetoothPassthroughBind();
|
|
|
|
Libretro::VR::Update(Libretro::widescreen ? 16.0f / 9.0f : 4.0f / 3.0f);
|
|
|
|
RETRO_PERFORMANCE_INIT(dolphin_main_func);
|
|
RETRO_PERFORMANCE_START(dolphin_main_func);
|
|
|
|
if (system.IsDualCoreMode())
|
|
{
|
|
Core::DoFrameStep(system);
|
|
system.GetFifo().RunGpuLoop();
|
|
}
|
|
else
|
|
{
|
|
system.GetCPU().RunSingleFrame();
|
|
}
|
|
|
|
RETRO_PERFORMANCE_STOP(dolphin_main_func);
|
|
|
|
if (Libretro::Input::g_has_microphone_support)
|
|
poll_microphone();
|
|
|
|
// What the frame produced for the frontend, handed over from its own thread.
|
|
Libretro::Input::FlushOutputs();
|
|
if (SoundStream* sound_stream = system.GetSoundStream())
|
|
sound_stream->PushPending();
|
|
}
|
|
|
|
size_t retro_serialize_size(void)
|
|
{
|
|
size_t size = 0;
|
|
|
|
Core::System& system = Core::System::GetInstance();
|
|
if (!Core::IsRunning(system))
|
|
return 0;
|
|
|
|
AsyncRequests* ar = AsyncRequests::GetInstance();
|
|
|
|
if (system.IsDualCoreMode())
|
|
ar->SetPassthrough(true);
|
|
|
|
Core::RunOnCPUThread(Core::System::GetInstance(), [&] {
|
|
PointerWrap p((u8**)&size, sizeof(size_t), PointerWrap::Mode::Measure);
|
|
State::DoState(Core::System::GetInstance(), p);
|
|
}, true); // wait = true
|
|
|
|
if (system.IsDualCoreMode())
|
|
ar->SetPassthrough(false);
|
|
|
|
return size;
|
|
}
|
|
|
|
bool retro_serialize(void* data, size_t size)
|
|
{
|
|
Core::System& system = Core::System::GetInstance();
|
|
if (!Core::IsRunning(system))
|
|
return false;
|
|
|
|
AsyncRequests* ar = AsyncRequests::GetInstance();
|
|
|
|
if (system.IsDualCoreMode())
|
|
ar->SetPassthrough(true);
|
|
|
|
const bool was_cpu = Core::IsCPUThread();
|
|
if (!was_cpu)
|
|
Core::DeclareAsCPUThread();
|
|
|
|
Core::RunOnCPUThread(Core::System::GetInstance(), [&] {
|
|
PointerWrap p((u8**)&data, size, PointerWrap::Mode::Write);
|
|
State::DoState(Core::System::GetInstance(), p);
|
|
}, true);
|
|
|
|
if (!was_cpu)
|
|
Core::UndeclareAsCPUThread();
|
|
|
|
if (system.IsDualCoreMode())
|
|
ar->SetPassthrough(false);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool retro_unserialize(const void* data, size_t size)
|
|
{
|
|
Core::System& system = Core::System::GetInstance();
|
|
if (!Core::IsRunning(system))
|
|
return false;
|
|
|
|
AsyncRequests* ar = AsyncRequests::GetInstance();
|
|
|
|
if (system.IsDualCoreMode())
|
|
ar->SetPassthrough(true);
|
|
|
|
const bool was_cpu = Core::IsCPUThread();
|
|
if (!was_cpu)
|
|
Core::DeclareAsCPUThread();
|
|
|
|
Core::RunOnCPUThread(Core::System::GetInstance(), [&] {
|
|
PointerWrap p((u8**)&data, size, PointerWrap::Mode::Read);
|
|
State::DoState(Core::System::GetInstance(), p);
|
|
}, true);
|
|
|
|
if (!was_cpu)
|
|
Core::UndeclareAsCPUThread();
|
|
|
|
if (system.IsDualCoreMode())
|
|
ar->SetPassthrough(false);
|
|
|
|
return true;
|
|
}
|
|
|
|
unsigned retro_get_region()
|
|
{
|
|
Core::System& system = Core::System::GetInstance();
|
|
|
|
if (DiscIO::IsNTSC(SConfig::GetInstance().m_region) ||
|
|
(Core::System::GetInstance().IsWii() && Config::Get(Config::SYSCONF_PAL60)) ||
|
|
system.IsTriforce())
|
|
return RETRO_REGION_NTSC;
|
|
|
|
return RETRO_REGION_PAL;
|
|
}
|
|
|
|
unsigned retro_api_version()
|
|
{
|
|
return RETRO_API_VERSION;
|
|
}
|
|
|
|
size_t retro_get_memory_size(unsigned id)
|
|
{
|
|
auto& memory = Core::System::GetInstance().GetMemory();
|
|
if (id == RETRO_MEMORY_SYSTEM_RAM)
|
|
return memory.GetRamSizeReal();
|
|
return 0;
|
|
}
|
|
|
|
void* retro_get_memory_data(unsigned id)
|
|
{
|
|
auto& memory = Core::System::GetInstance().GetMemory();
|
|
if (id == RETRO_MEMORY_SYSTEM_RAM)
|
|
return memory.GetRAM();
|
|
return NULL;
|
|
}
|
|
|
|
static void enable_cheat_by_code(bool enabled, const char* code)
|
|
{
|
|
std::string incoming(code);
|
|
bool found = false;
|
|
|
|
// Match Action Replay codes
|
|
for (auto& c : Libretro::g_ar_codes)
|
|
{
|
|
std::string serialized;
|
|
serialized.reserve(64);
|
|
|
|
for (const auto& op : c.ops)
|
|
{
|
|
if (!serialized.empty())
|
|
serialized += "+";
|
|
|
|
serialized += fmt::format("{:08X} {:08X}", op.cmd_addr, op.value);
|
|
}
|
|
|
|
if (serialized == incoming)
|
|
{
|
|
c.enabled = enabled;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Match Gecko codes only if not already matched
|
|
if (!found)
|
|
{
|
|
for (auto& c : Libretro::g_gecko_codes)
|
|
{
|
|
std::string serialized;
|
|
serialized.reserve(64);
|
|
|
|
for (const auto& line : c.codes)
|
|
{
|
|
if (!serialized.empty())
|
|
serialized += "+";
|
|
|
|
serialized += line.original_line;
|
|
}
|
|
|
|
if (serialized == incoming)
|
|
{
|
|
c.enabled = enabled;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// reset cheats to off
|
|
void retro_cheat_reset(void)
|
|
{
|
|
const bool importCheats = Libretro::GetOption<bool>(Libretro::Options::retroarch_core::CHEATS_IMPORT, true);
|
|
|
|
if (!Config::AreCheatsEnabled() || !importCheats)
|
|
return;
|
|
|
|
Core::RunOnCPUThread(Core::System::GetInstance(), [&] {
|
|
PatchEngine::Shutdown();
|
|
}, true);
|
|
|
|
Libretro::reload_cheats_from_ini();
|
|
}
|
|
|
|
// Enable/disable a cheat
|
|
void retro_cheat_set(unsigned index, bool enabled, const char *code)
|
|
{
|
|
if (!Config::AreCheatsEnabled())
|
|
{
|
|
Core::DisplayMessage("You cannot apply any cheats until you have enabled the following option:\n"
|
|
"Core > Internal Cheats - set this to ON");
|
|
return;
|
|
}
|
|
|
|
const bool importCheats = Libretro::GetOption<bool>(Libretro::Options::retroarch_core::CHEATS_IMPORT, true);
|
|
if (!importCheats)
|
|
{
|
|
Core::DisplayMessage("You cannot apply any cheats until you enabled the following option:\n"
|
|
"Core > Automatically Import Cheats into RetroArch - set this to ON");
|
|
return;
|
|
}
|
|
|
|
const std::string& game_id = SConfig::GetInstance().GetGameID();
|
|
const u16 game_revision = SConfig::GetInstance().GetRevision();
|
|
|
|
// Look up by code as index is unreliable
|
|
enable_cheat_by_code(enabled, code);
|
|
|
|
Core::RunOnCPUThread(Core::System::GetInstance(), [&] {
|
|
// Apply Action Replay
|
|
ActionReplay::ApplyCodes(Libretro::g_ar_codes, game_id, game_revision);
|
|
|
|
// Apply Gecko
|
|
Gecko::SetActiveCodes(Libretro::g_gecko_codes, game_id, game_revision);
|
|
}, true);
|
|
}
|
|
} // extern "C"
|