mirror of
https://github.com/Rosalie241/RMG.git
synced 2025-06-25 14:07:02 -04:00
428 lines
11 KiB
C++
428 lines
11 KiB
C++
/*
|
|
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
|
|
* Copyright (C) 2020-2025 Rosalie Wanders <rosalie@mailbox.org>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 3.
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#define CORE_INTERNAL
|
|
#include "MediaLoader.hpp"
|
|
#include "RomSettings.hpp"
|
|
#include "Emulation.hpp"
|
|
#include "RomHeader.hpp"
|
|
#include "Settings.hpp"
|
|
#include "Library.hpp"
|
|
#include "Netplay.hpp"
|
|
#include "Plugins.hpp"
|
|
#include "Cheats.hpp"
|
|
#include "Error.hpp"
|
|
#include "File.hpp"
|
|
#include "Rom.hpp"
|
|
|
|
#ifdef DISCORD_RPC
|
|
#include "DiscordRpc.hpp"
|
|
#endif // DISCORD_RPC
|
|
|
|
#include "m64p/Api.hpp"
|
|
|
|
//
|
|
// Local Functions
|
|
//
|
|
|
|
static bool get_emulation_state(m64p_emu_state* state)
|
|
{
|
|
std::string error;
|
|
m64p_error ret;
|
|
|
|
if (!m64p::Core.IsHooked())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ret = m64p::Core.DoCommand(M64CMD_CORE_STATE_QUERY, M64CORE_EMU_STATE, state);
|
|
if (ret != M64ERR_SUCCESS)
|
|
{
|
|
error = "get_emulation_state m64p::Core.DoCommand(M64CMD_CORE_STATE_QUERY) Failed: ";
|
|
error += m64p::Core.ErrorMessage(ret);
|
|
CoreSetError(error);
|
|
}
|
|
|
|
return ret == M64ERR_SUCCESS;
|
|
}
|
|
|
|
static void apply_coresettings_overlay(void)
|
|
{
|
|
CoreSettingsSetValue(SettingsID::Core_RandomizeInterrupt, CoreSettingsGetBoolValue(SettingsID::CoreOverlay_RandomizeInterrupt));
|
|
CoreSettingsSetValue(SettingsID::Core_CPU_Emulator, CoreSettingsGetIntValue(SettingsID::CoreOverlay_CPU_Emulator));
|
|
CoreSettingsSetValue(SettingsID::Core_DisableExtraMem, CoreSettingsGetBoolValue(SettingsID::CoreOverlay_DisableExtraMem));
|
|
CoreSettingsSetValue(SettingsID::Core_EnableDebugger, CoreSettingsGetBoolValue(SettingsID::CoreOverlay_EnableDebugger));
|
|
CoreSettingsSetValue(SettingsID::Core_CountPerOp, CoreSettingsGetIntValue(SettingsID::CoreOverlay_CountPerOp));
|
|
CoreSettingsSetValue(SettingsID::Core_CountPerOpDenomPot, CoreSettingsGetIntValue(SettingsID::CoreOverlay_CountPerOpDenomPot));
|
|
CoreSettingsSetValue(SettingsID::Core_SiDmaDuration, CoreSettingsGetIntValue(SettingsID::CoreOverlay_SiDmaDuration));
|
|
CoreSettingsSetValue(SettingsID::Core_SaveFileNameFormat, CoreSettingsGetIntValue(SettingsID::CoreOverLay_SaveFileNameFormat));
|
|
}
|
|
|
|
static void apply_game_coresettings_overlay(void)
|
|
{
|
|
std::string section;
|
|
CoreRomSettings romSettings;
|
|
bool overrideCoreSettings;
|
|
|
|
// when we fail to retrieve the rom settings, return
|
|
if (!CoreGetCurrentDefaultRomSettings(romSettings))
|
|
{
|
|
return;
|
|
}
|
|
|
|
section = romSettings.MD5;
|
|
|
|
// when we don't need to override the core settings, return
|
|
overrideCoreSettings = CoreSettingsGetBoolValue(SettingsID::Game_OverrideCoreSettings, section);
|
|
if (!overrideCoreSettings)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// apply settings overlay
|
|
CoreSettingsSetValue(SettingsID::Core_RandomizeInterrupt, CoreSettingsGetBoolValue(SettingsID::Game_RandomizeInterrupt, section));
|
|
CoreSettingsSetValue(SettingsID::Core_CPU_Emulator, CoreSettingsGetIntValue(SettingsID::Game_CPU_Emulator, section));
|
|
CoreSettingsSetValue(SettingsID::Core_CountPerOpDenomPot, CoreSettingsGetIntValue(SettingsID::Game_CountPerOpDenomPot, section));
|
|
}
|
|
|
|
static void apply_pif_rom_settings(void)
|
|
{
|
|
CoreRomHeader romHeader;
|
|
std::string error;
|
|
m64p_error ret;
|
|
int cpuEmulator;
|
|
bool usePifROM;
|
|
|
|
// when we fail to retrieve the rom settings, return
|
|
if (!CoreGetCurrentRomHeader(romHeader))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// when we're using the dynarec, return
|
|
cpuEmulator = CoreSettingsGetIntValue(SettingsID::Core_CPU_Emulator);
|
|
if (cpuEmulator >= 2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
usePifROM = CoreSettingsGetBoolValue(SettingsID::Core_PIF_Use);
|
|
if (!usePifROM)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const SettingsID settingsIds[] =
|
|
{
|
|
SettingsID::Core_PIF_NTSC,
|
|
SettingsID::Core_PIF_PAL,
|
|
};
|
|
|
|
std::string rom = CoreSettingsGetStringValue(settingsIds[static_cast<int>(romHeader.SystemType)]);
|
|
if (!std::filesystem::is_regular_file(rom))
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::vector<char> buffer;
|
|
if (!CoreReadFile(rom, buffer))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ret = m64p::Core.DoCommand(M64CMD_PIF_OPEN, buffer.size(), buffer.data());
|
|
if (ret != M64ERR_SUCCESS)
|
|
{
|
|
error = "open_pif_rom m64p::Core.DoCommand(M64CMD_PIF_OPEN) Failed: ";
|
|
error += m64p::Core.ErrorMessage(ret);
|
|
CoreSetError(error);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Exported Functions
|
|
//
|
|
|
|
CORE_EXPORT bool CoreStartEmulation(std::filesystem::path n64rom, std::filesystem::path n64ddrom,
|
|
std::string address, int port, int player)
|
|
{
|
|
std::string error;
|
|
m64p_error m64p_ret;
|
|
bool netplay_ret = false;
|
|
CoreRomType type;
|
|
bool netplay = !address.empty();
|
|
|
|
if (!CoreOpenRom(n64rom))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!CoreApplyRomPluginSettings())
|
|
{
|
|
CoreApplyPluginSettings();
|
|
CoreCloseRom();
|
|
return false;
|
|
}
|
|
|
|
if (!CoreArePluginsReady())
|
|
{
|
|
CoreApplyPluginSettings();
|
|
CoreCloseRom();
|
|
return false;
|
|
}
|
|
|
|
if (!CoreAttachPlugins())
|
|
{
|
|
CoreApplyPluginSettings();
|
|
CoreCloseRom();
|
|
return false;
|
|
}
|
|
|
|
if (netplay)
|
|
{ // netplay cheats
|
|
if (!CoreApplyNetplayCheats())
|
|
{
|
|
CoreDetachPlugins();
|
|
CoreApplyPluginSettings();
|
|
CoreCloseRom();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{ // local cheats
|
|
if (!CoreApplyCheats())
|
|
{
|
|
CoreDetachPlugins();
|
|
CoreApplyPluginSettings();
|
|
CoreCloseRom();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!CoreGetRomType(type))
|
|
{
|
|
CoreClearCheats();
|
|
CoreDetachPlugins();
|
|
CoreApplyPluginSettings();
|
|
CoreCloseRom();
|
|
return false;
|
|
}
|
|
|
|
// set disk file in media loader when ROM is a cartridge
|
|
if (type == CoreRomType::Cartridge)
|
|
{
|
|
CoreMediaLoaderSetDiskFile(n64ddrom);
|
|
}
|
|
|
|
// apply core settings overlay
|
|
apply_coresettings_overlay();
|
|
|
|
// apply game core settings overrides
|
|
apply_game_coresettings_overlay();
|
|
|
|
// apply pif rom settings
|
|
apply_pif_rom_settings();
|
|
|
|
#ifdef DISCORD_RPC
|
|
CoreDiscordRpcUpdate(true);
|
|
#endif // DISCORD_RPC
|
|
|
|
#ifdef NETPLAY
|
|
if (netplay)
|
|
{
|
|
netplay_ret = CoreInitNetplay(address, port, player);
|
|
if (!netplay_ret)
|
|
{
|
|
m64p_ret = M64ERR_SYSTEM_FAIL;
|
|
}
|
|
}
|
|
#endif // NETPLAY
|
|
|
|
// only start emulation when initializing netplay
|
|
// is successful or if there's no netplay requested
|
|
if (!netplay || netplay_ret)
|
|
{
|
|
m64p_ret = m64p::Core.DoCommand(M64CMD_EXECUTE, 0, nullptr);
|
|
if (m64p_ret != M64ERR_SUCCESS)
|
|
{
|
|
error = "CoreStartEmulation m64p::Core.DoCommand(M64CMD_EXECUTE) Failed: ";
|
|
error += m64p::Core.ErrorMessage(m64p_ret);
|
|
}
|
|
}
|
|
|
|
#ifdef NETPLAY
|
|
if (netplay && netplay_ret)
|
|
{
|
|
CoreShutdownNetplay();
|
|
}
|
|
#endif // NETPLAY
|
|
|
|
CoreClearCheats();
|
|
CoreDetachPlugins();
|
|
CoreCloseRom();
|
|
|
|
// restore plugin settings
|
|
CoreApplyPluginSettings();
|
|
|
|
// reset media loader state
|
|
CoreResetMediaLoader();
|
|
|
|
#ifdef DISCORD_RPC
|
|
CoreDiscordRpcUpdate(false);
|
|
#endif // DISCORD_RPC
|
|
|
|
if (!netplay || netplay_ret)
|
|
{
|
|
// we need to set the emulation error last,
|
|
// to prevent the other functions from
|
|
// overriding the emulation error
|
|
CoreSetError(error);
|
|
}
|
|
|
|
return m64p_ret == M64ERR_SUCCESS;
|
|
}
|
|
|
|
CORE_EXPORT bool CoreStopEmulation(void)
|
|
{
|
|
std::string error;
|
|
m64p_error ret;
|
|
|
|
if (!m64p::Core.IsHooked())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ret = m64p::Core.DoCommand(M64CMD_STOP, 0, nullptr);
|
|
if (ret != M64ERR_SUCCESS)
|
|
{
|
|
error = "CoreStopEmulation m64p::Core.DoCommand(M64CMD_STOP) Failed: ";
|
|
error += m64p::Core.ErrorMessage(ret);
|
|
CoreSetError(error);
|
|
return false;
|
|
}
|
|
|
|
return ret == M64ERR_SUCCESS;
|
|
}
|
|
|
|
CORE_EXPORT bool CorePauseEmulation(void)
|
|
{
|
|
std::string error;
|
|
m64p_error ret;
|
|
|
|
if (!m64p::Core.IsHooked())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (CoreHasInitNetplay())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!CoreIsEmulationRunning())
|
|
{
|
|
error = "CorePauseEmulation Failed: ";
|
|
error += "cannot pause emulation when emulation isn't running!";\
|
|
CoreSetError(error);
|
|
return false;
|
|
}
|
|
|
|
ret = m64p::Core.DoCommand(M64CMD_PAUSE, 0, nullptr);
|
|
if (ret != M64ERR_SUCCESS)
|
|
{
|
|
error = "CorePauseEmulation m64p::Core.DoCommand(M64CMD_PAUSE) Failed: ";
|
|
error += m64p::Core.ErrorMessage(ret);
|
|
CoreSetError(error);
|
|
}
|
|
|
|
return ret == M64ERR_SUCCESS;
|
|
}
|
|
|
|
CORE_EXPORT bool CoreResumeEmulation(void)
|
|
{
|
|
std::string error;
|
|
m64p_error ret;
|
|
|
|
if (!m64p::Core.IsHooked())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (CoreHasInitNetplay())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!CoreIsEmulationPaused())
|
|
{
|
|
error = "CoreIsEmulationPaused Failed: ";
|
|
error += "cannot resume emulation when emulation isn't paused!";
|
|
CoreSetError(error);
|
|
return false;
|
|
}
|
|
|
|
ret = m64p::Core.DoCommand(M64CMD_RESUME, 0, nullptr);
|
|
if (ret != M64ERR_SUCCESS)
|
|
{
|
|
error = "CoreResumeEmulation m64p::Core.DoCommand(M64CMD_RESUME) Failed: ";
|
|
error += m64p::Core.ErrorMessage(ret);
|
|
CoreSetError(error);
|
|
}
|
|
|
|
return ret == M64ERR_SUCCESS;
|
|
}
|
|
|
|
CORE_EXPORT bool CoreResetEmulation(bool hard)
|
|
{
|
|
std::string error;
|
|
m64p_error ret;
|
|
|
|
if (!m64p::Core.IsHooked())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (CoreIsEmulationPaused())
|
|
{
|
|
error = "CoreResetEmulation Failed: ";
|
|
error += "cannot reset emulation when paused!";
|
|
CoreSetError(error);
|
|
return false;
|
|
}
|
|
|
|
if (!CoreIsEmulationRunning())
|
|
{
|
|
error = "CoreResetEmulation Failed: ";
|
|
error += "cannot reset emulation when emulation isn't running!";
|
|
CoreSetError(error);
|
|
return false;
|
|
}
|
|
|
|
ret = m64p::Core.DoCommand(M64CMD_RESET, hard, nullptr);
|
|
if (ret != M64ERR_SUCCESS)
|
|
{
|
|
error = "CoreResetEmulation m64p::Core.DoCommand(M64CMD_RESET) Failed: ";
|
|
error += m64p::Core.ErrorMessage(ret);
|
|
CoreSetError(error);
|
|
}
|
|
|
|
return ret == M64ERR_SUCCESS;
|
|
}
|
|
|
|
CORE_EXPORT bool CoreIsEmulationRunning(void)
|
|
{
|
|
m64p_emu_state state = M64EMU_STOPPED;
|
|
return get_emulation_state(&state) && state == M64EMU_RUNNING;
|
|
}
|
|
|
|
CORE_EXPORT bool CoreIsEmulationPaused(void)
|
|
{
|
|
m64p_emu_state state = M64EMU_STOPPED;
|
|
return get_emulation_state(&state) && state == M64EMU_PAUSED;
|
|
}
|