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:
2026-04-09 21:34:49 -04:00
parent 547d1501c0
commit d502304d95
12 changed files with 353 additions and 23 deletions

View File

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

View File

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

View File

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