Setup native interface on app launch

This will allow different entry points to use a common configuration, avoiding duplicate setup logic
This commit is contained in:
Rafael Caetano 2022-09-08 22:26:46 +01:00
parent ee7bafb4ae
commit d69a4e7bdf
12 changed files with 267 additions and 223 deletions

View File

@ -40,6 +40,8 @@ add_library(
SHARED
src/main/cpp/MelonDSAndroidJNI.cpp
src/main/cpp/MelonDSAndroidConfiguration.cpp
src/main/cpp/MelonDSAndroidInterface.cpp
src/main/cpp/UriFileHandler.cpp
src/main/cpp/JniEnvHandler.cpp
)

View File

@ -0,0 +1,192 @@
#include <jni.h>
#include "MelonDS.h"
#include "MelonDSAndroidConfiguration.h"
MelonDSAndroid::EmulatorConfiguration MelonDSAndroidConfiguration::buildEmulatorConfiguration(JNIEnv* env, jobject emulatorConfiguration) {
jclass emulatorConfigurationClass = env->GetObjectClass(emulatorConfiguration);
jclass uriClass = env->FindClass("android/net/Uri");
jclass consoleTypeEnumClass = env->FindClass("me/magnum/melonds/domain/model/ConsoleType");
jclass audioBitrateEnumClass = env->FindClass("me/magnum/melonds/domain/model/AudioBitrate");
jclass audioInterpolationEnumClass = env->FindClass("me/magnum/melonds/domain/model/AudioInterpolation");
jclass audioLatencyEnumClass = env->FindClass("me/magnum/melonds/domain/model/AudioLatency");
jclass micSourceEnumClass = env->FindClass("me/magnum/melonds/domain/model/MicSource");
jmethodID uriToStringMethod = env->GetMethodID(uriClass, "toString", "()Ljava/lang/String;");
jboolean useCustomBios = env->GetBooleanField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "useCustomBios", "Z"));
jobject dsBios7Uri = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "dsBios7Uri", "Landroid/net/Uri;"));
jobject dsBios9Uri = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "dsBios9Uri", "Landroid/net/Uri;"));
jobject dsFirmwareUri = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "dsFirmwareUri", "Landroid/net/Uri;"));
jobject dsiBios7Uri = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "dsiBios7Uri", "Landroid/net/Uri;"));
jobject dsiBios9Uri = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "dsiBios9Uri", "Landroid/net/Uri;"));
jobject dsiFirmwareUri = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "dsiFirmwareUri", "Landroid/net/Uri;"));
jobject dsiNandUri = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "dsiNandUri", "Landroid/net/Uri;"));
jstring internalFilesDir = (jstring) env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "internalDirectory", "Ljava/lang/String;"));
jfloat fastForwardMaxSpeed = env->GetFloatField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "fastForwardSpeedMultiplier", "F"));
jboolean enableRewind = env->GetBooleanField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "rewindEnabled", "Z"));
jint rewindPeriodSeconds = env->GetIntField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "rewindPeriodSeconds", "I"));
jint rewindWindowSeconds = env->GetIntField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "rewindWindowSeconds", "I"));
jboolean useJit = env->GetBooleanField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "useJit", "Z"));
jobject consoleTypeEnum = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "consoleType", "Lme/magnum/melonds/domain/model/ConsoleType;"));
jint consoleType = env->GetIntField(consoleTypeEnum, env->GetFieldID(consoleTypeEnumClass, "consoleType", "I"));
jboolean soundEnabled = env->GetBooleanField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "soundEnabled", "Z"));
jint volume = env->GetIntField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "volume", "I"));
jobject audioInterpolationEnum = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "audioInterpolation", "Lme/magnum/melonds/domain/model/AudioInterpolation;"));
jint audioInterpolation = env->GetIntField(audioInterpolationEnum, env->GetFieldID(audioInterpolationEnumClass, "interpolationValue", "I"));
jobject audioBitrateEnum = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "audioBitrate", "Lme/magnum/melonds/domain/model/AudioBitrate;"));
jint audioBitrate = env->GetIntField(audioBitrateEnum, env->GetFieldID(audioBitrateEnumClass, "bitrateValue", "I"));
jobject audioLatencyEnum = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "audioLatency", "Lme/magnum/melonds/domain/model/AudioLatency;"));
jint audioLatency = env->GetIntField(audioLatencyEnum, env->GetFieldID(audioLatencyEnumClass, "latencyValue", "I"));
jobject micSourceEnum = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "micSource", "Lme/magnum/melonds/domain/model/MicSource;"));
jint micSource = env->GetIntField(micSourceEnum, env->GetFieldID(micSourceEnumClass, "sourceValue", "I"));
jboolean isCopy = JNI_FALSE;
jstring dsBios7String = dsBios7Uri ? (jstring) env->CallObjectMethod(dsBios7Uri, uriToStringMethod) : nullptr;
jstring dsBios9String = dsBios9Uri ? (jstring) env->CallObjectMethod(dsBios9Uri, uriToStringMethod) : nullptr;
jstring dsFirmwareString = dsFirmwareUri ? (jstring) env->CallObjectMethod(dsFirmwareUri, uriToStringMethod) : nullptr;
jstring dsiBios7String = dsiBios7Uri ? (jstring) env->CallObjectMethod(dsiBios7Uri, uriToStringMethod) : nullptr;
jstring dsiBios9String = dsiBios9Uri ? (jstring) env->CallObjectMethod(dsiBios9Uri, uriToStringMethod) : nullptr;
jstring dsiFirmwareString = dsiFirmwareUri ? (jstring) env->CallObjectMethod(dsiFirmwareUri, uriToStringMethod) : nullptr;
jstring dsiNandString = dsiNandUri ? (jstring) env->CallObjectMethod(dsiNandUri, uriToStringMethod) : nullptr;
const char* dsBios7Path = dsBios7Uri ? env->GetStringUTFChars(dsBios7String, &isCopy) : nullptr;
const char* dsBios9Path = dsBios9Uri ? env->GetStringUTFChars(dsBios9String, &isCopy) : nullptr;
const char* dsFirmwarePath = dsFirmwareUri ? env->GetStringUTFChars(dsFirmwareString, &isCopy) : nullptr;
const char* dsiBios7Path = dsiBios7Uri ? env->GetStringUTFChars(dsiBios7String, &isCopy) : nullptr;
const char* dsiBios9Path = dsiBios9Uri ? env->GetStringUTFChars(dsiBios9String, &isCopy) : nullptr;
const char* dsiFirmwarePath = dsiFirmwareUri ? env->GetStringUTFChars(dsiFirmwareString, &isCopy) : nullptr;
const char* dsiNandPath = dsiNandUri ? env->GetStringUTFChars(dsiNandString, &isCopy) : nullptr;
const char* internalDir = env->GetStringUTFChars(internalFilesDir, nullptr);
jobject firmwareConfigurationObject = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "firmwareConfiguration", "Lme/magnum/melonds/domain/model/FirmwareConfiguration;"));
jobject rendererConfigurationObject = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "rendererConfiguration", "Lme/magnum/melonds/domain/model/RendererConfiguration;"));
MelonDSAndroid::EmulatorConfiguration finalEmulatorConfiguration;
finalEmulatorConfiguration.userInternalFirmwareAndBios = !useCustomBios;
finalEmulatorConfiguration.dsBios7Path = const_cast<char*>(dsBios7Path);
finalEmulatorConfiguration.dsBios9Path = const_cast<char*>(dsBios9Path);
finalEmulatorConfiguration.dsFirmwarePath = const_cast<char*>(dsFirmwarePath);
finalEmulatorConfiguration.dsiBios7Path = const_cast<char*>(dsiBios7Path);
finalEmulatorConfiguration.dsiBios9Path = const_cast<char*>(dsiBios9Path);
finalEmulatorConfiguration.dsiFirmwarePath = const_cast<char*>(dsiFirmwarePath);
finalEmulatorConfiguration.dsiNandPath = const_cast<char*>(dsiNandPath);
finalEmulatorConfiguration.internalFilesDir = const_cast<char*>(internalDir);
finalEmulatorConfiguration.fastForwardSpeedMultiplier = fastForwardMaxSpeed;
finalEmulatorConfiguration.useJit = useJit;
finalEmulatorConfiguration.consoleType = consoleType;
finalEmulatorConfiguration.soundEnabled = soundEnabled;
finalEmulatorConfiguration.volume = volume;
finalEmulatorConfiguration.audioInterpolation = audioInterpolation;
finalEmulatorConfiguration.audioBitrate = audioBitrate;
finalEmulatorConfiguration.audioLatency = audioLatency;
finalEmulatorConfiguration.micSource = micSource;
finalEmulatorConfiguration.firmwareConfiguration = buildFirmwareConfiguration(env, firmwareConfigurationObject);
finalEmulatorConfiguration.renderSettings = buildRenderSettings(env, rendererConfigurationObject);
finalEmulatorConfiguration.rewindEnabled = enableRewind ? 1 : 0;
finalEmulatorConfiguration.rewindCaptureSpacingSeconds = rewindPeriodSeconds;
finalEmulatorConfiguration.rewindLengthSeconds = rewindWindowSeconds;
return finalEmulatorConfiguration;
}
MelonDSAndroid::FirmwareConfiguration MelonDSAndroidConfiguration::buildFirmwareConfiguration(JNIEnv* env, jobject firmwareConfiguration) {
jclass firmwareConfigurationClass = env->GetObjectClass(firmwareConfiguration);
jstring nicknameString = (jstring) env->GetObjectField(firmwareConfiguration, env->GetFieldID(firmwareConfigurationClass, "nickname", "Ljava/lang/String;"));
jstring messageString = (jstring) env->GetObjectField(firmwareConfiguration, env->GetFieldID(firmwareConfigurationClass, "message", "Ljava/lang/String;"));
int language = env->GetIntField(firmwareConfiguration, env->GetFieldID(firmwareConfigurationClass, "language", "I"));
int colour = env->GetIntField(firmwareConfiguration, env->GetFieldID(firmwareConfigurationClass, "favouriteColour", "I"));
int birthdayDay = env->GetIntField(firmwareConfiguration, env->GetFieldID(firmwareConfigurationClass, "birthdayDay", "I"));
int birthdayMonth = env->GetIntField(firmwareConfiguration, env->GetFieldID(firmwareConfigurationClass, "birthdayMonth", "I"));
bool randomizeMacAddress = env->GetBooleanField(firmwareConfiguration, env->GetFieldID(firmwareConfigurationClass, "randomizeMacAddress", "Z"));
jstring macAddressString = (jstring) env->GetObjectField(firmwareConfiguration, env->GetFieldID(firmwareConfigurationClass, "internalMacAddress", "Ljava/lang/String;"));
jboolean isCopy = JNI_FALSE;
const char* nickname = env->GetStringUTFChars(nicknameString, &isCopy);
const char* message = env->GetStringUTFChars(messageString, &isCopy);
const char* macAddress = macAddressString ? env->GetStringUTFChars(macAddressString, &isCopy) : nullptr;
u8 macAddressArray[6] = { 0 };
if (macAddress) {
bool isBad = false;
int sectionCounter = 0;
std::size_t start = 0;
std::size_t end = 0;
// Split address string into sections separated by a colon (:)
std::string macString = macAddress;
while ((end = macString.find(':', start)) != std::string::npos) {
if (end != start) {
char* endPointer;
std::string sectionString = macString.substr(start, end - start);
// Each code section must be 2 hex characters
if (sectionString.size() != 2) {
isBad = true;
break;
}
unsigned long section = strtoul(sectionString.c_str(), &endPointer, 16);
if (*endPointer == 0) {
if (sectionCounter >= sizeof(macAddressArray)) {
isBad = true;
break;
}
macAddressArray[sectionCounter] = (u8) section;
sectionCounter++;
} else {
isBad = true;
break;
}
}
start = end + 1;
}
if (!isBad && end != start) {
char* endPointer;
std::string sectionString = macString.substr(start, end - start);
if (sectionString.size() != 8) {
isBad = true;
} else {
unsigned long section = strtoul(sectionString.c_str(), &endPointer, 16);
if (*endPointer == 0 && sectionCounter < sizeof(macAddressArray)) {
macAddressArray[sectionCounter] = (u32) section;
sectionCounter++;
} else {
isBad = true;
}
}
}
// If the MAC address is invalid, enable randomization
if (isBad) {
randomizeMacAddress = true;
}
}
MelonDSAndroid::FirmwareConfiguration finalFirmwareConfiguration;
strncpy(finalFirmwareConfiguration.username, nickname, sizeof(finalFirmwareConfiguration.username) - 1);
strncpy(finalFirmwareConfiguration.message, message, sizeof(finalFirmwareConfiguration.message) - 1);
finalFirmwareConfiguration.username[sizeof(finalFirmwareConfiguration.username) - 1] = '\0';
finalFirmwareConfiguration.message[sizeof(finalFirmwareConfiguration.message) - 1] = '\0';
finalFirmwareConfiguration.language = language;
finalFirmwareConfiguration.favouriteColour = colour;
finalFirmwareConfiguration.birthdayDay = birthdayDay;
finalFirmwareConfiguration.birthdayMonth = birthdayMonth;
finalFirmwareConfiguration.randomizeMacAddress = randomizeMacAddress;
memcpy(finalFirmwareConfiguration.macAddress, macAddressArray, sizeof(macAddressArray));
if (isCopy) {
env->ReleaseStringUTFChars(nicknameString, nickname);
env->ReleaseStringUTFChars(messageString, message);
if (macAddress) env->ReleaseStringUTFChars(macAddressString, macAddress);
}
return finalFirmwareConfiguration;
}
GPU::RenderSettings MelonDSAndroidConfiguration::buildRenderSettings(JNIEnv* env, jobject renderSettings) {
jclass renderSettingsClass = env->GetObjectClass(renderSettings);
jboolean threadedRendering = env->GetBooleanField(renderSettings, env->GetFieldID(renderSettingsClass, "threadedRendering", "Z"));
return {
threadedRendering == JNI_TRUE,
1,
false
};
}

