diff --git a/addons/LibRetroHost.gdextension b/addons/LibRetroHost.gdextension index b2f2beb..07f152d 100644 --- a/addons/LibRetroHost.gdextension +++ b/addons/LibRetroHost.gdextension @@ -4,7 +4,7 @@ entry_symbol = "GDExtensionInit" compatibility_minimum = 4.1 [libraries] -windows.release.x86_64 = "lib/Windows-AMD64/LibRetroHost-d.dll" -windows.debug.x86_64 = "lib/Windows-AMD64/LibRetroHost-d.dll" linux.release.x86_64 = "res://addons/libLibRetroHost-d.so" linux.debug.x86_64 = "res://addons/libLibRetroHost-d.so" +windows.release.x86_64 = "lib/Windows-AMD64/LibRetroHost-d.dll" +windows.debug.x86_64 = "lib/Windows-AMD64/LibRetroHost-d.dll" \ No newline at end of file diff --git a/addons/libLibRetroHost-d.so b/addons/libLibRetroHost-d.so index e75170c..666cf23 100755 Binary files a/addons/libLibRetroHost-d.so and b/addons/libLibRetroHost-d.so differ diff --git a/gdlibretro/CMakeFiles/LibRetroHost.dir/compiler_depend.internal b/gdlibretro/CMakeFiles/LibRetroHost.dir/compiler_depend.internal index 1520111..e383680 100644 --- a/gdlibretro/CMakeFiles/LibRetroHost.dir/compiler_depend.internal +++ b/gdlibretro/CMakeFiles/LibRetroHost.dir/compiler_depend.internal @@ -1829,6 +1829,7 @@ CMakeFiles/LibRetroHost.dir/src/RegisterExtension.cpp.o /usr/include/c++/14/bits/fs_fwd.h /usr/include/c++/14/bits/fs_ops.h /usr/include/c++/14/bits/fs_path.h + /usr/include/c++/14/bits/fstream.tcc /usr/include/c++/14/bits/functexcept.h /usr/include/c++/14/bits/functional_hash.h /usr/include/c++/14/bits/gslice.h @@ -1937,6 +1938,7 @@ CMakeFiles/LibRetroHost.dir/src/RegisterExtension.cpp.o /usr/include/c++/14/ext/string_conversions.h /usr/include/c++/14/ext/type_traits.h /usr/include/c++/14/filesystem + /usr/include/c++/14/fstream /usr/include/c++/14/initializer_list /usr/include/c++/14/iomanip /usr/include/c++/14/ios @@ -2084,8 +2086,10 @@ CMakeFiles/LibRetroHost.dir/src/RegisterExtension.cpp.o /usr/include/x86_64-linux-gnu/bits/wordsize.h /usr/include/x86_64-linux-gnu/bits/xopen_lim.h /usr/include/x86_64-linux-gnu/c++/14/bits/atomic_word.h + /usr/include/x86_64-linux-gnu/c++/14/bits/basic_file.h /usr/include/x86_64-linux-gnu/c++/14/bits/c++allocator.h /usr/include/x86_64-linux-gnu/c++/14/bits/c++config.h + /usr/include/x86_64-linux-gnu/c++/14/bits/c++io.h /usr/include/x86_64-linux-gnu/c++/14/bits/c++locale.h /usr/include/x86_64-linux-gnu/c++/14/bits/cpu_defines.h /usr/include/x86_64-linux-gnu/c++/14/bits/ctype_base.h diff --git a/gdlibretro/CMakeFiles/LibRetroHost.dir/compiler_depend.make b/gdlibretro/CMakeFiles/LibRetroHost.dir/compiler_depend.make index aad7ca0..88df7fb 100644 --- a/gdlibretro/CMakeFiles/LibRetroHost.dir/compiler_depend.make +++ b/gdlibretro/CMakeFiles/LibRetroHost.dir/compiler_depend.make @@ -1824,6 +1824,7 @@ CMakeFiles/LibRetroHost.dir/src/RegisterExtension.cpp.o: src/RegisterExtension.c /usr/include/c++/14/bits/fs_fwd.h \ /usr/include/c++/14/bits/fs_ops.h \ /usr/include/c++/14/bits/fs_path.h \ + /usr/include/c++/14/bits/fstream.tcc \ /usr/include/c++/14/bits/functexcept.h \ /usr/include/c++/14/bits/functional_hash.h \ /usr/include/c++/14/bits/gslice.h \ @@ -1932,6 +1933,7 @@ CMakeFiles/LibRetroHost.dir/src/RegisterExtension.cpp.o: src/RegisterExtension.c /usr/include/c++/14/ext/string_conversions.h \ /usr/include/c++/14/ext/type_traits.h \ /usr/include/c++/14/filesystem \ + /usr/include/c++/14/fstream \ /usr/include/c++/14/initializer_list \ /usr/include/c++/14/iomanip \ /usr/include/c++/14/ios \ @@ -2079,8 +2081,10 @@ CMakeFiles/LibRetroHost.dir/src/RegisterExtension.cpp.o: src/RegisterExtension.c /usr/include/x86_64-linux-gnu/bits/wordsize.h \ /usr/include/x86_64-linux-gnu/bits/xopen_lim.h \ /usr/include/x86_64-linux-gnu/c++/14/bits/atomic_word.h \ + /usr/include/x86_64-linux-gnu/c++/14/bits/basic_file.h \ /usr/include/x86_64-linux-gnu/c++/14/bits/c++allocator.h \ /usr/include/x86_64-linux-gnu/c++/14/bits/c++config.h \ + /usr/include/x86_64-linux-gnu/c++/14/bits/c++io.h \ /usr/include/x86_64-linux-gnu/c++/14/bits/c++locale.h \ /usr/include/x86_64-linux-gnu/c++/14/bits/cpu_defines.h \ /usr/include/x86_64-linux-gnu/c++/14/bits/ctype_base.h \ diff --git a/gdlibretro/CMakeFiles/LibRetroHost.dir/src/CoreEnvironment.cpp.o b/gdlibretro/CMakeFiles/LibRetroHost.dir/src/CoreEnvironment.cpp.o index c5d6136..e7b92a8 100644 Binary files a/gdlibretro/CMakeFiles/LibRetroHost.dir/src/CoreEnvironment.cpp.o and b/gdlibretro/CMakeFiles/LibRetroHost.dir/src/CoreEnvironment.cpp.o differ diff --git a/gdlibretro/CMakeFiles/LibRetroHost.dir/src/RegisterExtension.cpp.o b/gdlibretro/CMakeFiles/LibRetroHost.dir/src/RegisterExtension.cpp.o index b94f8e0..22c6510 100644 Binary files a/gdlibretro/CMakeFiles/LibRetroHost.dir/src/RegisterExtension.cpp.o and b/gdlibretro/CMakeFiles/LibRetroHost.dir/src/RegisterExtension.cpp.o differ diff --git a/gdlibretro/CMakeFiles/LibRetroHost.dir/src/RegisterExtension.cpp.o.d b/gdlibretro/CMakeFiles/LibRetroHost.dir/src/RegisterExtension.cpp.o.d index a623b70..2665712 100644 --- a/gdlibretro/CMakeFiles/LibRetroHost.dir/src/RegisterExtension.cpp.o.d +++ b/gdlibretro/CMakeFiles/LibRetroHost.dir/src/RegisterExtension.cpp.o.d @@ -355,4 +355,8 @@ CMakeFiles/LibRetroHost.dir/src/RegisterExtension.cpp.o: \ /home/jay/gits/RetroQUEST/RetroQUEST/gdlibretro/extern/yaml-cpp/include/yaml-cpp/node/parse.h \ /home/jay/gits/RetroQUEST/RetroQUEST/gdlibretro/extern/yaml-cpp/include/yaml-cpp/node/emit.h \ /usr/include/dlfcn.h /usr/include/x86_64-linux-gnu/bits/dlfcn.h \ - /usr/include/x86_64-linux-gnu/bits/dl_find_object.h + /usr/include/x86_64-linux-gnu/bits/dl_find_object.h \ + /usr/include/c++/14/fstream \ + /usr/include/x86_64-linux-gnu/c++/14/bits/basic_file.h \ + /usr/include/x86_64-linux-gnu/c++/14/bits/c++io.h \ + /usr/include/c++/14/bits/fstream.tcc diff --git a/gdlibretro/CMakeFiles/LibRetroHost.dir/src/RetroHost.cpp.o b/gdlibretro/CMakeFiles/LibRetroHost.dir/src/RetroHost.cpp.o index 61d5e23..d56426c 100644 Binary files a/gdlibretro/CMakeFiles/LibRetroHost.dir/src/RetroHost.cpp.o and b/gdlibretro/CMakeFiles/LibRetroHost.dir/src/RetroHost.cpp.o differ diff --git a/gdlibretro/CMakeFiles/LibRetroHost.dir/src/VFS.cpp.o b/gdlibretro/CMakeFiles/LibRetroHost.dir/src/VFS.cpp.o index 276da5e..76a9497 100644 Binary files a/gdlibretro/CMakeFiles/LibRetroHost.dir/src/VFS.cpp.o and b/gdlibretro/CMakeFiles/LibRetroHost.dir/src/VFS.cpp.o differ diff --git a/gdlibretro/CMakeFiles/LibRetroHost.dir/src/Video.cpp.o b/gdlibretro/CMakeFiles/LibRetroHost.dir/src/Video.cpp.o index 298f776..4ffc9c4 100644 Binary files a/gdlibretro/CMakeFiles/LibRetroHost.dir/src/Video.cpp.o and b/gdlibretro/CMakeFiles/LibRetroHost.dir/src/Video.cpp.o differ diff --git a/gdlibretro/src/CoreEnvironment.cpp b/gdlibretro/src/CoreEnvironment.cpp index 5bf9fdd..c933c4b 100644 --- a/gdlibretro/src/CoreEnvironment.cpp +++ b/gdlibretro/src/CoreEnvironment.cpp @@ -149,7 +149,7 @@ bool RetroHost::core_environment(unsigned command, void *data){ case RETRO_ENVIRONMENT_GET_LIBRETRO_PATH: { godot::UtilityFunctions::print("[RetroHost] Core requested path"); - *(const char **)data = this->cwd.trim_suffix("/").utf8().get_data(); + *(const char **)data = this->cwd.utf8().get_data(); return true; } diff --git a/gdlibretro/src/RegisterExtension.cpp b/gdlibretro/src/RegisterExtension.cpp index 41e2483..2b0f95e 100644 --- a/gdlibretro/src/RegisterExtension.cpp +++ b/gdlibretro/src/RegisterExtension.cpp @@ -1,12 +1,11 @@ #include "gdextension_interface.h" - #include "godot_cpp/core/class_db.hpp" #include "godot_cpp/core/defs.hpp" #include "godot_cpp/godot.hpp" #include "godot_cpp/classes/engine.hpp" #include "godot_cpp/variant/utility_functions.hpp" - #include "RetroHost.hpp" +#include // Singleton instance pointer static RetroHost *retro_host_singleton = nullptr; @@ -65,13 +64,23 @@ extern "C" { GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) { + std::ofstream log_file("logs/gdextension.log", std::ios::app); + log_file << "[GDExtensionInit] Initializing GDExtension..." << std::endl; + godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization); - // Register the initializer and terminator functions init_obj.register_initializer(initialize_extension); - init_obj.register_terminator(uninitialize_extension); - init_obj.set_minimum_library_initialization_level(godot::MODULE_INITIALIZATION_LEVEL_SCENE); + log_file << "[GDExtensionInit] Registered initializer." << std::endl; - return init_obj.init(); + init_obj.register_terminator(uninitialize_extension); + log_file << "[GDExtensionInit] Registered terminator." << std::endl; + + init_obj.set_minimum_library_initialization_level(godot::MODULE_INITIALIZATION_LEVEL_SCENE); + log_file << "[GDExtensionInit] Set minimum initialization level." << std::endl; + + GDExtensionBool result = init_obj.init(); + log_file << "[GDExtensionInit] Initialization result: " << (result ? "success" : "failure") << std::endl; + + return result; } } diff --git a/gdlibretro/src/RetroHost.cpp b/gdlibretro/src/RetroHost.cpp index 370b068..6a77847 100644 --- a/gdlibretro/src/RetroHost.cpp +++ b/gdlibretro/src/RetroHost.cpp @@ -87,35 +87,48 @@ RetroHost *RetroHost::get_singleton() bool RetroHost::load_core(godot::String name) { + // Unload any previously loaded core this->unload_core(); godot::UtilityFunctions::print("[RetroHost] Starting load_core with name: ", name); + // Ensure the core name has the correct .so extension + if (!name.ends_with(".so")) { + name += ".so"; + godot::UtilityFunctions::print("[RetroHost] Adding .so extension to core name. Updated name: ", name); + } + + // Construct the core library path godot::String lib_path; if (godot::OS::get_singleton()->has_feature("editor")) { + // Editor mode: Use globalized path for the core this->cwd = godot::ProjectSettings::get_singleton()->globalize_path("res://") + "libretro-cores/"; lib_path = cwd + name; godot::UtilityFunctions::print("[RetroHost] Editor mode detected. Core path: ", lib_path); } else { + // Runtime mode: Use the executable's directory this->cwd = godot::OS::get_singleton()->get_executable_path().get_base_dir(); lib_path = cwd + "/" + name; godot::UtilityFunctions::print("[RetroHost] Runtime mode detected. Core path: ", lib_path); } + // Attempt to load the core library #ifdef PLATFORM_WINDOWS this->core.handle = LoadLibrary(lib_path.utf8().get_data()); if (this->core.handle == NULL) { - godot::UtilityFunctions::printerr("[RetroHost] Failed to load core \"", lib_path, "\". Error: ", GetLastErrorAsStr().c_str()); + godot::UtilityFunctions::printerr("[RetroHost] Failed to load core: ", lib_path, + ". Error: ", GetLastErrorAsStr()); return false; } #elif defined(PLATFORM_LINUX) || defined(PLATFORM_ANDROID) this->core.handle = dlopen(lib_path.utf8().get_data(), RTLD_LAZY); if (this->core.handle == nullptr) { - godot::UtilityFunctions::printerr("[RetroHost] Failed to load core \"", lib_path, "\". Error: ", dlerror()); + godot::UtilityFunctions::printerr("[RetroHost] Failed to load core: ", lib_path, + ". Error: ", dlerror()); return false; } #endif - godot::UtilityFunctions::print("[RetroHost] Core library loaded successfully."); + godot::UtilityFunctions::print("[RetroHost] Core library loaded successfully: ", lib_path); // Load RetroArch symbols dynamically load_symbol_return_false_on_err(this->core.handle, this->core.retro_init, retro_init); @@ -131,8 +144,10 @@ bool RetroHost::load_core(godot::String name) godot::UtilityFunctions::print("[RetroHost] All symbols loaded successfully."); + // Initialize core variables and RetroArch core this->core_name = name; this->load_core_variables(); + godot::UtilityFunctions::print("[RetroHost] Core variables loaded."); godot::UtilityFunctions::print("[RetroHost] Initializing core..."); this->core.retro_init(); @@ -179,4 +194,29 @@ void RetroHost::_bind_methods() godot::ClassDB::bind_method(godot::D_METHOD("load_core", "name"), &RetroHost::load_core); godot::ClassDB::bind_method(godot::D_METHOD("unload_core"), &RetroHost::unload_core); godot::ClassDB::bind_method(godot::D_METHOD("run"), &RetroHost::run); + godot::ClassDB::bind_method(godot::D_METHOD("load_game", "game_info"), &RetroHost::load_game); +} + +bool RetroHost::load_game(const godot::Dictionary &game_info) { + if (!this->core.initialized) { + godot::UtilityFunctions::printerr("[RetroHost] Cannot load game. Core not initialized."); + return false; + } + + struct retro_game_info game; + memset(&game, 0, sizeof(game)); + + // Get data and size from the Dictionary + godot::PackedByteArray data = game_info["data"]; + game.data = data.ptr(); + game.size = data.size(); + + godot::UtilityFunctions::print("[RetroHost] Loading game..."); + if (!this->core.retro_load_game(&game)) { + godot::UtilityFunctions::printerr("[RetroHost] Failed to load game."); + return false; + } + + godot::UtilityFunctions::print("[RetroHost] Game loaded successfully."); + return true; } \ No newline at end of file diff --git a/gdlibretro/src/RetroHost.hpp b/gdlibretro/src/RetroHost.hpp index 5d82bef..414ad9b 100644 --- a/gdlibretro/src/RetroHost.hpp +++ b/gdlibretro/src/RetroHost.hpp @@ -34,6 +34,7 @@ public: void unload_core(); void run(); void forwarded_input(const godot::Ref &event); + bool load_game(const godot::Dictionary &game_info); private: static RetroHost *singleton; @@ -120,4 +121,4 @@ private: protected: static void _bind_methods(); -}; \ No newline at end of file +}; diff --git a/gdlibretro/src/Video.cpp b/gdlibretro/src/Video.cpp index a947de1..325d8a1 100644 --- a/gdlibretro/src/Video.cpp +++ b/gdlibretro/src/Video.cpp @@ -4,6 +4,12 @@ void RetroHost::core_video_init(const struct retro_game_geometry *geometry) { + + if (!frame_buffer.is_valid()) { + godot::UtilityFunctions::printerr("[RetroHost] Framebuffer initialization failed."); + return; + } + godot::UtilityFunctions::print("[RetroHost] Video init ", geometry->base_width, " x ", geometry->base_height); this->frame_buffer = godot::Image::create(geometry->base_width, geometry->base_height, false, diff --git a/main.gd b/main.gd index 1e83338..e8c306d 100644 --- a/main.gd +++ b/main.gd @@ -1,38 +1,34 @@ -extends Node - -@onready var loader = preload("res://scripts/libretro_loader.gd").new() +extends Node3D func _ready(): + """ + Main entry point for the application. + """ print("[main] Entering _ready function") - var core_path = "res://cores/genesis_plus_gx_libretro.so" # Replace with your actual core path - if not FileAccess.file_exists(core_path): - push_error("Core not found at: " + core_path) - return false - else: - print("[main] File ", core_path," exists") + if not RetroHost: + push_error("[main] RetroHost singleton not found!") + return - var rom_path = "res://roms/megadrive/Sonic the Hedgehog.bin" # Replace with your actual ROM path - if not FileAccess.file_exists(rom_path): - push_error("[main] Rom not found at: " + rom_path) - return false - else: - print("[main] File ", rom_path," exists") + var rom_path = "res://roms/megadrive/Sonic the Hedgehog.bin" + var core_path = "genesis_plus_gx_libretro" - print("[main] Core path: ", core_path) - print("[main] ROM path: ", rom_path) - - var success = await loader.start_emulation(core_path, rom_path) # Use await to call the coroutine + print("[main] Starting emulation with core: ", core_path, ", ROM: ", rom_path) + var success = RetroHost.load_core(core_path) if success: - print("[main] Game started successfully") - start_emulation_loop() + print("[main] Core loaded successfully.") else: - print("[main] Failed to start the game") + print("[main] Failed to load core.") + return -func start_emulation_loop(): - """ - Continuously runs the emulation in the `_process` callback. - """ - set_process(true) + var rom_file = FileAccess.open(rom_path, FileAccess.READ) + if not rom_file: + print("[main] Failed to open ROM file.") + return -func _process(delta): - loader._process(delta) # Delegate the frame updates to the loader + var rom_data = rom_file.get_buffer(rom_file.get_length()) + rom_file.close() + + if RetroHost.load_game(rom_data): + print("[main] ROM loaded successfully.") + else: + print("[main] Failed to load ROM.") diff --git a/scripts/libretro_loader.gd b/scripts/libretro_loader.gd index fb13aa3..b472711 100644 --- a/scripts/libretro_loader.gd +++ b/scripts/libretro_loader.gd @@ -2,144 +2,57 @@ extends Node @onready var sub_viewport = $room/SubViewport @onready var texture_rect = $room/SubViewport/TextureRect -@onready var retro_host = Engine.get_singleton("RetroHost") # Get the RetroHost singleton -var current_core : Object = null # The emulator core (passed dynamically) -var current_rom : String = "" # Initialize to an empty string +func start_emulation(core_name: String, rom_path: String) -> bool: + print("[libretro_loader] Starting emulation with core: ", core_name, ", ROM: ", rom_path) -func start_emulation(core_path: String, rom_path: String) -> bool: - print("[libretro_loader] Starting emulation with core: ", core_path, ", ROM: ", rom_path) - - # Debug RetroHost access - print("[libretro_loader] RetroHost status: ", retro_host) - - # Check if RetroHost is available - if not retro_host: - var message = "[libretro_loader] RetroHost singleton not found!" - push_error(message) - print(message) - return false - - if not core_path: - var message = "[libretro_loader] Core path is missing." - push_error(message) - print(message) - return false - - if not rom_path: - var message = "[libretro_loader] ROM path is missing." - push_error(message) - print(message) - return false - - # Load the core (via RetroHost) - print("[libretro_loader] Loading core using RetroHost...") - var core_loaded = retro_host.load_core(core_path) - if not core_loaded: - push_error("[libretro_loader] Failed to load core: " + core_path) + # Load the core + if not RetroHost.load_core(core_name): + push_error("[libretro_loader] Failed to load core: " + core_name) return false + print("[libretro_loader] Core path: ", core_name) print("[libretro_loader] Core loaded successfully.") - # Check if ROM file exists - if not FileAccess.file_exists(rom_path): - push_error("[libretro_loader] ROM not found: " + rom_path) - return false - print("[libretro_loader] ROM file exists.") - - # Load the ROM - print("[libretro_loader] Opening ROM...") + # Open the ROM file var file = FileAccess.open(rom_path, FileAccess.READ) if not file: push_error("[libretro_loader] Failed to open ROM: " + rom_path) return false + print("[libretro_loader] ROM path: ", rom_path) var rom_data = file.get_buffer(file.get_length()) file.close() print("[libretro_loader] ROM data loaded successfully.") - # Pass ROM data to the core - if not retro_host.core.load_game(rom_data): - push_error("[libretro_loader] Failed to load ROM into core: " + rom_path) + # Pass ROM data to the core using RetroHost + var game_info = {} + game_info["data"] = rom_data + game_info["size"] = rom_data.size() + + if not RetroHost.load_game(game_info): + push_error("[libretro_loader] Failed to load ROM into the core.") return false - print("[libretro_loader] ROM loaded successfully.") - - current_rom = rom_path print("[libretro_loader] Core and ROM loaded successfully.") - - # Ensure SubViewport is ready - sub_viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS - - # Wait for the SubViewport to initialize (up to 3 seconds) - var max_wait_time = 3.0 # Maximum wait time in seconds - var elapsed_time = 0.0 - while not sub_viewport.get_texture() and elapsed_time < max_wait_time: - await get_tree().idle_frame # Waits for the next frame - elapsed_time += get_process_delta_time() - if not sub_viewport.get_texture(): - push_error("[libretro_loader] Error: SubViewport texture is not ready after waiting.") - return false - - texture_rect.texture = sub_viewport.get_texture() - print("[libretro_loader] SubViewport texture assigned successfully.") return true func _process(delta): """ - Dynamically updates the TextureRect's texture during runtime. + Run the core and update the framebuffer. """ - if current_core: - current_core.run() + RetroHost.run() - var frame_data = current_core.get_frame_buffer() - if frame_data: - var frame_width = current_core.get_frame_width() - var frame_height = current_core.get_frame_height() - - if frame_width > 0 and frame_height > 0: - var image = Image.create_from_data( - frame_width, - frame_height, - false, - Image.FORMAT_RGB8, - frame_data - ) - var texture = ImageTexture.create_from_image(image) - texture_rect.texture = texture + var frame_buffer = RetroHost.get_frame_buffer() + if frame_buffer: + var img_tex = ImageTexture.create_from_image(frame_buffer) + texture_rect.texture = img_tex func _ready(): """ - Initializes the SubViewport and TextureRect and waits for RetroHost singleton. + Initialize and start emulation. """ - print("[libretro_loader] _ready: Initializing SubViewport and TextureRect.") - if sub_viewport and texture_rect: - print("[libretro_loader] SubViewport and TextureRect initialized successfully.") - else: - push_error("[libretro_loader] Error: SubViewport or TextureRect is missing.") - return - - # Ensure SubViewport always renders - sub_viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS - print("[libretro_loader] SubViewport set to always update.") - - print("[libretro_loader] _ready: Waiting for RetroHost singleton...") - - while not Engine.get_singleton("RetroHost"): - print("[libretro_loader] Waiting for RetroHost...") - await get_tree().idle_frame - - print("[libretro_loader] _ready: Wait ended") - - # Assign the RetroHost singleton after it's available - retro_host = Engine.get_singleton("RetroHost") - if retro_host: - print("[libretro_loader] RetroHost singleton initialized successfully.") - else: - push_error("[libretro_loader] Failed to find RetroHost singleton!") - return - - # Proceed with starting emulation - var success = await start_emulation("res://cores/genesis_plus_gx_libretro.so", "res://roms/megadrive/Sonic the Hedgehog.bin") + print("[libretro_loader] _ready: Initializing...") + var success = start_emulation("genesis_plus_gx_libretro", "res://roms/megadrive/Sonic the Hedgehog.bin") if success: print("[libretro_loader] Emulation started successfully.") else: