Add NES emulation support with bundled FCEUmm core
Implement NesCore with FCEUmm/Nestopia/Mesen libretro core support. Generalize native frontend to handle cores that require ROM data in memory (need_fullpath=false) and move PS1-specific config out of C++ into Ps1Core. Bundle both core .so files as APK assets with automatic extraction. Fix cover art fetching for GoodNES-named ROMs by expanding region codes (U)->(USA, Europe) to match LibRetro thumbnail naming.
This commit is contained in:
@@ -121,4 +121,18 @@ Java_com_lazy_emulate_emulation_NativeLibretro_nativeSetSaveDir(JNIEnv* env, job
|
||||
env->ReleaseStringUTFChars(dir, d);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_lazy_emulate_emulation_NativeLibretro_nativeSetControllerPortDevice(JNIEnv*, jobject, jint port, jint device) {
|
||||
LibretroFrontend::instance().setControllerPortDevice(static_cast<unsigned>(port), static_cast<unsigned>(device));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_lazy_emulate_emulation_NativeLibretro_nativeSetCoreOption(JNIEnv* env, jobject, jstring key, jstring value) {
|
||||
const char* k = env->GetStringUTFChars(key, nullptr);
|
||||
const char* v = env->GetStringUTFChars(value, nullptr);
|
||||
LibretroFrontend::instance().setCoreOption(k, v);
|
||||
env->ReleaseStringUTFChars(value, v);
|
||||
env->ReleaseStringUTFChars(key, k);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -74,20 +74,14 @@ bool LibretroFrontend::loadCore(const char* soPath) {
|
||||
core_set_input_poll_(inputPollCallback);
|
||||
core_set_input_state_(inputStateCallback);
|
||||
|
||||
// Set controller to analog pad (DualShock) for PS1
|
||||
core_set_controller_port_device_(0, RETRO_DEVICE_ANALOG);
|
||||
|
||||
retro_system_info sys_info{};
|
||||
core_get_system_info_(&sys_info);
|
||||
need_fullpath_ = sys_info.need_fullpath;
|
||||
LOGI("Core loaded: %s %s", sys_info.library_name, sys_info.library_version);
|
||||
LOGI(" need_fullpath: %d, block_extract: %d", sys_info.need_fullpath, sys_info.block_extract);
|
||||
|
||||
// Set default core options for pcsx_rearmed
|
||||
core_options_["pcsx_rearmed_drc"] = "enabled";
|
||||
core_options_["pcsx_rearmed_pad1type"] = "analog";
|
||||
core_options_["pcsx_rearmed_frameskip_type"] = "disabled";
|
||||
core_options_["pcsx_rearmed_region"] = "auto";
|
||||
core_options_["pcsx_rearmed_memcard2"] = "disabled";
|
||||
// Clear any previous core options
|
||||
core_options_.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -100,13 +94,29 @@ bool LibretroFrontend::loadGame(const char* romPath) {
|
||||
|
||||
LOGI("Loading game: %s", romPath);
|
||||
|
||||
// pcsx_rearmed uses need_fullpath=true, so we just pass the path
|
||||
retro_game_info game_info{};
|
||||
game_info.path = romPath;
|
||||
game_info.data = nullptr;
|
||||
game_info.size = 0;
|
||||
game_info.meta = nullptr;
|
||||
|
||||
if (need_fullpath_) {
|
||||
game_info.data = nullptr;
|
||||
game_info.size = 0;
|
||||
} else {
|
||||
// Core requires ROM data in memory
|
||||
std::ifstream rom_file(romPath, std::ios::binary | std::ios::ate);
|
||||
if (!rom_file) {
|
||||
LOGE("Failed to open ROM file: %s", romPath);
|
||||
return false;
|
||||
}
|
||||
size_t rom_size = rom_file.tellg();
|
||||
rom_file.seekg(0);
|
||||
rom_data_.resize(rom_size);
|
||||
rom_file.read(reinterpret_cast<char*>(rom_data_.data()), rom_size);
|
||||
game_info.data = rom_data_.data();
|
||||
game_info.size = rom_size;
|
||||
LOGI("ROM loaded into memory: %zu bytes", rom_size);
|
||||
}
|
||||
|
||||
if (!core_load_game_(&game_info)) {
|
||||
LOGE("retro_load_game failed");
|
||||
return false;
|
||||
@@ -187,6 +197,8 @@ void LibretroFrontend::stop() {
|
||||
game_loaded_.store(false);
|
||||
}
|
||||
|
||||
rom_data_.clear();
|
||||
rom_data_.shrink_to_fit();
|
||||
audio_engine_.stop();
|
||||
LOGI("Emulation stopped");
|
||||
}
|
||||
@@ -641,12 +653,6 @@ void LibretroFrontend::setDisplayMode(int mode) {
|
||||
|
||||
void LibretroFrontend::setFrameSkip(int skip) {
|
||||
frame_skip_ = skip;
|
||||
if (skip > 0) {
|
||||
core_options_["pcsx_rearmed_frameskip_type"] = "auto";
|
||||
core_options_["pcsx_rearmed_frameskip_threshold"] = "33";
|
||||
} else {
|
||||
core_options_["pcsx_rearmed_frameskip_type"] = "disabled";
|
||||
}
|
||||
options_updated_.store(true);
|
||||
}
|
||||
|
||||
@@ -657,3 +663,15 @@ void LibretroFrontend::setSystemDir(const char* dir) {
|
||||
void LibretroFrontend::setSaveDir(const char* dir) {
|
||||
save_dir_ = dir;
|
||||
}
|
||||
|
||||
void LibretroFrontend::setControllerPortDevice(unsigned port, unsigned device) {
|
||||
if (core_handle_ && core_set_controller_port_device_) {
|
||||
core_set_controller_port_device_(port, device);
|
||||
LOGI("Controller port %u set to device %u", port, device);
|
||||
}
|
||||
}
|
||||
|
||||
void LibretroFrontend::setCoreOption(const char* key, const char* value) {
|
||||
core_options_[key] = value;
|
||||
options_updated_.store(true);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
enum class DisplayMode : int {
|
||||
STRETCH = 0,
|
||||
@@ -43,6 +44,8 @@ public:
|
||||
|
||||
void setSystemDir(const char* dir);
|
||||
void setSaveDir(const char* dir);
|
||||
void setControllerPortDevice(unsigned port, unsigned device);
|
||||
void setCoreOption(const char* key, const char* value);
|
||||
|
||||
bool isRunning() const { return running_.load(); }
|
||||
|
||||
@@ -114,6 +117,10 @@ private:
|
||||
std::string system_dir_;
|
||||
std::string save_dir_;
|
||||
|
||||
// ROM data (kept alive for cores with need_fullpath=false)
|
||||
std::vector<uint8_t> rom_data_;
|
||||
bool need_fullpath_ = true;
|
||||
|
||||
// Frame timing
|
||||
double target_fps_ = 60.0;
|
||||
int frame_skip_ = 0;
|
||||
|
||||
Reference in New Issue
Block a user