Checkpoint

This commit is contained in:
XargonWan 2024-12-13 12:19:48 +09:00
parent 6f7436e8d1
commit c0d9fbd31f
21 changed files with 1373 additions and 299 deletions

5
.gitignore vendored
View file

@ -1,12 +1,9 @@
# Godot 4+ specific ignores
.godot/
/android/
addons/gdretroplay/build_linux
roms
cores
assets
godot-cpp
.sconsign.dblite
libretro_binding/libretro_binding.os
libretro_binding/libretro_binding.so
.sconsign.dblite
gdlibretro

3
.gitmodules vendored
View file

@ -1,3 +0,0 @@
[submodule "external/gdlibretro"]
path = external/gdlibretro
url = https://github.com/gabrielmedici/gdlibretro

Binary file not shown.

76
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,76 @@
{
"files.associations": {
"any": "cpp",
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"cctype": "cpp",
"charconv": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"codecvt": "cpp",
"compare": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"forward_list": "cpp",
"list": "cpp",
"map": "cpp",
"set": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"ratio": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"format": "cpp",
"fstream": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"semaphore": "cpp",
"span": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stdfloat": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"text_encoding": "cpp",
"thread": "cpp",
"cinttypes": "cpp",
"typeinfo": "cpp",
"valarray": "cpp",
"variant": "cpp"
}
}

View file

@ -0,0 +1,10 @@
[configuration]
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"

BIN
addons/libLibRetroHost-d.so Executable file

Binary file not shown.

9
build_gdlibretro.sh Executable file
View file

@ -0,0 +1,9 @@
#!/bin/bash
git clone https://github.com/gabrielmedici/gdlibretro
cd gdlibretro
git submodule update --init --recursive
cmake -DNO_GIT_REVISION=ON -DCMAKE_BUILD_TYPE=Debug -DLINUX=true -DCMAKE_CXX_FLAGS="-DLINUX" .
cmake --build .
cd -
mv -fv "gdlibretro/LibRetroHost/lib/Linux-x86_64/libLibRetroHost-d.so" "addons"

View file

@ -1,9 +0,0 @@
#!/bin/bash
if [ ! -d "godot-cpp" ]; then git clone https://github.com/godotengine/godot-cpp.git; fi
cd godot-cpp
git pull
git submodule update --init --recursive
scons platform=linux generate_bindings=yes -j$(nproc)
cd ..
scons platform=linux -j$(nproc)

1
external/gdlibretro vendored

@ -1 +0,0 @@
Subproject commit 7ebbf840187f54466988c0b5b20524ad21ca1d58

View file

@ -1,16 +0,0 @@
Import('env')
# Configurazione di compilazione
env_shared = env.Clone()
env_shared.Prepend(CPPPATH=[
'#godot-cpp/include',
'#godot-cpp/include/core',
'#godot-cpp/include/gen',
'#godot-cpp/gen/include',
'#godot-cpp/gdextension',
'#godot-cpp/include/godot_cpp'
])
env_shared.Append(LIBS=['dl']) # Libreria per caricamento dinamico
# Compilazione del modulo come libreria condivisa
env_shared.SharedLibrary(target='libretro_binding', source=['libretro_binding.cpp'])

View file

