Initial commit: multi-platform emulator with PS1 support

Jetpack Compose app with adaptive layouts (phone/tablet/foldable) using
Window Size Classes. Integrates a C++ libretro frontend via JNI for
actual PS1 emulation using pcsx_rearmed, with AAudio output and
ANativeWindow rendering.

- Emulation core architecture with interface for NES, SNES, N64,
  Genesis, PS1, PS2 (PS1 implemented, others placeholder)
- Touch controller overlay with D-pad (diagonal support), face buttons,
  shoulders, and analog sticks
- Bluetooth/USB controller detection and input mapping
- Customizable controller layout editor with drag-to-reposition
- Game library with auto-scan of /sdcard/Games/ subfolders
- Auto-import of libretro core .so from /sdcard/Games/cores/
- Save states and SRAM persistence
This commit is contained in:
2026-04-08 20:34:37 -04:00
commit 39e3f8b7c0
67 changed files with 4520 additions and 0 deletions

View File

@@ -0,0 +1,119 @@
#include "libretro_frontend.h"
#include <jni.h>
#include <android/log.h>
#include <android/native_window_jni.h>
#define LOG_TAG "JniBridge"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
extern "C" {
JNIEXPORT jboolean JNICALL
Java_com_lazy_emulate_emulation_NativeLibretro_nativeLoadCore(JNIEnv* env, jobject, jstring corePath) {
const char* path = env->GetStringUTFChars(corePath, nullptr);
bool result = LibretroFrontend::instance().loadCore(path);
env->ReleaseStringUTFChars(corePath, path);
return result;
}
JNIEXPORT jboolean JNICALL
Java_com_lazy_emulate_emulation_NativeLibretro_nativeLoadGame(JNIEnv* env, jobject, jstring romPath) {
const char* path = env->GetStringUTFChars(romPath, nullptr);
bool result = LibretroFrontend::instance().loadGame(path);
env->ReleaseStringUTFChars(romPath, path);
return result;
}
JNIEXPORT void JNICALL
Java_com_lazy_emulate_emulation_NativeLibretro_nativeStart(JNIEnv*, jobject) {
LibretroFrontend::instance().start();
}
JNIEXPORT void JNICALL
Java_com_lazy_emulate_emulation_NativeLibretro_nativePause(JNIEnv*, jobject) {
LibretroFrontend::instance().pause();
}
JNIEXPORT void JNICALL
Java_com_lazy_emulate_emulation_NativeLibretro_nativeResume(JNIEnv*, jobject) {
LibretroFrontend::instance().resume();
}
JNIEXPORT void JNICALL
Java_com_lazy_emulate_emulation_NativeLibretro_nativeStop(JNIEnv*, jobject) {
LibretroFrontend::instance().stop();
}
JNIEXPORT void JNICALL
Java_com_lazy_emulate_emulation_NativeLibretro_nativeReset(JNIEnv*, jobject) {
LibretroFrontend::instance().reset();
}
JNIEXPORT void JNICALL
Java_com_lazy_emulate_emulation_NativeLibretro_nativeSetSurface(JNIEnv* env, jobject, jobject surface) {
if (surface) {
ANativeWindow* window = ANativeWindow_fromSurface(env, surface);
LibretroFrontend::instance().setWindow(window);
ANativeWindow_release(window); // setWindow does its own acquire
} else {
LibretroFrontend::instance().releaseWindow();
}
}
JNIEXPORT void JNICALL
Java_com_lazy_emulate_emulation_NativeLibretro_nativeReleaseSurface(JNIEnv*, jobject) {
LibretroFrontend::instance().releaseWindow();
}
JNIEXPORT void JNICALL
Java_com_lazy_emulate_emulation_NativeLibretro_nativeSetButtonState(JNIEnv*, jobject, jint state) {
LibretroFrontend::instance().setButtonState(static_cast<uint16_t>(state));
}
JNIEXPORT void JNICALL
Java_com_lazy_emulate_emulation_NativeLibretro_nativeSetAnalogState(JNIEnv*, jobject, jint stick, jshort x, jshort y) {
LibretroFrontend::instance().setAnalogState(stick, x, y);
}
JNIEXPORT jboolean JNICALL
Java_com_lazy_emulate_emulation_NativeLibretro_nativeSaveState(JNIEnv* env, jobject, jstring path) {
const char* p = env->GetStringUTFChars(path, nullptr);
bool result = LibretroFrontend::instance().saveState(p);
env->ReleaseStringUTFChars(path, p);
return result;
}
JNIEXPORT jboolean JNICALL
Java_com_lazy_emulate_emulation_NativeLibretro_nativeLoadState(JNIEnv* env, jobject, jstring path) {
const char* p = env->GetStringUTFChars(path, nullptr);
bool result = LibretroFrontend::instance().loadState(p);
env->ReleaseStringUTFChars(path, p);
return result;
}
JNIEXPORT void JNICALL
Java_com_lazy_emulate_emulation_NativeLibretro_nativeSetAudioEnabled(JNIEnv*, jobject, jboolean enabled) {
LibretroFrontend::instance().setAudioEnabled(enabled);
}
JNIEXPORT void JNICALL
Java_com_lazy_emulate_emulation_NativeLibretro_nativeSetFrameSkip(JNIEnv*, jobject, jint skip) {
LibretroFrontend::instance().setFrameSkip(skip);
}
JNIEXPORT void JNICALL
Java_com_lazy_emulate_emulation_NativeLibretro_nativeSetSystemDir(JNIEnv* env, jobject, jstring dir) {
const char* d = env->GetStringUTFChars(dir, nullptr);
LibretroFrontend::instance().setSystemDir(d);
env->ReleaseStringUTFChars(dir, d);
}
JNIEXPORT void JNICALL
Java_com_lazy_emulate_emulation_NativeLibretro_nativeSetSaveDir(JNIEnv* env, jobject, jstring dir) {
const char* d = env->GetStringUTFChars(dir, nullptr);
LibretroFrontend::instance().setSaveDir(d);
env->ReleaseStringUTFChars(dir, d);
}
} // extern "C"