/* * Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG * Copyright (C) 2020-2025 Rosalie Wanders * * 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 . */ #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(romHeader.SystemType)]); if (!std::filesystem::is_regular_file(rom)) { return; } std::vector 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; }