@ -1,93 +0,0 @@
#include "libretro_binding.h"
#include <dlfcn.h>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#include <fstream>
using namespace godot;
bool LibretroCoreBinding::initialize(const String &core_path) {
UtilityFunctions::print("Loading core: ", core_path);
core_handle = dlopen(core_path.utf8().get_data(), RTLD_LAZY);
if (!core_handle) {
UtilityFunctions::print("Error loading core: ", dlerror());
return false;
}
// Link functions
retro_init = (retro_init_t)dlsym(core_handle, "retro_init");
retro_deinit = (retro_deinit_t)dlsym(core_handle, "retro_deinit");
retro_load_game = (retro_load_game_t)dlsym(core_handle, "retro_load_game");
retro_run = (retro_run_t)dlsym(core_handle, "retro_run");
retro_get_video_frame = (retro_get_video_frame_t)dlsym(core_handle, "retro_get_video_frame");
if (!retro_init || !retro_deinit || !retro_load_game || !retro_run || !retro_get_video_frame) {
UtilityFunctions::print("Error: Missing functions in core.");
dlclose(core_handle);
core_handle = nullptr;
return false;
}
retro_init();
UtilityFunctions::print("Core successfully initialized.");
return true;
}
bool LibretroCoreBinding::load_game(const PackedByteArray &rom_data) {
if (!core_handle) {
UtilityFunctions::print("Error: Core not loaded.");
return false;
}
// Save ROM to a temporary file
std::string temp_rom_path = "/tmp/libretro_temp.rom";
std::ofstream temp_file(temp_rom_path, std::ios::binary);
if (!temp_file.is_open()) {
UtilityFunctions::print("Error: Unable to open temporary ROM file.");
return false;
}
// Write the ROM data to the file
temp_file.write(reinterpret_cast<const char *>(rom_data.ptr()), rom_data.size());
temp_file.close();
bool result = retro_load_game(temp_rom_path.c_str());
if (result) {
UtilityFunctions::print("ROM successfully loaded: ", temp_rom_path.c_str());
} else {
UtilityFunctions::print("Error: Failed to load ROM.");
}
return result;
}
void LibretroCoreBinding::run() {
if (!core_handle || !retro_run) {
UtilityFunctions::print("Error: Core not initialized.");
return;
}
retro_run();
}
PackedByteArray LibretroCoreBinding::get_frame_buffer() {
if (!core_handle || !retro_get_video_frame) {
UtilityFunctions::print("Error: Core not initialized.");
return PackedByteArray();
}
void *frame = nullptr;
retro_get_video_frame(&frame, &frame_width, &frame_height);
frame_buffer.resize(frame_width * frame_height * 4); // Assume RGBA format
memcpy(frame_buffer.ptrw(), frame, frame_buffer.size());
return frame_buffer;
}
int LibretroCoreBinding::get_frame_width() const {
return frame_width;
}
int LibretroCoreBinding::get_frame_height() const {
return frame_height;
}

View file

@ -1,6 +0,0 @@
{
"entry_class": "LibretroCoreBinding",
"library_path": "res://libretro_binding/libretro_binding.so",
"singleton": false,
"autoload": false
}

View file

@ -1,41 +0,0 @@
#ifndef LIBRETRO_CORE_BINDING_H
#define LIBRETRO_CORE_BINDING_H
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#include <godot_cpp/variant/packed_byte_array.hpp>
#include <godot_cpp/classes/ref.hpp>
#include <godot_cpp/classes/object.hpp> // Correct base class
// Typedefs for libretro core functions
typedef void (*retro_init_t)();
typedef void (*retro_deinit_t)();
typedef bool (*retro_load_game_t)(const char *);
typedef void (*retro_run_t)();
typedef void (*retro_get_video_frame_t)(void **, unsigned *, unsigned *);
class LibretroCoreBinding : public godot::Object {
GDCLASS(LibretroCoreBinding, godot::Object);
private:
void *core_handle = nullptr;
retro_init_t retro_init = nullptr;
retro_deinit_t retro_deinit = nullptr;
retro_load_game_t retro_load_game = nullptr;
retro_run_t retro_run = nullptr;
retro_get_video_frame_t retro_get_video_frame = nullptr;
unsigned frame_width = 0;
unsigned frame_height = 0;
godot::PackedByteArray frame_buffer;
public:
bool initialize(const godot::String &core_path);
bool load_game(const godot::PackedByteArray &rom_data);
void run();
godot::PackedByteArray get_frame_buffer();
int get_frame_width() const;
int get_frame_height() const;
};
#endif // LIBRETRO_CORE_BINDING_H

View file

@ -1,9 +0,0 @@
#include "libretro_binding.h"
void register_libretro_core_binding_types() {
ClassDB::register_class<LibretroCoreBinding>();
}
void unregister_libretro_core_binding_types() {
// Non necessario per ora
}

40
main.gd
View file

@ -1,26 +1,26 @@
extends Node3D
extends Node
var xr_interface: XRInterface
var emulator_script: Object
@onready var loader = preload("res://scripts/libretro_loader.gd").new()
func _ready():
# Inizializzazione OpenXR
xr_interface = XRServer.find_interface("OpenXR")
if xr_interface and xr_interface.is_initialized():
print("OpenXR initialized successfully")
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)
get_viewport().use_xr = true
var core_path = "res://cores/genesis_plus_gx_libretro.so" # Replace with your actual core path
var rom_path = "res://roms/megadrive/Sonic the Hedgehog.bin" # Replace with your actual ROM path
print("Core path: ", core_path)
print("ROM path: ", rom_path)
var success = await loader.start_emulation(core_path, rom_path) # Use await to call the coroutine
if success:
print("Game started successfully.")
start_emulation_loop()
else:
print("OpenXR not initialized, please check if your headset is connected")
print("Failed to start the game.")
# Carica lo script dell'emulatore
emulator_script = preload("res://scripts/emulate.gd").new()
func start_emulation_loop():
"""
Continuously runs the emulation in the `_process` callback.
"""
set_process(true)
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.")
func _process(delta):
loader._process(delta) # Delegate the frame updates to the loader