View File

@ -0,0 +1,12 @@
#ifndef MELONDSANDROIDCONFIGURATION_H
#define MELONDSANDROIDCONFIGURATION_H
#include "MelonDS.h"
namespace MelonDSAndroidConfiguration {
MelonDSAndroid::EmulatorConfiguration buildEmulatorConfiguration(JNIEnv* env, jobject emulatorConfiguration);
MelonDSAndroid::FirmwareConfiguration buildFirmwareConfiguration(JNIEnv* env, jobject firmwareConfiguration);
GPU::RenderSettings buildRenderSettings(JNIEnv* env, jobject renderSettings);
}
#endif //MELONDSANDROIDCONFIGURATION_H

View File

@ -0,0 +1,33 @@
#include "JniEnvHandler.h"
#include "UriFileHandler.h"
#include "MelonDS.h"
JavaVM* vm;
JniEnvHandler* jniEnvHandler;
jobject androidUriFileHandler;
UriFileHandler* fileHandler;
extern "C"
{
JNIEXPORT void JNICALL
Java_me_magnum_melonds_MelonDSAndroidInterface_setup(JNIEnv* env, jobject thiz, jobject uriFileHandler)
{
env->GetJavaVM(&vm);
jniEnvHandler = new JniEnvHandler(vm);
androidUriFileHandler = env->NewGlobalRef(uriFileHandler);
fileHandler = new UriFileHandler(jniEnvHandler, androidUriFileHandler);
MelonDSAndroid::fileHandler = fileHandler;
}
JNIEXPORT void JNICALL
Java_me_magnum_melonds_MelonDSAndroidInterface_cleanup(JNIEnv* env, jobject thiz)
{
env->DeleteGlobalRef(androidUriFileHandler);
androidUriFileHandler = nullptr;
vm = nullptr;
delete fileHandler;
delete jniEnvHandler;
}
}

View File

@ -10,12 +10,10 @@
#include <android/asset_manager_jni.h>
#include "UriFileHandler.h"
#include "JniEnvHandler.h"
#include "MelonDSAndroidConfiguration.h"
#define MAX_CHEAT_SIZE (2*64)
MelonDSAndroid::EmulatorConfiguration buildEmulatorConfiguration(JNIEnv* env, jobject emulatorConfiguration);
MelonDSAndroid::FirmwareConfiguration buildFirmwareConfiguration(JNIEnv* env, jobject firmwareConfiguration);
GPU::RenderSettings buildRenderSettings(JNIEnv* env, jobject renderSettings);
void* emulate(void*);
pthread_t emuThread;
@ -33,30 +31,22 @@ float fastForwardSpeedMultiplier;
bool limitFps = true;
bool isFastForwardEnabled = false;
jobject globalAssetManager;
jobject androidUriFileHandler;
UriFileHandler* fileHandler;
JniEnvHandler* jniEnvHandler;
JavaVM* vm;
extern "C"
{
JNIEXPORT void JNICALL
Java_me_magnum_melonds_MelonEmulator_setupEmulator(JNIEnv* env, jobject thiz, jobject emulatorConfiguration, jobject javaAssetManager, jobject uriFileHandler, jobject textureBuffer)
Java_me_magnum_melonds_MelonEmulator_setupEmulator(JNIEnv* env, jobject thiz, jobject emulatorConfiguration, jobject javaAssetManager, jobject textureBuffer)
{
env->GetJavaVM(&vm);
jniEnvHandler = new JniEnvHandler(vm);
MelonDSAndroid::EmulatorConfiguration finalEmulatorConfiguration = buildEmulatorConfiguration(env, emulatorConfiguration);
MelonDSAndroid::EmulatorConfiguration finalEmulatorConfiguration = MelonDSAndroidConfiguration::buildEmulatorConfiguration(env, emulatorConfiguration);
fastForwardSpeedMultiplier = finalEmulatorConfiguration.fastForwardSpeedMultiplier;
globalAssetManager = env->NewGlobalRef(javaAssetManager);
androidUriFileHandler = env->NewGlobalRef(uriFileHandler);
globalAssetManager = env->NewGlobalRef(javaAssetManager);;
AAssetManager* assetManager = AAssetManager_fromJava(env, globalAssetManager);
fileHandler = new UriFileHandler(jniEnvHandler, androidUriFileHandler);
u32* textureBufferPointer = (u32*) env->GetDirectBufferAddress(textureBuffer);
MelonDSAndroid::setup(finalEmulatorConfiguration, assetManager, fileHandler, textureBufferPointer);
MelonDSAndroid::setup(finalEmulatorConfiguration, assetManager, textureBufferPointer);
paused = false;
}
@ -356,10 +346,6 @@ Java_me_magnum_melonds_MelonEmulator_stopEmulation(JNIEnv* env, jobject thiz)
env->DeleteGlobalRef(globalAssetManager);
globalAssetManager = nullptr;
env->DeleteGlobalRef(androidUriFileHandler);
androidUriFileHandler = nullptr;
delete fileHandler;
delete jniEnvHandler;
}
JNIEXPORT void JNICALL
@ -402,7 +388,7 @@ Java_me_magnum_melonds_MelonEmulator_setFastForwardEnabled(JNIEnv* env, jobject
JNIEXPORT void JNICALL
Java_me_magnum_melonds_MelonEmulator_updateEmulatorConfiguration(JNIEnv* env, jobject thiz, jobject emulatorConfiguration)
{
MelonDSAndroid::EmulatorConfiguration newConfiguration = buildEmulatorConfiguration(env, emulatorConfiguration);
MelonDSAndroid::EmulatorConfiguration newConfiguration = MelonDSAndroidConfiguration::buildEmulatorConfiguration(env, emulatorConfiguration);
MelonDSAndroid::updateEmulatorConfiguration(newConfiguration);
fastForwardSpeedMultiplier = newConfiguration.fastForwardSpeedMultiplier;
@ -413,195 +399,6 @@ Java_me_magnum_melonds_MelonEmulator_updateEmulatorConfiguration(JNIEnv* env, jo
}
}
MelonDSAndroid::EmulatorConfiguration buildEmulatorConfiguration(JNIEnv* env, jobject emulatorConfiguration) {
jclass emulatorConfigurationClass = env->GetObjectClass(emulatorConfiguration);
jclass uriClass = env->FindClass("android/net/Uri");
jclass consoleTypeEnumClass = env->FindClass("me/magnum/melonds/domain/model/ConsoleType");
jclass audioBitrateEnumClass = env->FindClass("me/magnum/melonds/domain/model/AudioBitrate");
jclass audioInterpolationEnumClass = env->FindClass("me/magnum/melonds/domain/model/AudioInterpolation");
jclass audioLatencyEnumClass = env->FindClass("me/magnum/melonds/domain/model/AudioLatency");
jclass micSourceEnumClass = env->FindClass("me/magnum/melonds/domain/model/MicSource");
jmethodID uriToStringMethod = env->GetMethodID(uriClass, "toString", "()Ljava/lang/String;");
jboolean useCustomBios = env->GetBooleanField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "useCustomBios", "Z"));
jobject dsBios7Uri = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "dsBios7Uri", "Landroid/net/Uri;"));
jobject dsBios9Uri = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "dsBios9Uri", "Landroid/net/Uri;"));
jobject dsFirmwareUri = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "dsFirmwareUri", "Landroid/net/Uri;"));
jobject dsiBios7Uri = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "dsiBios7Uri", "Landroid/net/Uri;"));
jobject dsiBios9Uri = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "dsiBios9Uri", "Landroid/net/Uri;"));
jobject dsiFirmwareUri = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "dsiFirmwareUri", "Landroid/net/Uri;"));
jobject dsiNandUri = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "dsiNandUri", "Landroid/net/Uri;"));
jstring internalFilesDir = (jstring) env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "internalDirectory", "Ljava/lang/String;"));
jfloat fastForwardMaxSpeed = env->GetFloatField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "fastForwardSpeedMultiplier", "F"));
jboolean enableRewind = env->GetBooleanField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "rewindEnabled", "Z"));
jint rewindPeriodSeconds = env->GetIntField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "rewindPeriodSeconds", "I"));
jint rewindWindowSeconds = env->GetIntField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "rewindWindowSeconds", "I"));
jboolean useJit = env->GetBooleanField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "useJit", "Z"));
jobject consoleTypeEnum = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "consoleType", "Lme/magnum/melonds/domain/model/ConsoleType;"));
jint consoleType = env->GetIntField(consoleTypeEnum, env->GetFieldID(consoleTypeEnumClass, "consoleType", "I"));
jboolean soundEnabled = env->GetBooleanField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "soundEnabled", "Z"));
jint volume = env->GetIntField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "volume", "I"));
jobject audioInterpolationEnum = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "audioInterpolation", "Lme/magnum/melonds/domain/model/AudioInterpolation;"));
jint audioInterpolation = env->GetIntField(audioInterpolationEnum, env->GetFieldID(audioInterpolationEnumClass, "interpolationValue", "I"));
jobject audioBitrateEnum = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "audioBitrate", "Lme/magnum/melonds/domain/model/AudioBitrate;"));
jint audioBitrate = env->GetIntField(audioBitrateEnum, env->GetFieldID(audioBitrateEnumClass, "bitrateValue", "I"));
jobject audioLatencyEnum = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "audioLatency", "Lme/magnum/melonds/domain/model/AudioLatency;"));
jint audioLatency = env->GetIntField(audioLatencyEnum, env->GetFieldID(audioLatencyEnumClass, "latencyValue", "I"));
jobject micSourceEnum = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "micSource", "Lme/magnum/melonds/domain/model/MicSource;"));
jint micSource = env->GetIntField(micSourceEnum, env->GetFieldID(micSourceEnumClass, "sourceValue", "I"));
jboolean isCopy = JNI_FALSE;
jstring dsBios7String = dsBios7Uri ? (jstring) env->CallObjectMethod(dsBios7Uri, uriToStringMethod) : nullptr;
jstring dsBios9String = dsBios9Uri ? (jstring) env->CallObjectMethod(dsBios9Uri, uriToStringMethod) : nullptr;
jstring dsFirmwareString = dsFirmwareUri ? (jstring) env->CallObjectMethod(dsFirmwareUri, uriToStringMethod) : nullptr;
jstring dsiBios7String = dsiBios7Uri ? (jstring) env->CallObjectMethod(dsiBios7Uri, uriToStringMethod) : nullptr;
jstring dsiBios9String = dsiBios9Uri ? (jstring) env->CallObjectMethod(dsiBios9Uri, uriToStringMethod) : nullptr;
jstring dsiFirmwareString = dsiFirmwareUri ? (jstring) env->CallObjectMethod(dsiFirmwareUri, uriToStringMethod) : nullptr;
jstring dsiNandString = dsiNandUri ? (jstring) env->CallObjectMethod(dsiNandUri, uriToStringMethod) : nullptr;
const char* dsBios7Path = dsBios7Uri ? env->GetStringUTFChars(dsBios7String, &isCopy) : nullptr;
const char* dsBios9Path = dsBios9Uri ? env->GetStringUTFChars(dsBios9String, &isCopy) : nullptr;
const char* dsFirmwarePath = dsFirmwareUri ? env->GetStringUTFChars(dsFirmwareString, &isCopy) : nullptr;
const char* dsiBios7Path = dsiBios7Uri ? env->GetStringUTFChars(dsiBios7String, &isCopy) : nullptr;
const char* dsiBios9Path = dsiBios9Uri ? env->GetStringUTFChars(dsiBios9String, &isCopy) : nullptr;
const char* dsiFirmwarePath = dsiFirmwareUri ? env->GetStringUTFChars(dsiFirmwareString, &isCopy) : nullptr;
const char* dsiNandPath = dsiNandUri ? env->GetStringUTFChars(dsiNandString, &isCopy) : nullptr;
const char* internalDir = env->GetStringUTFChars(internalFilesDir, JNI_FALSE);
jobject firmwareConfigurationObject = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "firmwareConfiguration", "Lme/magnum/melonds/domain/model/FirmwareConfiguration;"));
jobject rendererConfigurationObject = env->GetObjectField(emulatorConfiguration, env->GetFieldID(emulatorConfigurationClass, "rendererConfiguration", "Lme/magnum/melonds/domain/model/RendererConfiguration;"));
MelonDSAndroid::EmulatorConfiguration finalEmulatorConfiguration;
finalEmulatorConfiguration.userInternalFirmwareAndBios = !useCustomBios;
finalEmulatorConfiguration.dsBios7Path = const_cast<char*>(dsBios7Path);
finalEmulatorConfiguration.dsBios9Path = const_cast<char*>(dsBios9Path);
finalEmulatorConfiguration.dsFirmwarePath = const_cast<char*>(dsFirmwarePath);
finalEmulatorConfiguration.dsiBios7Path = const_cast<char*>(dsiBios7Path);
finalEmulatorConfiguration.dsiBios9Path = const_cast<char*>(dsiBios9Path);
finalEmulatorConfiguration.dsiFirmwarePath = const_cast<char*>(dsiFirmwarePath);
finalEmulatorConfiguration.dsiNandPath = const_cast<char*>(dsiNandPath);
finalEmulatorConfiguration.internalFilesDir = const_cast<char*>(internalDir);
finalEmulatorConfiguration.fastForwardSpeedMultiplier = fastForwardMaxSpeed;
finalEmulatorConfiguration.useJit = useJit;
finalEmulatorConfiguration.consoleType = consoleType;
finalEmulatorConfiguration.soundEnabled = soundEnabled;
finalEmulatorConfiguration.volume = volume;
finalEmulatorConfiguration.audioInterpolation = audioInterpolation;
finalEmulatorConfiguration.audioBitrate = audioBitrate;
finalEmulatorConfiguration.audioLatency = audioLatency;
finalEmulatorConfiguration.micSource = micSource;
finalEmulatorConfiguration.firmwareConfiguration = buildFirmwareConfiguration(env, firmwareConfigurationObject);
finalEmulatorConfiguration.renderSettings = buildRenderSettings(env, rendererConfigurationObject);
finalEmulatorConfiguration.rewindEnabled = enableRewind ? 1 : 0;
finalEmulatorConfiguration.rewindCaptureSpacingSeconds = rewindPeriodSeconds;
finalEmulatorConfiguration.rewindLengthSeconds = rewindWindowSeconds;
return finalEmulatorConfiguration;
}
MelonDSAndroid::FirmwareConfiguration buildFirmwareConfiguration(JNIEnv* env, jobject firmwareConfiguration) {
jclass firmwareConfigurationClass = env->GetObjectClass(firmwareConfiguration);
jstring nicknameString = (jstring) env->GetObjectField(firmwareConfiguration, env->GetFieldID(firmwareConfigurationClass, "nickname", "Ljava/lang/String;"));
jstring messageString = (jstring) env->GetObjectField(firmwareConfiguration, env->GetFieldID(firmwareConfigurationClass, "message", "Ljava/lang/String;"));
int language = env->GetIntField(firmwareConfiguration, env->GetFieldID(firmwareConfigurationClass, "language", "I"));
int colour = env->GetIntField(firmwareConfiguration, env->GetFieldID(firmwareConfigurationClass, "favouriteColour", "I"));
int birthdayDay = env->GetIntField(firmwareConfiguration, env->GetFieldID(firmwareConfigurationClass, "birthdayDay", "I"));
int birthdayMonth = env->GetIntField(firmwareConfiguration, env->GetFieldID(firmwareConfigurationClass, "birthdayMonth", "I"));
bool randomizeMacAddress = env->GetBooleanField(firmwareConfiguration, env->GetFieldID(firmwareConfigurationClass, "randomizeMacAddress", "Z"));
jstring macAddressString = (jstring) env->GetObjectField(firmwareConfiguration, env->GetFieldID(firmwareConfigurationClass, "internalMacAddress", "Ljava/lang/String;"));
jboolean isCopy = JNI_FALSE;
const char* nickname = env->GetStringUTFChars(nicknameString, &isCopy);
const char* message = env->GetStringUTFChars(messageString, &isCopy);
const char* macAddress = macAddressString ? env->GetStringUTFChars(macAddressString, &isCopy) : nullptr;
u8 macAddressArray[6] = { 0 };
if (macAddress) {
bool isBad = false;
int sectionCounter = 0;
std::size_t start = 0;
std::size_t end = 0;
// Split address string into sections separated by a colon (:)
std::string macString = macAddress;
while ((end = macString.find(':', start)) != std::string::npos) {
if (end != start) {
char* endPointer;
std::string sectionString = macString.substr(start, end - start);
// Each code section must be 2 hex characters
if (sectionString.size() != 2) {
isBad = true;
break;
}
unsigned long section = strtoul(sectionString.c_str(), &endPointer, 16);
if (*endPointer == 0) {
if (sectionCounter >= sizeof(macAddressArray)) {
isBad = true;
break;
}
macAddressArray[sectionCounter] = (u8) section;
sectionCounter++;
} else {
isBad = true;
break;
}
}
start = end + 1;
}
if (!isBad && end != start) {
char* endPointer;
std::string sectionString = macString.substr(start, end - start);
if (sectionString.size() != 8) {
isBad = true;
} else {
unsigned long section = strtoul(sectionString.c_str(), &endPointer, 16);
if (*endPointer == 0 && sectionCounter < sizeof(macAddressArray)) {
macAddressArray[sectionCounter] = (u32) section;
sectionCounter++;
} else {
isBad = true;
}
}
}
// If the MAC address is invalid, enable randomization
if (isBad) {
randomizeMacAddress = true;
}
}
MelonDSAndroid::FirmwareConfiguration finalFirmwareConfiguration;
strncpy(finalFirmwareConfiguration.username, nickname, sizeof(finalFirmwareConfiguration.username) - 1);
strncpy(finalFirmwareConfiguration.message, message, sizeof(finalFirmwareConfiguration.message) - 1);
finalFirmwareConfiguration.username[sizeof(finalFirmwareConfiguration.username) - 1] = '\0';
finalFirmwareConfiguration.message[sizeof(finalFirmwareConfiguration.message) - 1] = '\0';
finalFirmwareConfiguration.language = language;
finalFirmwareConfiguration.favouriteColour = colour;
finalFirmwareConfiguration.birthdayDay = birthdayDay;
finalFirmwareConfiguration.birthdayMonth = birthdayMonth;
finalFirmwareConfiguration.randomizeMacAddress = randomizeMacAddress;
memcpy(finalFirmwareConfiguration.macAddress, macAddressArray, sizeof(macAddressArray));
if (isCopy) {
env->ReleaseStringUTFChars(nicknameString, nickname);
env->ReleaseStringUTFChars(messageString, message);
if (macAddress) env->ReleaseStringUTFChars(macAddressString, macAddress);
}
return finalFirmwareConfiguration;
}
GPU::RenderSettings buildRenderSettings(JNIEnv* env, jobject renderSettings) {
jclass renderSettingsClass = env->GetObjectClass(renderSettings);
jboolean threadedRendering = env->GetBooleanField(renderSettings, env->GetFieldID(renderSettingsClass, "threadedRendering", "Z"));
return {
threadedRendering == JNI_TRUE,
1,
false
};
}
double getCurrentMillis() {
timespec now;
clock_gettime(CLOCK_REALTIME, &now);

View File

@ -0,0 +1,8 @@
package me.magnum.melonds
import me.magnum.melonds.common.UriFileHandler
object MelonDSAndroidInterface {
external fun setup(uriFileHandler: UriFileHandler)
external fun cleanup()
}

View File

@ -8,6 +8,8 @@ import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import dagger.hilt.android.HiltAndroidApp
import io.reactivex.disposables.Disposable
import me.magnum.melonds.common.UriFileHandler
import me.magnum.melonds.common.uridelegates.UriHandler
import me.magnum.melonds.domain.repositories.SettingsRepository
import me.magnum.melonds.migrations.Migrator
import javax.inject.Inject
@ -16,11 +18,16 @@ import javax.inject.Inject
class MelonDSApplication : Application(), Configuration.Provider {
companion object {
const val NOTIFICATION_CHANNEL_ID_BACKGROUND_TASKS = "channel_cheat_importing"
init {
System.loadLibrary("melonDS-android-frontend")
}
}
@Inject lateinit var workerFactory: HiltWorkerFactory
@Inject lateinit var settingsRepository: SettingsRepository
@Inject lateinit var migrator: Migrator
@Inject lateinit var uriHandler: UriHandler
private var themeObserverDisposable: Disposable? = null
@ -29,6 +36,7 @@ class MelonDSApplication : Application(), Configuration.Provider {
createNotificationChannels()
applyTheme()
performMigrations()
MelonDSAndroidInterface.setup(UriFileHandler(this, uriHandler))
}
private fun createNotificationChannels() {
@ -60,5 +68,6 @@ class MelonDSApplication : Application(), Configuration.Provider {
override fun onTerminate() {
super.onTerminate()
themeObserverDisposable?.dispose()
MelonDSAndroidInterface.cleanup()
}
}

View File

@ -35,7 +35,7 @@ object MelonEmulator {
DSI_NAND_BAD
}
external fun setupEmulator(emulatorConfiguration: EmulatorConfiguration, assetManager: AssetManager?, uriFileHandler: UriFileHandler, textureBuffer: ByteBuffer)
external fun setupEmulator(emulatorConfiguration: EmulatorConfiguration, assetManager: AssetManager?, textureBuffer: ByteBuffer)
external fun setupCheats(cheats: Array<Cheat>)

View File

@ -30,7 +30,6 @@ import io.reactivex.subjects.PublishSubject
import me.magnum.melonds.MelonEmulator
import me.magnum.melonds.R
import me.magnum.melonds.common.Schedulers
import me.magnum.melonds.common.UriFileHandler
import me.magnum.melonds.common.uridelegates.UriHandler
import me.magnum.melonds.common.vibration.TouchVibrator
import me.magnum.melonds.databinding.ActivityEmulatorBinding
@ -46,8 +45,8 @@ import me.magnum.melonds.ui.emulator.DSRenderer.RendererListener
import me.magnum.melonds.ui.emulator.firmware.FirmwareEmulatorDelegate
import me.magnum.melonds.ui.emulator.input.*
import me.magnum.melonds.ui.emulator.rewind.EdgeSpacingDecorator
import me.magnum.melonds.ui.emulator.rewind.model.RewindSaveState
import me.magnum.melonds.ui.emulator.rewind.RewindSaveStateAdapter
import me.magnum.melonds.ui.emulator.rewind.model.RewindSaveState
import me.magnum.melonds.ui.emulator.rom.RomEmulatorDelegate
import me.magnum.melonds.ui.settings.SettingsActivity
import me.magnum.melonds.utils.PackageManagerCompat
@ -64,10 +63,6 @@ class EmulatorActivity : AppCompatActivity(), RendererListener {
const val KEY_BOOT_FIRMWARE_CONSOLE = "boot_firmware_console"
private const val KEY_BOOT_FIRMWARE_ONLY = "boot_firmware_only"
init {
System.loadLibrary("melonDS-android-frontend")
}
fun getRomEmulatorActivityIntent(context: Context, rom: Rom): Intent {
return Intent(context, EmulatorActivity::class.java).apply {
putExtra(KEY_ROM, RomParcelable(rom))
@ -520,10 +515,6 @@ class EmulatorActivity : AppCompatActivity(), RendererListener {
}
}
fun buildUriFileHandler(): UriFileHandler {
return UriFileHandler(this, uriHandler)
}
fun getRendererTextureBuffer(): ByteBuffer {
return dsRenderer.textureBuffer
}

View File

@ -26,7 +26,7 @@ class FirmwareEmulatorDelegate(activity: EmulatorActivity) : EmulatorDelegate(ac
return getEmulatorLaunchConfiguration(firmwareConsoleType).flatMapCompletable { emulatorConfiguration ->
Completable.create {
activity.viewModel.loadLayoutForFirmware()
MelonEmulator.setupEmulator(emulatorConfiguration, activity.assets, activity.buildUriFileHandler(), activity.getRendererTextureBuffer())
MelonEmulator.setupEmulator(emulatorConfiguration, activity.assets, activity.getRendererTextureBuffer())
val loadResult = MelonEmulator.bootFirmware()
if (loadResult != MelonEmulator.FirmwareLoadResult.SUCCESS) {

View File

@ -63,7 +63,7 @@ class RomEmulatorDelegate(activity: EmulatorActivity, private val picasso: Picas
Pair(cheats, emulatorConfiguration)
}.flatMap { (cheats, emulatorConfiguration) ->
Single.create<MelonEmulator.LoadResult> { emitter ->
MelonEmulator.setupEmulator(emulatorConfiguration, activity.assets, activity.buildUriFileHandler(), activity.getRendererTextureBuffer())
MelonEmulator.setupEmulator(emulatorConfiguration, activity.assets, activity.getRendererTextureBuffer())
val rom = romPair.first
val romPath = romPair.second

@ -1 +1 @@
Subproject commit 159c113545484fc38c0d74c7f8d066a626093911
Subproject commit 69de234b13b7064499b8e06d2ff46b96b152541f