Some advancements but the singleton makes the game crash...

This commit is contained in:
XargonWan 2025-01-05 15:24:02 +09:00
parent d8ce8263b9
commit b479e1636f
17 changed files with 131 additions and 154 deletions

View file

@ -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"

Binary file not shown.

View file

@ -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

View file

@ -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 \

View file

@ -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

View file

@ -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;
}

View file

@ -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 <fstream>
// 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;
}
}

View file

@ -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;
}

View file

@ -34,6 +34,7 @@ public:
void unload_core();
void run();
void forwarded_input(const godot::Ref<godot::InputEvent> &event);
bool load_game(const godot::Dictionary &game_info);
private:
static RetroHost *singleton;
@ -120,4 +121,4 @@ private:
protected:
static void _bind_methods();
};
};

View file

@ -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,

54
main.gd
View file

@ -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.")

View file

@ -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: