#pragma once #include "libretro.h" #include "audio_engine.h" #include #include #include #include #include #include enum class DisplayMode : int { STRETCH = 0, ASPECT_RATIO = 1, INTEGER_SCALE = 2 }; class LibretroFrontend { public: static LibretroFrontend& instance(); bool loadCore(const char* soPath); bool loadGame(const char* romPath); void start(); void pause(); void resume(); void stop(); void reset(); void setWindow(ANativeWindow* window); void releaseWindow(); void setButtonState(uint16_t state); void setAnalogState(int stick, int16_t x, int16_t y); bool saveState(const char* path); bool loadState(const char* path); void setAudioEnabled(bool enabled); void setFrameSkip(int skip); void setDisplayMode(int mode); void setSystemDir(const char* dir); void setSaveDir(const char* dir); bool isRunning() const { return running_.load(); } private: LibretroFrontend() = default; ~LibretroFrontend(); void runLoop(); void unloadCore(); // Libretro callbacks (static because libretro uses C function pointers) static bool environmentCallback(unsigned cmd, void* data); static void videoRefreshCallback(const void* data, unsigned width, unsigned height, size_t pitch); static size_t audioSampleBatchCallback(const int16_t* data, size_t frames); static void audioSampleCallback(int16_t left, int16_t right); static void inputPollCallback(); static int16_t inputStateCallback(unsigned port, unsigned device, unsigned index, unsigned id); static void logCallback(enum retro_log_level level, const char* fmt, ...); // Core function pointers (resolved via dlsym) void* core_handle_ = nullptr; retro_init_t core_init_ = nullptr; retro_deinit_t core_deinit_ = nullptr; retro_api_version_t core_api_version_ = nullptr; retro_get_system_info_t core_get_system_info_ = nullptr; retro_get_system_av_info_t core_get_system_av_info_ = nullptr; retro_set_environment_t core_set_environment_ = nullptr; retro_set_video_refresh_t core_set_video_refresh_ = nullptr; retro_set_audio_sample_t core_set_audio_sample_ = nullptr; retro_set_audio_sample_batch_t core_set_audio_sample_batch_ = nullptr; retro_set_input_poll_t core_set_input_poll_ = nullptr; retro_set_input_state_t core_set_input_state_ = nullptr; retro_set_controller_port_device_t core_set_controller_port_device_ = nullptr; retro_load_game_t core_load_game_ = nullptr; retro_unload_game_t core_unload_game_ = nullptr; retro_run_t core_run_ = nullptr; retro_reset_t core_reset_ = nullptr; retro_serialize_size_t core_serialize_size_ = nullptr; retro_serialize_t core_serialize_ = nullptr; retro_unserialize_t core_unserialize_ = nullptr; retro_get_memory_data_t core_get_memory_data_ = nullptr; retro_get_memory_size_t core_get_memory_size_ = nullptr; // State std::atomic running_{false}; std::atomic paused_{false}; std::atomic game_loaded_{false}; std::thread emu_thread_; // Video ANativeWindow* window_ = nullptr; std::mutex window_mutex_; unsigned pixel_format_ = RETRO_PIXEL_FORMAT_RGB565; std::atomic display_mode_{DisplayMode::STRETCH}; float core_aspect_ratio_ = 0.0f; unsigned core_base_width_ = 0; unsigned core_base_height_ = 0; // Audio AudioEngine audio_engine_; // Input std::atomic button_state_{0}; std::atomic analog_lx_{0}, analog_ly_{0}; std::atomic analog_rx_{0}, analog_ry_{0}; // Paths std::string system_dir_; std::string save_dir_; // Frame timing double target_fps_ = 60.0; int frame_skip_ = 0; // Core options std::unordered_map core_options_; std::atomic options_updated_{false}; };