View file

@ -27,7 +27,7 @@ enabled=PackedStringArray("res://addons/godot-xr-tools/plugin.cfg")
[gd_extension]
extensions=PackedStringArray("res://libretro_binding/libretro_binding.gdextension")
extensions=PackedStringArray("res://addons/LibRetroHost.gdextension")
[rendering]

1060
prompt.md Normal file

File diff suppressed because it is too large Load diff

51
prompt.sh Executable file
View file

@ -0,0 +1,51 @@
clear
rm "prompt.md"
write(){
echo "$1" >> ./prompt.md
}
write "RetroQUEST is an application mainly written for Meta Quest 3 with Linux non-VR support in Godot 4.3.
In the future will be added supporto for Widnows, Mac and other VRs as well.
The app is working mainly with VR controls but can be used even with desktop controls (mouse, keyboard, controllers).
The app is Quest Native and not PCVR."
write "The scope of this application is to emulate games and render them on a virtual CRT TV in the VR space, more features will be added later."
write "The first iteration will be to be able to run the rom "roms/megadrive/Sonic the Hedgehog.bin" with the libretro core "cores/genesis_plus_gx_libretro.so", more features will be added later, for now I will be happy to see the game just running."
write "For this scope I am using gdlibretro (https://github.com/gabrielmedici/gdlibretro) with some modifications to make it work on Linux and Android."
write "Here is my directory structure"
write "$(tree . -L 2)"
write "Now I will cat you the main files for you to understand better the project."
files="project.godot main.tscn scripts/emulate.gd scripts/libretro_loader.gd addons/gdlibretro.gdextension addons/LibRetroHost.gdextension gdlibretro/src/CoreEnvironment.cpp gdlibretro/src/RetroHost.cpp gdlibretro/src/RetroHost.hpp gdlibretro/src/Video.cpp gdlibretro/src/RegisterExtension.cpp gdlibretro/src/CMakeLists.txt"
for file in $files; do
if [ -f "$file" ]; then
write "--- $file ---"
write "$(cat $file)"
write "--- end of $file ---"
write ""
fi
done
write ""
write "My issue now is the following, can you help me to fix it?"
write "Godot says:
[RetroHost] Constructor
core/extension/gdextension.cpp:1011 - No GDExtension library found for current OS and architecture (linux.x86_64) in configuration file: res://gdlibretro/LibRetroHost/LibRetroHost.gdextension
Failed loading resource: res://gdlibretro/LibRetroHost/LibRetroHost.gdextension. Make sure resources have been imported by opening the project in the editor at least once.
Core path: res://cores/genesis_plus_gx_libretro.so
ROM path: res://roms/megadrive/Sonic the Hedgehog.bin
Starting emulation with core: res://cores/genesis_plus_gx_libretro.so, ROM: res://roms/megadrive/Sonic the Hedgehog.bin
Loading core...
Failed to start the game.
--- Debugging process stopped ---
core/extension/gdextension.cpp:1011 - No GDExtension library found for current OS and architecture (linux.x86_64) in configuration file: res://gdlibretro/LibRetroHost/LibRetroHost.gdextension
Failed loading resource: res://gdlibretro/LibRetroHost/LibRetroHost.gdextension. Make sure resources have been imported by opening the project in the editor at least once.
core/extension/gdextension.cpp:1011 - No GDExtension library found for current OS and architecture (linux.x86_64) in configuration file: res://gdlibretro/LibRetroHost/LibRetroHost.gdextension
Failed loading resource: res://gdlibretro/LibRetroHost/LibRetroHost.gdextension. Make sure resources have been imported by opening the project in the editor at least once.
"

View file

@ -1,46 +0,0 @@
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

54
scripts/emulate.gd.old Normal file
View file

