mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-08-07 08:38:43 +00:00
commit
25ba2393db
8 changed files with 324 additions and 7 deletions
|
@ -393,7 +393,7 @@ else()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ANDROID)
|
if(ANDROID)
|
||||||
target_link_libraries(Alber PRIVATE log)
|
target_link_libraries(Alber PRIVATE EGL log)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ENABLE_LTO OR ENABLE_USER_BUILD)
|
if(ENABLE_LTO OR ENABLE_USER_BUILD)
|
||||||
|
|
|
@ -1,11 +1,46 @@
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
#include <stdexcept>
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
#include "renderer_gl/renderer_gl.hpp"
|
||||||
#include "emulator.hpp"
|
#include "emulator.hpp"
|
||||||
|
|
||||||
std::unique_ptr<Emulator> emulator;
|
std::unique_ptr<Emulator> emulator = nullptr;
|
||||||
|
RendererGL* renderer = nullptr;
|
||||||
|
bool romLoaded = false;
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL Java_com_panda3ds_pandroid_AlberDriver_Initialize(JNIEnv* env, jobject obj) {
|
extern "C" JNIEXPORT void JNICALL Java_com_panda3ds_pandroid_AlberDriver_Initialize(JNIEnv* env, jobject obj) {
|
||||||
__android_log_print(ANDROID_LOG_INFO, "Panda3DS", "Initializing Alber Driver");
|
|
||||||
emulator = std::make_unique<Emulator>();
|
emulator = std::make_unique<Emulator>();
|
||||||
__android_log_print(ANDROID_LOG_INFO, "Panda3DS", "Done");
|
if (emulator->getRendererType() != RendererType::OpenGL) {
|
||||||
|
throw std::runtime_error("Renderer is not OpenGL");
|
||||||
|
}
|
||||||
|
renderer = static_cast<RendererGL*>(emulator->getRenderer());
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "AlberDriver", "OpenGL ES Before %d.%d", GLVersion.major, GLVersion.minor);
|
||||||
|
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(eglGetProcAddress))) {
|
||||||
|
throw std::runtime_error("OpenGL ES init failed");
|
||||||
|
}
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "AlberDriver", "OpenGL ES %d.%d", GLVersion.major, GLVersion.minor);
|
||||||
|
emulator->initGraphicsContext(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" JNIEXPORT void JNICALL Java_com_panda3ds_pandroid_AlberDriver_RunFrame(JNIEnv* env, jobject obj, jint fbo) {
|
||||||
|
renderer->setFBO(fbo);
|
||||||
|
renderer->resetStateManager();
|
||||||
|
emulator->runFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" JNIEXPORT void JNICALL Java_com_panda3ds_pandroid_AlberDriver_Finalize(JNIEnv* env, jobject obj) {
|
||||||
|
emulator = nullptr;
|
||||||
|
renderer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" JNIEXPORT jboolean JNICALL Java_com_panda3ds_pandroid_AlberDriver_HasRomLoaded(JNIEnv* env, jobject obj) {
|
||||||
|
return romLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" JNIEXPORT void JNICALL Java_com_panda3ds_pandroid_AlberDriver_LoadRom(JNIEnv* env, jobject obj, jstring path) {
|
||||||
|
const char* pathStr = env->GetStringUTFChars(path, nullptr);
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "AlberDriver", "Loading ROM %s", pathStr);
|
||||||
|
romLoaded = emulator->loadROM(pathStr);
|
||||||
|
env->ReleaseStringUTFChars(path, pathStr);
|
||||||
}
|
}
|
|
@ -22,5 +22,5 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage"/>
|
||||||
</manifest>
|
</manifest>
|
|
@ -2,7 +2,15 @@ package com.panda3ds.pandroid;
|
||||||
|
|
||||||
public class AlberDriver {
|
public class AlberDriver {
|
||||||
|
|
||||||
|
AlberDriver() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
public static native void Initialize();
|
public static native void Initialize();
|
||||||
|
public static native void RunFrame(int fbo);
|
||||||
|
public static native boolean HasRomLoaded();
|
||||||
|
public static native void LoadRom(String path);
|
||||||
|
public static native void Finalize();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
System.loadLibrary("Alber");
|
System.loadLibrary("Alber");
|
||||||
|
|
|
@ -1,16 +1,67 @@
|
||||||
package com.panda3ds.pandroid;
|
package com.panda3ds.pandroid;
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import static android.provider.Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.WindowInsets;
|
||||||
|
import android.view.View;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import com.panda3ds.pandroid.PathUtils;
|
||||||
|
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
PandaGlSurfaceView glView;
|
||||||
|
|
||||||
|
private static final int PICK_3DS_ROM = 2;
|
||||||
|
|
||||||
|
private void openFile() {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
|
intent.setType("*/*");
|
||||||
|
startActivityForResult(intent, PICK_3DS_ROM);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_main);
|
if (!Environment.isExternalStorageManager()) {
|
||||||
|
Intent intent = new Intent(ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
AlberDriver.Initialize();
|
glView = new PandaGlSurfaceView(this);
|
||||||
|
setContentView(glView);
|
||||||
|
FloatingActionButton fab = new FloatingActionButton(this);
|
||||||
|
fab.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
openFile();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(200, 200);
|
||||||
|
addContentView(fab, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
if (requestCode == PICK_3DS_ROM) {
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
String path = PathUtils.getPath(getApplicationContext(), data.getData());
|
||||||
|
Toast.makeText(getApplicationContext(), "pandroid opening " + path, Toast.LENGTH_LONG).show();
|
||||||
|
glView.queueEvent(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
AlberDriver.LoadRom(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
package com.panda3ds.pandroid;
|
||||||
|
|
||||||
|
import javax.microedition.khronos.egl.EGLConfig;
|
||||||
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
|
|
||||||
|
import static android.opengl.GLES32.*;
|
||||||
|
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.opengl.GLSurfaceView;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
|
||||||
|
public class PandaGlRenderer implements GLSurfaceView.Renderer {
|
||||||
|
int screenWidth, screenHeight;
|
||||||
|
int screenTexture;
|
||||||
|
public int screenFbo;
|
||||||
|
|
||||||
|
PandaGlRenderer() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finalize() throws Throwable {
|
||||||
|
if (screenTexture != 0) {
|
||||||
|
glDeleteTextures(1, new int[]{screenTexture}, 0);
|
||||||
|
}
|
||||||
|
if (screenFbo != 0) {
|
||||||
|
glDeleteFramebuffers(1, new int[]{screenFbo}, 0);
|
||||||
|
}
|
||||||
|
super.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
|
||||||
|
Log.i("pandroid", glGetString(GL_EXTENSIONS));
|
||||||
|
Log.w("pandroid", glGetString(GL_VERSION));
|
||||||
|
screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels;
|
||||||
|
screenHeight = Resources.getSystem().getDisplayMetrics().heightPixels;
|
||||||
|
|
||||||
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
int[] generateBuffer = new int[1];
|
||||||
|
glGenTextures(1, generateBuffer, 0);
|
||||||
|
screenTexture = generateBuffer[0];
|
||||||
|
glBindTexture(GL_TEXTURE_2D, screenTexture);
|
||||||
|
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, screenWidth, screenHeight);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
|
glGenFramebuffers(1, generateBuffer, 0);
|
||||||
|
screenFbo = generateBuffer[0];
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, screenFbo);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, screenTexture, 0);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
AlberDriver.Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDrawFrame(GL10 unused) {
|
||||||
|
if (AlberDriver.HasRomLoaded()) {
|
||||||
|
AlberDriver.RunFrame(screenFbo);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, screenFbo);
|
||||||
|
glBlitFramebuffer(0, 0, 400, 480, 0, 0, screenWidth, screenHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onSurfaceChanged(GL10 unused, int width, int height) {
|
||||||
|
glViewport(0, 0, width, height);
|
||||||
|
screenWidth = width;
|
||||||
|
screenHeight = height;
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.panda3ds.pandroid;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.opengl.GLSurfaceView;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
|
||||||
|
import com.panda3ds.pandroid.PandaGlRenderer;
|
||||||
|
|
||||||
|
public class PandaGlSurfaceView extends GLSurfaceView {
|
||||||
|
final PandaGlRenderer renderer;
|
||||||
|
|
||||||
|
public PandaGlSurfaceView(Context context) {
|
||||||
|
super(context);
|
||||||
|
setEGLContextClientVersion(3);
|
||||||
|
renderer = new PandaGlRenderer();
|
||||||
|
setRenderer(renderer);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
package com.panda3ds.pandroid;
|
||||||
|
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
|
||||||
|
public class PathUtils {
|
||||||
|
|
||||||
|
public static String getPath(final Context context, final Uri uri) {
|
||||||
|
|
||||||
|
// DocumentProvider
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, uri)) {
|
||||||
|
|
||||||
|
if (isExternalStorageDocument(uri)) {// ExternalStorageProvider
|
||||||
|
final String docId = DocumentsContract.getDocumentId(uri);
|
||||||
|
final String[] split = docId.split(":");
|
||||||
|
final String type = split[0];
|
||||||
|
String storageDefinition;
|
||||||
|
|
||||||
|
|
||||||
|
if("primary".equalsIgnoreCase(type)){
|
||||||
|
|
||||||
|
return Environment.getExternalStorageDirectory() + "/" + split[1];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if(Environment.isExternalStorageRemovable()){
|
||||||
|
storageDefinition = "EXTERNAL_STORAGE";
|
||||||
|
|
||||||
|
} else{
|
||||||
|
storageDefinition = "SECONDARY_STORAGE";
|
||||||
|
}
|
||||||
|
|
||||||
|
return System.getenv(storageDefinition) + "/" + split[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (isDownloadsDocument(uri)) {// DownloadsProvider
|
||||||
|
|
||||||
|
final String id = DocumentsContract.getDocumentId(uri);
|
||||||
|
final Uri contentUri = ContentUris.withAppendedId(
|
||||||
|
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
|
||||||
|
|
||||||
|
return getDataColumn(context, contentUri, null, null);
|
||||||
|
|
||||||
|
} else if (isMediaDocument(uri)) {// MediaProvider
|
||||||
|
final String docId = DocumentsContract.getDocumentId(uri);
|
||||||
|
final String[] split = docId.split(":");
|
||||||
|
final String type = split[0];
|
||||||
|
|
||||||
|
Uri contentUri = null;
|
||||||
|
if ("image".equals(type)) {
|
||||||
|
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
} else if ("video".equals(type)) {
|
||||||
|
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
} else if ("audio".equals(type)) {
|
||||||
|
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String selection = "_id=?";
|
||||||
|
final String[] selectionArgs = new String[]{
|
||||||
|
split[1]
|
||||||
|
};
|
||||||
|
|
||||||
|
return getDataColumn(context, contentUri, selection, selectionArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if ("content".equalsIgnoreCase(uri.getScheme())) {// MediaStore (and general)
|
||||||
|
|
||||||
|
// Return the remote address
|
||||||
|
if (isGooglePhotosUri(uri))
|
||||||
|
return uri.getLastPathSegment();
|
||||||
|
|
||||||
|
return getDataColumn(context, uri, null, null);
|
||||||
|
|
||||||
|
} else if ("file".equalsIgnoreCase(uri.getScheme())) {// File
|
||||||
|
return uri.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
|
||||||
|
|
||||||
|
Cursor cursor = null;
|
||||||
|
final String column = "_data";
|
||||||
|
final String[] projection = {
|
||||||
|
column
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
final int column_index = cursor.getColumnIndexOrThrow(column);
|
||||||
|
return cursor.getString(column_index);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (cursor != null)
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean isExternalStorageDocument(Uri uri) {
|
||||||
|
return "com.android.externalstorage.documents".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean isDownloadsDocument(Uri uri) {
|
||||||
|
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isMediaDocument(Uri uri) {
|
||||||
|
return "com.android.providers.media.documents".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isGooglePhotosUri(Uri uri) {
|
||||||
|
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue