mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
369 lines
12 KiB
C++
369 lines
12 KiB
C++
/**
|
|
* @file libretro_plugin.hpp
|
|
* @brief Libretro/RetroArch integration plugin for retro gaming
|
|
*
|
|
* Enables running retro games via libretro cores with video/audio
|
|
* capture for streaming, recording, or cloud gaming.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "media/plugin.hpp"
|
|
#include <vector>
|
|
#include <memory>
|
|
#include <atomic>
|
|
#include <thread>
|
|
#include <functional>
|
|
#include <deque>
|
|
|
|
namespace media::plugins {
|
|
|
|
/**
|
|
* @brief Supported libretro core systems
|
|
*/
|
|
enum class RetroSystem {
|
|
// Nintendo
|
|
NES, ///< Nintendo Entertainment System (FCEUmm, Nestopia)
|
|
SNES, ///< Super Nintendo (Snes9x, bsnes)
|
|
N64, ///< Nintendo 64 (Mupen64Plus, ParaLLEl)
|
|
GB, ///< Game Boy (Gambatte, SameBoy)
|
|
GBC, ///< Game Boy Color
|
|
GBA, ///< Game Boy Advance (mGBA, VBA-M)
|
|
NDS, ///< Nintendo DS (DeSmuME, melonDS)
|
|
VB, ///< Virtual Boy (Beetle VB)
|
|
|
|
// Sega
|
|
MasterSystem, ///< Sega Master System (Genesis Plus GX)
|
|
Genesis, ///< Sega Genesis/Mega Drive
|
|
SegaCD, ///< Sega CD
|
|
Saturn, ///< Sega Saturn (Beetle Saturn, Yabause)
|
|
Dreamcast, ///< Sega Dreamcast (Flycast)
|
|
GameGear, ///< Sega Game Gear
|
|
|
|
// Sony
|
|
PS1, ///< PlayStation (Beetle PSX, PCSX ReARMed)
|
|
PSP, ///< PlayStation Portable (PPSSPP)
|
|
|
|
// Atari
|
|
Atari2600, ///< Atari 2600 (Stella)
|
|
Atari7800, ///< Atari 7800 (ProSystem)
|
|
AtariLynx, ///< Atari Lynx (Handy)
|
|
AtariJaguar, ///< Atari Jaguar (Virtual Jaguar)
|
|
|
|
// Other
|
|
PCEngine, ///< PC Engine/TurboGrafx-16 (Beetle PCE)
|
|
NeoGeo, ///< Neo Geo (FinalBurn Neo)
|
|
Arcade, ///< Arcade (MAME, FinalBurn Neo)
|
|
DOS, ///< DOS (DOSBox)
|
|
ScummVM, ///< ScummVM adventure games
|
|
|
|
// Computers
|
|
MSX, ///< MSX (blueMSX, fMSX)
|
|
Amiga, ///< Amiga (PUAE)
|
|
C64, ///< Commodore 64 (VICE)
|
|
ZXSpectrum, ///< ZX Spectrum (Fuse)
|
|
|
|
Custom ///< User-specified core
|
|
};
|
|
|
|
/**
|
|
* @brief Libretro core information
|
|
*/
|
|
struct LibretroCore {
|
|
std::string name; ///< Core name (e.g., "snes9x")
|
|
std::string display_name; ///< Display name (e.g., "Snes9x")
|
|
std::string path; ///< Path to .dll/.so/.dylib
|
|
std::string version;
|
|
RetroSystem system = RetroSystem::Custom;
|
|
|
|
std::vector<std::string> supported_extensions;
|
|
bool supports_save_states = true;
|
|
bool supports_cheats = true;
|
|
bool supports_achievements = false; ///< RetroAchievements
|
|
|
|
// Core options
|
|
std::map<std::string, std::string> default_options;
|
|
};
|
|
|
|
/**
|
|
* @brief Input device types
|
|
*/
|
|
enum class RetroInputDevice {
|
|
None,
|
|
Joypad, ///< Standard controller
|
|
Mouse,
|
|
Keyboard,
|
|
Lightgun,
|
|
Analog, ///< Analog controller (DualShock)
|
|
Pointer, ///< Touch/pointer device
|
|
Multitap ///< Multi-player adapter
|
|
};
|
|
|
|
/**
|
|
* @brief Controller button mapping
|
|
*/
|
|
struct RetroInputMapping {
|
|
int player = 0;
|
|
RetroInputDevice device = RetroInputDevice::Joypad;
|
|
|
|
// Button mappings (key code -> retro button)
|
|
std::map<int, int> button_map;
|
|
|
|
// Axis mappings for analog
|
|
std::map<int, std::pair<int, int>> axis_map; ///< axis -> (retro_axis, multiplier)
|
|
};
|
|
|
|
/**
|
|
* @brief Game session configuration
|
|
*/
|
|
struct RetroSessionConfig {
|
|
std::string session_id;
|
|
std::string rom_path;
|
|
std::string core_name; ///< Core to use
|
|
|
|
// Video output
|
|
int output_width = 1280;
|
|
int output_height = 720;
|
|
int output_fps = 60;
|
|
std::string pixel_format = "rgb565";
|
|
bool integer_scaling = true; ///< Pixel-perfect scaling
|
|
std::string shader_preset; ///< RetroArch shader preset
|
|
|
|
// Audio output
|
|
int audio_sample_rate = 48000;
|
|
bool audio_sync = true;
|
|
float audio_volume = 1.0f;
|
|
|
|
// Save data
|
|
std::string save_directory;
|
|
std::string state_directory;
|
|
bool auto_save = true;
|
|
int auto_save_interval_sec = 60;
|
|
|
|
// Core options override
|
|
std::map<std::string, std::string> core_options;
|
|
|
|
// Input
|
|
std::vector<RetroInputMapping> input_mappings;
|
|
|
|
// Streaming/recording
|
|
bool stream_output = false;
|
|
std::string stream_url; ///< RTMP URL for streaming
|
|
bool record_output = false;
|
|
std::string record_path;
|
|
|
|
// Netplay
|
|
bool netplay_enabled = false;
|
|
bool netplay_host = false;
|
|
std::string netplay_server;
|
|
int netplay_port = 55435;
|
|
std::string netplay_password;
|
|
|
|
// Cheats
|
|
std::vector<std::string> cheat_codes;
|
|
|
|
// RetroAchievements
|
|
bool achievements_enabled = false;
|
|
std::string ra_username;
|
|
std::string ra_token;
|
|
bool achievements_hardcore = false;
|
|
};
|
|
|
|
/**
|
|
* @brief Save state metadata
|
|
*/
|
|
struct RetroSaveState {
|
|
std::string state_id;
|
|
std::string session_id;
|
|
std::string path;
|
|
std::chrono::system_clock::time_point created_at;
|
|
std::string screenshot_path;
|
|
std::string description;
|
|
bool is_auto_save = false;
|
|
int slot = 0;
|
|
};
|
|
|
|
/**
|
|
* @brief Achievement unlock info
|
|
*/
|
|
struct RetroAchievement {
|
|
int id;
|
|
std::string title;
|
|
std::string description;
|
|
std::string badge_url;
|
|
int points;
|
|
bool unlocked = false;
|
|
std::chrono::system_clock::time_point unlock_time;
|
|
bool hardcore = false;
|
|
};
|
|
|
|
/**
|
|
* @brief Game session runtime state
|
|
*/
|
|
struct RetroSessionState {
|
|
std::string session_id;
|
|
bool is_running = false;
|
|
bool is_paused = false;
|
|
|
|
std::string rom_name;
|
|
std::string core_name;
|
|
RetroSystem system;
|
|
|
|
// Timing
|
|
double fps = 0.0;
|
|
double frame_time_ms = 0.0;
|
|
uint64_t frame_count = 0;
|
|
std::chrono::seconds play_time{0};
|
|
|
|
// Performance
|
|
float cpu_usage_percent = 0.0f;
|
|
int audio_buffer_level = 0;
|
|
bool fast_forward = false;
|
|
bool slow_motion = false;
|
|
float speed_multiplier = 1.0f;
|
|
|
|
// Netplay
|
|
bool netplay_connected = false;
|
|
int netplay_player_count = 0;
|
|
int netplay_ping_ms = 0;
|
|
|
|
// Last screenshot
|
|
std::string last_screenshot_path;
|
|
};
|
|
|
|
/**
|
|
* @brief Libretro integration plugin
|
|
*
|
|
* Provides retro gaming capabilities via libretro cores:
|
|
* - Load and run any libretro core
|
|
* - Video capture for streaming/recording
|
|
* - Save states and battery saves
|
|
* - Netplay support
|
|
* - RetroAchievements integration
|
|
* - Shader support (CRT, scanlines, etc.)
|
|
* - Input mapping for multiple players
|
|
*/
|
|
class LibretroPlugin : public Plugin {
|
|
public:
|
|
LibretroPlugin();
|
|
~LibretroPlugin() override;
|
|
|
|
// Plugin interface
|
|
auto name() const -> std::string override { return "libretro"; }
|
|
auto version() const -> std::string override { return "1.0.0"; }
|
|
auto description() const -> std::string override {
|
|
return "Libretro/RetroArch integration for retro gaming with streaming support";
|
|
}
|
|
|
|
auto initialize(const nlohmann::json& config) -> Result<void> override;
|
|
auto shutdown() -> Result<void> override;
|
|
|
|
auto can_handle(JobType type) const -> bool override;
|
|
auto process(const Job& job, ProgressCallback on_progress) -> Result<nlohmann::json> override;
|
|
auto cancel(const std::string& job_id) -> Result<void> override;
|
|
|
|
auto supported_job_types() const -> std::vector<JobType> override {
|
|
return { JobType::Custom }; // Uses custom job type "retro_game"
|
|
}
|
|
|
|
// Core management
|
|
auto scan_cores(const std::string& directory) -> Result<int>;
|
|
auto get_available_cores() -> std::vector<LibretroCore>;
|
|
auto get_core_for_rom(const std::string& rom_path) -> Result<LibretroCore>;
|
|
auto download_core(const std::string& core_name, RetroSystem system) -> Result<std::string>;
|
|
auto get_core_info(const std::string& core_name) -> Result<LibretroCore>;
|
|
|
|
// Session management
|
|
auto start_session(const RetroSessionConfig& config) -> Result<std::string>;
|
|
auto stop_session(const std::string& session_id) -> Result<void>;
|
|
auto pause_session(const std::string& session_id) -> Result<void>;
|
|
auto resume_session(const std::string& session_id) -> Result<void>;
|
|
auto get_session_state(const std::string& session_id) -> Result<RetroSessionState>;
|
|
auto list_active_sessions() -> std::vector<RetroSessionState>;
|
|
|
|
// Save states
|
|
auto save_state(const std::string& session_id, int slot = -1,
|
|
const std::string& description = "") -> Result<RetroSaveState>;
|
|
auto load_state(const std::string& session_id, int slot) -> Result<void>;
|
|
auto load_state_file(const std::string& session_id, const std::string& path) -> Result<void>;
|
|
auto list_save_states(const std::string& session_id) -> std::vector<RetroSaveState>;
|
|
auto delete_save_state(const std::string& state_id) -> Result<void>;
|
|
|
|
// Input handling
|
|
auto send_input(const std::string& session_id, int player, int button, bool pressed) -> Result<void>;
|
|
auto send_analog_input(const std::string& session_id, int player, int axis, float value) -> Result<void>;
|
|
auto update_input_mapping(const std::string& session_id, const RetroInputMapping& mapping) -> Result<void>;
|
|
|
|
// Video/Audio capture
|
|
auto take_screenshot(const std::string& session_id) -> Result<std::string>;
|
|
auto start_recording(const std::string& session_id, const std::string& output_path) -> Result<void>;
|
|
auto stop_recording(const std::string& session_id) -> Result<std::string>;
|
|
auto start_streaming(const std::string& session_id, const std::string& rtmp_url) -> Result<void>;
|
|
auto stop_streaming(const std::string& session_id) -> Result<void>;
|
|
|
|
// Cheats
|
|
auto load_cheats(const std::string& session_id, const std::vector<std::string>& codes) -> Result<void>;
|
|
auto enable_cheat(const std::string& session_id, int index, bool enabled) -> Result<void>;
|
|
auto clear_cheats(const std::string& session_id) -> Result<void>;
|
|
|
|
// Speed control
|
|
auto set_speed(const std::string& session_id, float multiplier) -> Result<void>;
|
|
auto toggle_fast_forward(const std::string& session_id, bool enabled) -> Result<void>;
|
|
auto frame_advance(const std::string& session_id) -> Result<void>;
|
|
|
|
// Shaders
|
|
auto list_shader_presets() -> std::vector<std::string>;
|
|
auto set_shader(const std::string& session_id, const std::string& preset) -> Result<void>;
|
|
|
|
// RetroAchievements
|
|
auto login_retroachievements(const std::string& username, const std::string& password) -> Result<std::string>;
|
|
auto get_achievements(const std::string& session_id) -> Result<std::vector<RetroAchievement>>;
|
|
auto get_achievement_progress(const std::string& session_id) -> Result<nlohmann::json>;
|
|
|
|
// Netplay
|
|
auto host_netplay(const std::string& session_id, int port, const std::string& password = "") -> Result<std::string>;
|
|
auto join_netplay(const std::string& session_id, const std::string& host, int port,
|
|
const std::string& password = "") -> Result<void>;
|
|
auto disconnect_netplay(const std::string& session_id) -> Result<void>;
|
|
auto send_netplay_chat(const std::string& session_id, const std::string& message) -> Result<void>;
|
|
|
|
private:
|
|
struct SessionRuntime;
|
|
std::map<std::string, std::unique_ptr<SessionRuntime>> sessions_;
|
|
std::mutex sessions_mutex_;
|
|
|
|
std::map<std::string, LibretroCore> cores_;
|
|
std::string cores_directory_;
|
|
std::string system_directory_;
|
|
std::string saves_directory_;
|
|
|
|
// Libretro callbacks (static for C API)
|
|
static void retro_video_refresh(const void* data, unsigned width, unsigned height, size_t pitch);
|
|
static void retro_audio_sample(int16_t left, int16_t right);
|
|
static size_t retro_audio_sample_batch(const int16_t* data, size_t frames);
|
|
static void retro_input_poll();
|
|
static int16_t retro_input_state(unsigned port, unsigned device, unsigned index, unsigned id);
|
|
static bool retro_environment(unsigned cmd, void* data);
|
|
static void retro_log(enum retro_log_level level, const char* fmt, ...);
|
|
|
|
auto load_core(const std::string& path) -> Result<void*>;
|
|
auto unload_core(void* handle) -> void;
|
|
void run_frame(SessionRuntime& session);
|
|
void encode_frame(SessionRuntime& session);
|
|
void session_loop(const std::string& session_id);
|
|
};
|
|
|
|
/**
|
|
* @brief Retro log levels (matches libretro)
|
|
*/
|
|
enum retro_log_level {
|
|
RETRO_LOG_DEBUG = 0,
|
|
RETRO_LOG_INFO,
|
|
RETRO_LOG_WARN,
|
|
RETRO_LOG_ERROR
|
|
};
|
|
|
|
} // namespace media::plugins
|
|
|
|
MEDIA_PLUGIN_EXPORT(media::plugins::LibretroPlugin)
|