@ -0,0 +1,54 @@
extends Node
# Reference to the LibRetroHost singleton
@onready var libretro_core = Engine.get_singleton("RetroHost")
func start_game(core_path: String, rom_path: String) -> bool:
"""
Initializes the LibRetro core and loads the specified ROM.
Returns true if the game is loaded successfully, otherwise false.
"""
if not libretro_core.load_core(core_path):
push_error("Error: Unable to load LibRetro core: " + core_path)
return false
print("Core loaded successfully:", core_path)
if not FileAccess.file_exists(rom_path):
push_error("Error: ROM not found: " + rom_path)
return false
var rom_data = load_rom_data(rom_path)
if rom_data.is_empty():
push_error("Error: Unable to load ROM data: " + rom_path)
return false
if not libretro_core.load_game_from_memory(rom_data):
push_error("Error: Unable to load game ROM: " + rom_path)
return false
print("Game loaded successfully:", rom_path)
return true
func load_rom_data(rom_path: String) -> PackedByteArray:
"""
Loads the ROM data from a file.
"""
var file = FileAccess.open(rom_path, FileAccess.READ)
if not file:
push_error("Error: Unable to open ROM file: " + rom_path)
return PackedByteArray()
var rom_data = file.get_buffer(file.get_length())
file.close()
return rom_data
func run():
"""
Starts the emulation loop.
"""
if not libretro_core:
push_error("Error: LibRetroHost is not initialized.")
return
libretro_core.run()

View file

@ -1,57 +1,98 @@
extends Node
@onready var tv_viewport: SubViewport = $CRTTV/SubViewport
@onready var tv_texture_rect: TextureRect = $CRTTV/SubViewport/TextureRect
@onready var sub_viewport = $room/SubViewport
@onready var texture_rect = $room/SubViewport/TextureRect
var current_core = null
var current_rom = null
func load_libretro_core(core_path: String) -> bool:
current_core = load(core_path)
if not current_core:
push_error("Failed to load core: " + core_path)
return false
current_core.initialize()
return true
func load_rom(rom_path: String) -> bool:
if not FileAccess.file_exists(rom_path):
push_error("ROM not found: " + rom_path)
return false
var file = FileAccess.open(rom_path, FileAccess.READ)
if not file:
push_error("Error opening ROM: " + rom_path)
return false
var rom_data = file.get_buffer(file.get_length())
file.close()
if not current_core.load_game(rom_data):
push_error("Failed to load ROM: " + rom_path)
return false
current_rom = rom_path
return true
var current_core : Object = null # The emulator core (passed dynamically)
var current_rom : String = "" # Initialize to an empty string
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
print("Starting emulation with core: ", core_path, ", ROM: ", rom_path)
if not core_path or not rom_path:
push_error("Core path or ROM path is missing.")
return false
# Load the core (emulator)
print("Loading core...")
current_core = load(core_path)
if not current_core:
push_error("Failed to load core: " + core_path)
return false
print("Core loaded successfully.")
current_core.initialize()
# Load the ROM
print("Checking ROM file...")
if not FileAccess.file_exists(rom_path):
push_error("ROM not found: " + rom_path)
return false
print("ROM file exists.")
var file = FileAccess.open(rom_path, FileAccess.READ)
if not file:
push_error("Error opening ROM: " + rom_path)
return false
print("ROM file opened successfully.")
var rom_data = file.get_buffer(file.get_length())
file.close()
if not current_core.load_game(rom_data):
push_error("Failed to load ROM: " + rom_path)
return false
print("ROM loaded successfully.")
current_rom = rom_path
print("Core and ROM loaded successfully.")
# Wait for SubViewport to render at least one frame
sub_viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
await get_tree().create_timer(0.1).timeout # Delay ensures rendering starts
# Assign the SubViewport texture
var viewport_texture = sub_viewport.get_texture()
if viewport_texture:
texture_rect.texture = viewport_texture
print("SubViewport texture assigned successfully.")
else:
push_error("Error: SubViewport texture is not ready.")
return false
return true
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
"""
Dynamically updates the TextureRect's texture during runtime.
"""
if current_core:
current_core.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
func _ready():
"""
Initializes the SubViewport and TextureRect.
"""
if sub_viewport and texture_rect:
print("SubViewport and TextureRect initialized successfully.")
else:
push_error("Error: SubViewport or TextureRect is missing.")
# Ensure SubViewport always renders
sub_viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS