citra/src/core/core.h

424 lines
13 KiB
C++
Raw Normal View History

2014-04-09 00:15:46 +01:00
// Copyright 2014 Citra Emulator Project
2014-12-17 05:38:14 +00:00
// Licensed under GPLv2 or any later version
2014-04-09 00:15:46 +01:00
// Refer to the license.txt file included.
2014-04-09 00:15:46 +01:00
#pragma once
#include <memory>
#include <mutex>
#include <string>
2020-04-06 22:47:52 +01:00
#include <boost/serialization/version.hpp>
#include "common/common_types.h"
#include "core/custom_tex_cache.h"
#include "core/frontend/applets/mii_selector.h"
#include "core/frontend/applets/swkbd.h"
#include "core/frontend/image_interface.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/perf_stats.h"
#include "core/telemetry_session.h"
class ARM_Interface;
2014-04-04 02:22:13 +01:00
namespace Frontend {
class EmuWindow;
}
2018-11-21 03:38:47 +00:00
namespace Memory {
class MemorySystem;
}
2017-12-20 18:44:32 +00:00
namespace AudioCore {
class DspInterface;
}
Initial support for scripting (#4016) * Add ZeroMQ external submodule * ZeroMQ libzmq building on macOS * Added RPC namespace, settings and logging * Added request queue handling and new classes * Add C++ interface to ZeroMQ * Added start of ZeroMQ RPC Server implementation. * Request construction and callback request handling * Read and write memory implementation * Add ID to request format and send reply * Add RPC setting to macOS UI * Fixed initialization order bug and added exception handling * Working read-write through Python * Update CMakeLists for libzmq to resolve target name conflict on Windows * Platform-specific CMake definitions for Windows/non-Windows * Add comments * Revert "Add RPC setting to macOS UI" * Always run RPC server instead of configurable * Add Python scripting example. Updated .gitignore * Rename member variables to remove trailing underscore * Finally got libzmq external project building on macOS * Add missing dependency during libzmq build * Adding more missing dependencies [skip ci] * Only build what is required from libzmq * Extra length checks on client input * Call InvalidateCacheRange after memory write * Revert MinGW change. Fix clang-format. Improve error handling in request/reply. Allow any length of data read/write in Python. * Re-organized RPC static global state into a proper class. [skip ci] * Make sure libzmq always builds in Release mode * Renamed Request to Packet since Request and Reply are the same thing * Moved request fulfillment out of Packet and into RPCServer * Change request thread from sleep to condition variable * Remove non-blocking polling from ZMQ server code. Receive now blocks and terminates properly without sleeping. This change significantly improves script speed. * Move scripting files to dist/ instead of src/ * C++ code review changes for jroweboy [skip ci] * Python code review changes for jroweboy [skip ci] * Add docstrings and tests to citra.py [skip ci] * Add host OS check for libzmq build * Revert "Add host OS check for libzmq build" * Fixed a hang when emulation is stopped and restarted due to improper destruction order of ZMQ objects [skip ci] * Add scripting directory to archive packaging [skip ci] * Specify C/CXX compiler variables on MinGW build * Only specify compiler on Linux mingw * Use gcc and g++ on Windows mingw * Specify generator for mingw * Don't specify toolchain on windows mingw * Changed citra.py to support Python 3 instead of Python 2 * Fix bug where RPC wouldn't restart after Stop/Start emulation * Added copyright to headers and reorganized includes and forward declarations
2018-09-11 21:00:12 +01:00
namespace RPC {
class RPCServer;
}
namespace Service {
namespace SM {
class ServiceManager;
}
namespace FS {
class ArchiveManager;
}
} // namespace Service
2018-10-11 19:49:52 +01:00
namespace Kernel {
class KernelSystem;
}
namespace Cheats {
class CheatEngine;
}
2019-01-26 14:36:39 +00:00
namespace VideoDumper {
class Backend;
}
2019-09-07 23:13:10 +01:00
class RendererBase;
namespace Core {
class ExclusiveMonitor;
2018-10-27 20:53:20 +01:00
class Timing;
class System {
public:
/**
* Gets the instance of the System singleton class.
* @returns Reference to the instance of the System singleton class.
*/
[[nodiscard]] static System& GetInstance() {
return s_instance;
}
/// Enumeration representing the return values of the System Initialize and Load process.
enum class ResultStatus : u32 {
2016-12-17 06:20:47 +00:00
Success, ///< Succeeded
ErrorNotInitialized, ///< Error trying to use core prior to initialization
ErrorGetLoader, ///< Error finding the correct application loader
ErrorSystemMode, ///< Error determining the system mode
ErrorLoader, ///< Error loading the specified application
ErrorLoader_ErrorEncrypted, ///< Error loading the specified application due to encryption
ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an
/// invalid format
ErrorSystemFiles, ///< Error in finding system files
ErrorVideoCore, ///< Error in the video core
ErrorVideoCore_ErrorGenericDrivers, ///< Error in the video core due to the user having
/// generic drivers installed
ErrorVideoCore_ErrorBelowGL43, ///< Error in the video core due to the user not having
/// OpenGL 4.3 or higher
2020-04-12 23:12:15 +01:00
ErrorSavestate, ///< Error saving or loading
ShutdownRequested, ///< Emulated program requested a system shutdown
ErrorUnknown ///< Any other error
};
~System();
/**
* Run the core CPU loop
2016-12-17 06:20:47 +00:00
* This function runs the core for the specified number of CPU instructions before trying to
* update hardware. This is much faster than SingleStep (and should be equivalent), as the CPU
* is not required to do a full dispatch with each instruction. NOTE: the number of instructions
* requested is not guaranteed to run, as this will be interrupted preemptively if a hardware
* update is requested (e.g. on a thread switch).
* @param tight_loop If false, the CPU single-steps.
* @return Result status, indicating whethor or not the operation succeeded.
*/
[[nodiscard]] ResultStatus RunLoop(bool tight_loop = true);
/**
* Step the CPU one instruction
* @return Result status, indicating whethor or not the operation succeeded.
*/
[[nodiscard]] ResultStatus SingleStep();
/// Shutdown the emulated system.
void Shutdown(bool is_deserializing = false);
/// Shutdown and then load again
void Reset();
2020-01-12 00:24:44 +00:00
enum class Signal : u32 { None, Shutdown, Reset, Save, Load };
bool SendSignal(Signal signal, u32 param = 0);
2020-01-12 00:24:44 +00:00
/// Request reset of the system
2022-10-23 21:55:50 +01:00
void RequestReset(const std::string& chainload = "") {
m_chainloadpath = chainload;
2020-01-12 00:24:44 +00:00
SendSignal(Signal::Reset);
}
/// Request shutdown of the system
void RequestShutdown() {
2020-01-12 00:24:44 +00:00
SendSignal(Signal::Shutdown);
}
/**
* Load an executable application.
* @param emu_window Reference to the host-system window used for video output and keyboard
* input.
* @param filepath String path to the executable application to load on the host file system.
* @returns ResultStatus code, indicating if the operation succeeded.
*/
[[nodiscard]] ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
Frontend::EmuWindow* secondary_window = {});
/**
* Indicates if the emulated system is powered on (all subsystems initialized and able to run an
* application).
* @returns True if the emulated system is powered on, otherwise false.
*/
[[nodiscard]] bool IsPoweredOn() const {
return cpu_cores.size() > 0 &&
std::all_of(cpu_cores.begin(), cpu_cores.end(),
[](std::shared_ptr<ARM_Interface> ptr) { return ptr != nullptr; });
;
}
/**
* Returns a reference to the telemetry session for this emulation session.
* @returns Reference to the telemetry session.
*/
[[nodiscard]] Core::TelemetrySession& TelemetrySession() const {
return *telemetry_session;
}
/// Prepare the core emulation for a reschedule
void PrepareReschedule();
[[nodiscard]] PerfStats::Results GetAndResetPerfStats();
/**
* Gets a reference to the emulated CPU.
* @returns A reference to the emulated CPU.
*/
[[nodiscard]] ARM_Interface& GetRunningCore() {
return *running_core;
};
/**
* Gets a reference to the emulated CPU.
* @param core_id The id of the core requested.
* @returns A reference to the emulated CPU.
*/
[[nodiscard]] ARM_Interface& GetCore(u32 core_id) {
return *cpu_cores[core_id];
};
[[nodiscard]] u32 GetNumCores() const {
return static_cast<u32>(cpu_cores.size());
}
void InvalidateCacheRange(u32 start_address, std::size_t length) {
for (const auto& cpu : cpu_cores) {
cpu->InvalidateCacheRange(start_address, length);
}
}
2017-12-20 18:44:32 +00:00
/**
* Gets a reference to the emulated DSP.
* @returns A reference to the emulated DSP.
*/
[[nodiscard]] AudioCore::DspInterface& DSP() {
2017-12-20 18:44:32 +00:00
return *dsp_core;
}
[[nodiscard]] RendererBase& Renderer();
2019-09-07 23:13:10 +01:00
/**
* Gets a reference to the service manager.
* @returns A reference to the service manager.
*/
[[nodiscard]] Service::SM::ServiceManager& ServiceManager();
/**
* Gets a const reference to the service manager.
* @returns A const reference to the service manager.
*/
[[nodiscard]] const Service::SM::ServiceManager& ServiceManager() const;
/// Gets a reference to the archive manager
[[nodiscard]] Service::FS::ArchiveManager& ArchiveManager();
/// Gets a const reference to the archive manager
[[nodiscard]] const Service::FS::ArchiveManager& ArchiveManager() const;
2018-10-11 19:49:52 +01:00
/// Gets a reference to the kernel
[[nodiscard]] Kernel::KernelSystem& Kernel();
2018-10-11 19:49:52 +01:00
/// Gets a const reference to the kernel
[[nodiscard]] const Kernel::KernelSystem& Kernel() const;
2018-10-11 19:49:52 +01:00
2018-10-27 20:53:20 +01:00
/// Gets a reference to the timing system
[[nodiscard]] Timing& CoreTiming();
2018-10-27 20:53:20 +01:00
/// Gets a const reference to the timing system
[[nodiscard]] const Timing& CoreTiming() const;
2018-10-27 20:53:20 +01:00
2018-11-21 03:38:47 +00:00
/// Gets a reference to the memory system
[[nodiscard]] Memory::MemorySystem& Memory();
2018-11-21 03:38:47 +00:00
/// Gets a const reference to the memory system
[[nodiscard]] const Memory::MemorySystem& Memory() const;
2018-11-21 03:38:47 +00:00
/// Gets a reference to the cheat engine
[[nodiscard]] Cheats::CheatEngine& CheatEngine();
/// Gets a const reference to the cheat engine
[[nodiscard]] const Cheats::CheatEngine& CheatEngine() const;
/// Gets a reference to the custom texture cache system
[[nodiscard]] Core::CustomTexCache& CustomTexCache();
/// Gets a const reference to the custom texture cache system
[[nodiscard]] const Core::CustomTexCache& CustomTexCache() const;
/// Gets a reference to the video dumper backend
[[nodiscard]] VideoDumper::Backend& VideoDumper();
/// Gets a const reference to the video dumper backend
[[nodiscard]] const VideoDumper::Backend& VideoDumper() const;
std::unique_ptr<PerfStats> perf_stats;
FrameLimiter frame_limiter;
void SetStatus(ResultStatus new_status, const char* details = nullptr) {
status = new_status;
if (details) {
status_details = details;
}
}
[[nodiscard]] const std::string& GetStatusDetails() const {
return status_details;
}
[[nodiscard]] Loader::AppLoader& GetAppLoader() const {
return *app_loader;
}
/// Frontend Applets
void RegisterMiiSelector(std::shared_ptr<Frontend::MiiSelector> mii_selector);
void RegisterSoftwareKeyboard(std::shared_ptr<Frontend::SoftwareKeyboard> swkbd);
[[nodiscard]] std::shared_ptr<Frontend::MiiSelector> GetMiiSelector() const {
return registered_mii_selector;
}
[[nodiscard]] std::shared_ptr<Frontend::SoftwareKeyboard> GetSoftwareKeyboard() const {
return registered_swkbd;
}
/// Image interface
void RegisterImageInterface(std::shared_ptr<Frontend::ImageInterface> image_interface);
[[nodiscard]] std::shared_ptr<Frontend::ImageInterface> GetImageInterface() const {
return registered_image_interface;
}
void SaveState(u32 slot) const;
2019-08-07 02:53:56 +01:00
void LoadState(u32 slot);
2019-08-07 02:53:56 +01:00
2022-10-20 22:14:57 +01:00
/// Self delete ncch
2022-10-21 00:12:29 +01:00
bool SetSelfDelete(const std::string& file) {
if (m_filepath == file) {
2022-10-20 22:14:57 +01:00
self_delete_pending = true;
2022-10-21 00:12:29 +01:00
return true;
}
return false;
2022-10-20 22:14:57 +01:00
}
private:
/**
* Initialize the emulated system.
* @param emu_window Reference to the host-system window used for video output and keyboard
* input.
* @param system_mode The system mode.
* @return ResultStatus code, indicating if the operation succeeded.
*/
[[nodiscard]] ResultStatus Init(Frontend::EmuWindow& emu_window,
Frontend::EmuWindow* secondary_window, u32 system_mode,
u8 n3ds_mode, u32 num_cores);
/// Reschedule the core emulation
void Reschedule();
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
/// ARM11 CPU core
std::vector<std::shared_ptr<ARM_Interface>> cpu_cores;
ARM_Interface* running_core = nullptr;
/// DSP core
2017-12-20 18:44:32 +00:00
std::unique_ptr<AudioCore::DspInterface> dsp_core;
/// When true, signals that a reschedule should happen
bool reschedule_pending{};
/// Telemetry session for this emulation session
std::unique_ptr<Core::TelemetrySession> telemetry_session;
/// Service manager
2019-12-23 11:41:07 +00:00
std::unique_ptr<Service::SM::ServiceManager> service_manager;
/// Frontend applets
std::shared_ptr<Frontend::MiiSelector> registered_mii_selector;
std::shared_ptr<Frontend::SoftwareKeyboard> registered_swkbd;
/// Cheats manager
std::unique_ptr<Cheats::CheatEngine> cheat_engine;
2019-01-26 14:36:39 +00:00
/// Video dumper backend
std::unique_ptr<VideoDumper::Backend> video_dumper;
/// Custom texture cache system
std::unique_ptr<Core::CustomTexCache> custom_tex_cache;
/// Image interface
std::shared_ptr<Frontend::ImageInterface> registered_image_interface;
Initial support for scripting (#4016) * Add ZeroMQ external submodule * ZeroMQ libzmq building on macOS * Added RPC namespace, settings and logging * Added request queue handling and new classes * Add C++ interface to ZeroMQ * Added start of ZeroMQ RPC Server implementation. * Request construction and callback request handling * Read and write memory implementation * Add ID to request format and send reply * Add RPC setting to macOS UI * Fixed initialization order bug and added exception handling * Working read-write through Python * Update CMakeLists for libzmq to resolve target name conflict on Windows * Platform-specific CMake definitions for Windows/non-Windows * Add comments * Revert "Add RPC setting to macOS UI" * Always run RPC server instead of configurable * Add Python scripting example. Updated .gitignore * Rename member variables to remove trailing underscore * Finally got libzmq external project building on macOS * Add missing dependency during libzmq build * Adding more missing dependencies [skip ci] * Only build what is required from libzmq * Extra length checks on client input * Call InvalidateCacheRange after memory write * Revert MinGW change. Fix clang-format. Improve error handling in request/reply. Allow any length of data read/write in Python. * Re-organized RPC static global state into a proper class. [skip ci] * Make sure libzmq always builds in Release mode * Renamed Request to Packet since Request and Reply are the same thing * Moved request fulfillment out of Packet and into RPCServer * Change request thread from sleep to condition variable * Remove non-blocking polling from ZMQ server code. Receive now blocks and terminates properly without sleeping. This change significantly improves script speed. * Move scripting files to dist/ instead of src/ * C++ code review changes for jroweboy [skip ci] * Python code review changes for jroweboy [skip ci] * Add docstrings and tests to citra.py [skip ci] * Add host OS check for libzmq build * Revert "Add host OS check for libzmq build" * Fixed a hang when emulation is stopped and restarted due to improper destruction order of ZMQ objects [skip ci] * Add scripting directory to archive packaging [skip ci] * Specify C/CXX compiler variables on MinGW build * Only specify compiler on Linux mingw * Use gcc and g++ on Windows mingw * Specify generator for mingw * Don't specify toolchain on windows mingw * Changed citra.py to support Python 3 instead of Python 2 * Fix bug where RPC wouldn't restart after Stop/Start emulation * Added copyright to headers and reorganized includes and forward declarations
2018-09-11 21:00:12 +01:00
/// RPC Server for scripting support
std::unique_ptr<RPC::RPCServer> rpc_server;
std::unique_ptr<Service::FS::ArchiveManager> archive_manager;
std::unique_ptr<Memory::MemorySystem> memory;
2018-10-11 19:49:52 +01:00
std::unique_ptr<Kernel::KernelSystem> kernel;
2018-10-27 20:53:20 +01:00
std::unique_ptr<Timing> timing;
2018-12-06 01:24:37 +00:00
2022-10-23 07:25:00 +01:00
std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
private:
static System s_instance;
bool initalized = false;
ResultStatus status = ResultStatus::Success;
std::string status_details = "";
/// Saved variables for reset
Frontend::EmuWindow* m_emu_window;
Frontend::EmuWindow* m_secondary_window;
std::string m_filepath;
2022-10-23 21:55:50 +01:00
std::string m_chainloadpath;
u64 title_id;
2022-10-20 22:14:57 +01:00
bool self_delete_pending;
std::mutex signal_mutex;
Signal current_signal;
u32 signal_param;
2019-08-06 17:45:06 +01:00
friend class boost::serialization::access;
2019-12-27 21:07:29 +00:00
template <typename Archive>
void serialize(Archive& ar, const unsigned int file_version);
};
[[nodiscard]] inline ARM_Interface& GetRunningCore() {
return System::GetInstance().GetRunningCore();
}
[[nodiscard]] inline ARM_Interface& GetCore(u32 core_id) {
return System::GetInstance().GetCore(core_id);
}
[[nodiscard]] inline u32 GetNumCores() {
return System::GetInstance().GetNumCores();
}
[[nodiscard]] inline AudioCore::DspInterface& DSP() {
2017-12-20 18:44:32 +00:00
return System::GetInstance().DSP();
}
} // namespace Core
2020-04-06 22:47:52 +01:00
BOOST_CLASS_VERSION(Core::System, 1)