From 6f7436e8d1e54899b3afc89b016120ac68868d41 Mon Sep 17 00:00:00 2001 From: XargonWan Date: Wed, 11 Dec 2024 16:29:31 +0900 Subject: [PATCH] feat(libretro_binding): some advancement but broken on godot side --- .gitignore | 1 + .gitmodules | 3 + addons/gdretroplay/CMakeLists.txt | 40 ---- addons/gdretroplay/build_linux.sh | 13 -- addons/gdretroplay/gdretroplay.gdextension | 15 -- addons/gdretroplay/src/emulator_core.cpp | 185 ------------------ addons/gdretroplay/src/emulator_core.h | 108 ---------- external/gdlibretro | 1 + libretro_binding/libretro_binding.gdextension | 6 + main.gd | 24 ++- project.godot | 4 + scripts/emulate.gd | 46 +++++ scripts/libretro_loader.gd | 104 ++++------ 13 files changed, 111 insertions(+), 439 deletions(-) create mode 100644 .gitmodules delete mode 100644 addons/gdretroplay/CMakeLists.txt delete mode 100755 addons/gdretroplay/build_linux.sh delete mode 100644 addons/gdretroplay/gdretroplay.gdextension delete mode 100644 addons/gdretroplay/src/emulator_core.cpp delete mode 100644 addons/gdretroplay/src/emulator_core.h create mode 160000 external/gdlibretro create mode 100644 libretro_binding/libretro_binding.gdextension create mode 100644 scripts/emulate.gd diff --git a/.gitignore b/.gitignore index b5a797e..9da8179 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ godot-cpp .sconsign.dblite libretro_binding/libretro_binding.os libretro_binding/libretro_binding.so +.sconsign.dblite diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..921e28f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "external/gdlibretro"] + path = external/gdlibretro + url = https://github.com/gabrielmedici/gdlibretro diff --git a/addons/gdretroplay/CMakeLists.txt b/addons/gdretroplay/CMakeLists.txt deleted file mode 100644 index d9a8613..0000000 --- a/addons/gdretroplay/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -# Dichiarazione del progetto -cmake_minimum_required(VERSION 3.15) -project(gdretroplay) - -# Imposta il C++ standard -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -# Percorsi per Godot C++ bindings -message(STATUS "Godot C++ Bindings Path: $ENV{GODOT_CPP_BINDINGS_PATH}") - -include_directories($ENV{GODOT_CPP_BINDINGS_PATH}/include) -include_directories($ENV{GODOT_CPP_BINDINGS_PATH}/include/core) -include_directories($ENV{GODOT_CPP_BINDINGS_PATH}/include/gen) - -# File sorgenti -file(GLOB SOURCES "src/*.cpp") -message(STATUS "Sources found: ${SOURCES}") - -# Creazione della libreria condivisa -add_library(${PROJECT_NAME} SHARED ${SOURCES}) - -# Configurazioni specifiche per piattaforma -if(UNIX AND NOT ANDROID) - set(CMAKE_SHARED_LIBRARY_PREFIX "") - set(CMAKE_SHARED_LIBRARY_SUFFIX ".so") -endif() - -if(ANDROID) - include_directories(${ANDROID_NDK}/sources/android/native_app_glue) - set(CMAKE_SHARED_LIBRARY_PREFIX "") - set(CMAKE_SHARED_LIBRARY_SUFFIX ".so") -endif() - -# Imposta le proprietà del target -set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "gdretroplay") - -# Linka le librerie di Godot -link_directories($ENV{GODOT_CPP_BINDINGS_PATH}/bin) -target_link_libraries(${PROJECT_NAME} godot-cpp) diff --git a/addons/gdretroplay/build_linux.sh b/addons/gdretroplay/build_linux.sh deleted file mode 100755 index f7a5ad5..0000000 --- a/addons/gdretroplay/build_linux.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -GODOT_CPP_BINDINGS_PATH="$HOME/gits/godot-cpp" - -cd "$GODOT_CPP_BINDINGS_PATH" -scons platform=linux target=template_release generate_bindings=yes -cd - - -rm -rf build_linux -mkdir build_linux -cd build_linux -cmake .. -DCMAKE_BUILD_TYPE=Release -cmake --build . diff --git a/addons/gdretroplay/gdretroplay.gdextension b/addons/gdretroplay/gdretroplay.gdextension deleted file mode 100644 index bfcd875..0000000 --- a/addons/gdretroplay/gdretroplay.gdextension +++ /dev/null @@ -1,15 +0,0 @@ -{ - "entry_symbol": "gdextension_gdretroplay_library_init", - "library": [ - { - "platform": "Linux", - "debug": "build/gdretroplay.linux.debug.so", - "release": "build/gdretroplay.linux.release.so" - }, - { - "platform": "Android", - "debug": "build/gdretroplay.android.debug.so", - "release": "build/gdretroplay.android.release.so" - } - ] -} diff --git a/addons/gdretroplay/src/emulator_core.cpp b/addons/gdretroplay/src/emulator_core.cpp deleted file mode 100644 index 5378ca7..0000000 --- a/addons/gdretroplay/src/emulator_core.cpp +++ /dev/null @@ -1,185 +0,0 @@ -#include "emulator_core.h" -#include -#include -#include - -namespace EmulatorSystem { - -EmulatorCore::EmulatorCore() { - core = std::make_unique(); - frame_texture.instantiate(); - audio_stream.instantiate(); - input_state.fill(0); -} - -EmulatorCore::~EmulatorCore() { - cleanup(); -} - -bool EmulatorCore::initialize_core(const godot::String& core_path) { - cleanup(); - - core->handle = dlopen(core_path.utf8().get_data(), RTLD_LAZY); - if (!core->handle) return false; - - if (!load_core_functions()) { - cleanup(); - return false; - } - - core->init(); - setup_av_system(); - - return true; -} - -bool EmulatorCore::load_core_functions() { - if (!core->handle) return false; - - #define LOAD_SYM(sym) \ - core->sym = (decltype(core->sym))dlsym(core->handle, "retro_" #sym) - - LOAD_SYM(init); - LOAD_SYM(deinit); - LOAD_SYM(run); - LOAD_SYM(load_game); - LOAD_SYM(save_game_state); - LOAD_SYM(load_game_state); - LOAD_SYM(get_memory_data); - LOAD_SYM(set_memory_data); - LOAD_SYM(set_controller_port_device); - LOAD_SYM(serialize_size); - - #undef LOAD_SYM - - return (core->init && core->run && core->load_game); -} - -bool EmulatorCore::load_game(const godot::String& game_path) { - godot::Ref f = godot::FileAccess::open(game_path, godot::FileAccess::READ); - if (f.is_null()) return false; - - save_path = game_path.get_basename() + ".srm"; - game_data.resize(f->get_length()); - f->get_buffer(game_data.data(), game_data.size()); - - if (!core->load_game(game_data.data(), game_data.size())) { - return false; - } - - // Inizializza il buffer per gli states - if (core->serialize_size) { - state_buffer.resize(core->serialize_size()); - } - - // Carica SRAM se esiste - load_sram(); - - return true; -} - -void EmulatorCore::process_frame() { - if (core->run) { - core->run(); - } -} - -bool EmulatorCore::save_state(const godot::String& path) { - if (!core->save_game_state || state_buffer.empty()) return false; - - if (!core->save_game_state(state_buffer.data(), state_buffer.size())) { - return false; - } - - godot::Ref f = godot::FileAccess::open(path, godot::FileAccess::WRITE); - if (f.is_null()) return false; - - f->store_buffer(state_buffer.data(), state_buffer.size()); - return true; -} - -bool EmulatorCore::load_state(const godot::String& path) { - if (!core->load_game_state || state_buffer.empty()) return false; - - godot::Ref f = godot::FileAccess::open(path, godot::FileAccess::READ); - if (f.is_null()) return false; - - f->get_buffer(state_buffer.data(), state_buffer.size()); - return core->load_game_state(state_buffer.data(), state_buffer.size()); -} - -bool EmulatorCore::load_sram() { - if (!core->get_memory_data) return false; - - godot::Ref f = godot::FileAccess::open(save_path, godot::FileAccess::READ); - if (f.is_null()) return false; - - save_data.resize(f->get_length()); - f->get_buffer(save_data.data(), save_data.size()); - - return core->set_memory_data(0, save_data.data()); -} - -bool EmulatorCore::save_sram() { - if (!core->get_memory_data) return false; - - if (!core->get_memory_data(0, save_data.data())) { - return false; - } - - godot::Ref f = godot::FileAccess::open(save_path, godot::FileAccess::WRITE); - if (f.is_null()) return false; - - f->store_buffer(save_data.data(), save_data.size()); - return true; -} - -void EmulatorCore::set_button_pressed(int port, int button, bool pressed) { - if (port >= 0 && port < system_info.input_ports && button < 16) { - if (pressed) { - input_state[port] |= (1 << button); - } else { - input_state[port] &= ~(1 << button); - } - } -} - -void EmulatorCore::set_axis_value(int port, int axis, float value) { - // Implementa la gestione degli assi analogici se necessario -} - -void EmulatorCore::video_callback(const void* data, unsigned width, - unsigned height, size_t pitch) { - // Implementa il rendering del frame - // ... -} - -void EmulatorCore::audio_callback(int16_t left, int16_t right) { - // Implementa la gestione dell'audio - // ... -} - -void EmulatorCore::_bind_methods() { - using namespace godot; - - ClassDB::bind_method(D_METHOD("initialize_core", "core_path"), - &EmulatorCore::initialize_core); - ClassDB::bind_method(D_METHOD("load_game", "game_path"), - &EmulatorCore::load_game); - ClassDB::bind_method(D_METHOD("process_frame"), - &EmulatorCore::process_frame); - ClassDB::bind_method(D_METHOD("save_state", "path"), - &EmulatorCore::save_state); - ClassDB::bind_method(D_METHOD("load_state", "path"), - &EmulatorCore::load_state); - ClassDB::bind_method(D_METHOD("set_button_pressed", "port", "button", "pressed"), - &EmulatorCore::set_button_pressed); - ClassDB::bind_method(D_METHOD("set_axis_value", "port", "axis", "value"), - &EmulatorCore::set_axis_value); - ClassDB::bind_method(D_METHOD("get_texture"), - &EmulatorCore::get_texture); - ClassDB::bind_method(D_METHOD("get_audio_stream"), - &EmulatorCore::get_audio_stream); -} - -} // namespace EmulatorSystem diff --git a/addons/gdretroplay/src/emulator_core.h b/addons/gdretroplay/src/emulator_core.h deleted file mode 100644 index a45f00c..0000000 --- a/addons/gdretroplay/src/emulator_core.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef EMULATOR_CORE_H -#define EMULATOR_CORE_H - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace EmulatorSystem { - -class EmulatorCore : public godot::Node { - GDCLASS(EmulatorCore, godot::Node) - -private: - // Strutture per la gestione del core - struct CoreFunctions { - void* handle{nullptr}; - void (*init)(); - void (*deinit)(); - void (*run)(); - bool (*load_game)(const void* data, size_t size); - bool (*save_game_state)(void* data, size_t size); - bool (*load_game_state)(const void* data, size_t size); - bool (*get_memory_data)(unsigned id, void* data); - bool (*set_memory_data)(unsigned id, const void* data); - void (*set_controller_port_device)(unsigned port, unsigned device); - size_t (*serialize_size)(); - }; - - // Strutture per la gestione dell'audio/video - struct SystemInfo { - unsigned width{0}; - unsigned height{0}; - double fps{60.0}; - double sample_rate{44100.0}; - unsigned input_ports{2}; - }; - - // Membri privati - std::unique_ptr core; - SystemInfo system_info; - std::vector game_data; - std::vector save_data; - std::string save_path; - - godot::Ref frame_texture; - godot::Ref audio_stream; - godot::Ref audio_playback; - - std::vector frame_buffer; - std::array input_state; - - // Buffer per states - std::vector state_buffer; - - // Funzioni private - bool load_core_functions(); - void setup_av_system(); - void cleanup(); - bool load_sram(); - bool save_sram(); - - // Callback handlers - static void video_callback(const void* data, unsigned width, - unsigned height, size_t pitch); - static void audio_callback(int16_t left, int16_t right); - static void input_poll_callback(); - static int16_t input_state_callback(unsigned port, unsigned device, - unsigned index, unsigned id); - -protected: - static void _bind_methods(); - -public: - EmulatorCore(); - ~EmulatorCore(); - - // Funzioni principali - bool initialize_core(const godot::String& core_path); - bool load_game(const godot::String& game_path); - void process_frame(); - void reset(); - void close_game(); - - // Gestione stati - bool save_state(const godot::String& path); - bool load_state(const godot::String& path); - - // Input - void set_button_pressed(int port, int button, bool pressed); - void set_axis_value(int port, int axis, float value); - - // Getters - godot::Ref get_texture() const { return frame_texture; } - godot::Ref get_audio_stream() const { return audio_stream; } - - // Configurazione - void set_audio_buffer_size(float seconds); - void set_controller_type(int port, int device_type); -}; - -} // namespace EmulatorSystem - -#endif // EMULATOR_CORE_H diff --git a/external/gdlibretro b/external/gdlibretro new file mode 160000 index 0000000..7ebbf84 --- /dev/null +++ b/external/gdlibretro @@ -0,0 +1 @@ +Subproject commit 7ebbf840187f54466988c0b5b20524ad21ca1d58 diff --git a/libretro_binding/libretro_binding.gdextension b/libretro_binding/libretro_binding.gdextension new file mode 100644 index 0000000..9996923 --- /dev/null +++ b/libretro_binding/libretro_binding.gdextension @@ -0,0 +1,6 @@ +{ + "entry_class": "LibretroCoreBinding", + "library_path": "res://libretro_binding/libretro_binding.so", + "singleton": false, + "autoload": false +} diff --git a/main.gd b/main.gd index e24095d..2493cb2 100644 --- a/main.gd +++ b/main.gd @@ -1,20 +1,26 @@ extends Node3D var xr_interface: XRInterface +var emulator_script: Object func _ready(): + # Inizializzazione OpenXR xr_interface = XRServer.find_interface("OpenXR") - if xr_interface and xr_interface.is_initialized(): - print("OpenXR initialized succesfully") - + print("OpenXR initialized successfully") DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED) - get_viewport().use_xr = true else: print("OpenXR not initialized, please check if your headset is connected") - - # Configurazione SubViewport - $SubViewport.size = Vector2(320, 240) # Risoluzione tipica retro - $SubViewport/TextureRect.expand = true - $SubViewport/TextureRect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + + # Carica lo script dell'emulatore + emulator_script = preload("res://scripts/emulate.gd").new() + +func launch_game(): + var core_path = "res://cores/genesis_plus_gx_libretro.so" + var rom_path = "res://roms/megadrive/Sonic the Hedgehog.bin" + + if emulator_script.start_game(core_path, rom_path): + print("Il gioco è stato avviato con successo!") + else: + print("Errore durante l'avvio del gioco.") diff --git a/project.godot b/project.godot index 41345cc..fd3e7a5 100644 --- a/project.godot +++ b/project.godot @@ -25,6 +25,10 @@ XrSimulator="*res://addons/xr-simulator/XRSimulator.tscn" enabled=PackedStringArray("res://addons/godot-xr-tools/plugin.cfg") +[gd_extension] + +extensions=PackedStringArray("res://libretro_binding/libretro_binding.gdextension") + [rendering] renderer/rendering_method="gl_compatibility" diff --git a/scripts/emulate.gd b/scripts/emulate.gd new file mode 100644 index 0000000..08ee031 --- /dev/null +++ b/scripts/emulate.gd @@ -0,0 +1,46 @@ +extends Node + +var libretro_core = LibretroCoreBinding.new() # Use the class directly + +func start_game(core_path: String, rom_path: String) -> bool: + """ + Inizializza il core libretro e carica la ROM specificata. + Restituisce true se il gioco è stato caricato correttamente, altrimenti false. + """ + # Carica il gestore libretro + var libretro_handler = preload("res://scripts/libretro_loader.gd").new() + + # Inizializzazione del core + if not libretro_handler.initialize(core_path): + print("Errore: impossibile caricare il core libretro:", core_path) + return false + + print("Core caricato con successo:", core_path) + + # Caricamento della ROM + var rom_data: PackedByteArray = load_rom_data(rom_path) + if not libretro_handler.load_game(rom_data): + print("Errore: impossibile caricare la ROM:", rom_path) + return false + + print("Gioco caricato con successo:", rom_path) + return true + +func load_rom_data(rom_path: String) -> PackedByteArray: + """ + Carica i dati di una ROM da file. + """ + var file = FileAccess.open(rom_path, FileAccess.READ) + if not file: + push_error("Error opening file: " + rom_path) + return PackedByteArray() + + var data = file.get_buffer(file.get_length()) + file.close() + return data + +func initialize_core(core_path: String) -> bool: + if not libretro_core.initialize(core_path): + print("Errore: impossibile caricare il core libretro") + return false + return true diff --git a/scripts/libretro_loader.gd b/scripts/libretro_loader.gd index 4f5c5a3..d0b7557 100644 --- a/scripts/libretro_loader.gd +++ b/scripts/libretro_loader.gd @@ -1,91 +1,57 @@ -# libretro_loader.gd extends Node -# Configurazione SubViewport TV @onready var tv_viewport: SubViewport = $CRTTV/SubViewport @onready var tv_texture_rect: TextureRect = $CRTTV/SubViewport/TextureRect -# Stato dell'emulazione var current_core = null var current_rom = null -# Funzione per caricare un core -func load_libretro_core(core_path: String): - # Caricamento dinamico del core +func load_libretro_core(core_path: String) -> bool: current_core = load(core_path) - if not current_core: - push_error("Impossibile caricare il core: " + core_path) + push_error("Failed to load core: " + core_path) return false - - # Inizializzazione base del core current_core.initialize() return true -# Funzione per caricare una ROM -func load_rom(rom_path: String): - # Verifica esistenza ROM +func load_rom(rom_path: String) -> bool: if not FileAccess.file_exists(rom_path): - push_error("ROM non trovata: " + rom_path) + push_error("ROM not found: " + rom_path) return false - # Caricamento dati ROM - var rom_data = FileAccess.get_file_as_bytes(rom_path) + var file = FileAccess.open(rom_path, FileAccess.READ) + if not file: + push_error("Error opening ROM: " + rom_path) + return false - # Caricamento ROM nel core - var load_result = current_core.load_game(rom_data) + var rom_data = file.get_buffer(file.get_length()) + file.close() - if load_result: - current_rom = rom_path + if not current_core.load_game(rom_data): + push_error("Failed to load ROM: " + rom_path) + return false - return load_result + current_rom = rom_path + return true - # Funzione per avviare l'emulazione - func start_emulation(core_path: String, rom_path: String): - # Caricamento core - if not load_libretro_core(core_path): - return false +func start_emulation(core_path: String, rom_path: String) -> bool: + if not load_libretro_core(core_path): + return false + if not load_rom(rom_path): + return false + current_core.run() + return true - # Caricamento ROM - if not load_rom(rom_path): - return false - - # Avvio emulazione - current_core.run() - return true - - # Processo di rendering - func _process(delta): - if current_core: - # Recupero frame corrente - var frame_data = current_core.get_frame_buffer() - - if frame_data: - # Conversione in texture - var image = Image.create_from_data( - current_core.get_frame_width(), - current_core.get_frame_height(), - false, - Image.FORMAT_RGB8, - frame_data - ) - - var texture = ImageTexture.create_from_image(image) - - # Applicazione texture alla TV - tv_texture_rect.texture = texture - - # Esempio di utilizzo - func _ready(): - # Percorsi di esempio - var CORE_PATH = "res://cores/genesis_plus_gx_libretro.so" - var ROM_PATH = "res://roms/megadrive/Sonic.bin" - - # Avvio emulazione - start_emulation(CORE_PATH, ROM_PATH) - - # Gestione input - func _input(event): - if current_core: - # Conversione input Godot in input Libretro - current_core.handle_input(event) +func _process(delta): + if current_core: + var frame_data = current_core.get_frame_buffer() + if frame_data: + var image = Image.create_from_data( + current_core.get_frame_width(), + current_core.get_frame_height(), + false, + Image.FORMAT_RGB8, + frame_data + ) + var texture = ImageTexture.create_from_image(image) + tv_texture_rect.texture = texture