devlo: snapshot for thread user-thread-20251020073048

This commit is contained in:
devloai[bot]
2025-10-20 07:32:12 +00:00
parent 36e50678d6
commit fa9478edf6
208 changed files with 17517 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
+4
View File
@@ -0,0 +1,4 @@
/build
/src/main/assets/hash.txt
*.po
*.pot
+239
View File
@@ -0,0 +1,239 @@
import com.android.build.gradle.internal.tasks.factory.dependsOn
import java.io.IOException
import java.security.MessageDigest
import java.util.regex.Pattern
import javax.xml.bind.DatatypeConverter
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.kotlinx.gettext)
alias(libs.plugins.aboutlibraries.android)
}
fun String.runCommand(workingDir: File = File(".")): String? {
try {
val proc = ProcessBuilder(*trim().split("\\s".toRegex()).toTypedArray())
.directory(workingDir)
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.redirectError(ProcessBuilder.Redirect.PIPE)
.start()
assert(proc.waitFor(1, TimeUnit.MINUTES))
return proc.inputStream.bufferedReader().readText()
} catch (e: IOException) {
e.printStackTrace()
return null
}
}
fun getGitHash(): String? = "git log --format=%h -1".runCommand()?.trim()
val versionMajor: Int? = System.getenv("EMULATOR_VERSION_MAJOR")?.toIntOrNull()
val versionMinor: Int? = System.getenv("EMULATOR_VERSION_MINOR")?.toIntOrNull()
versionMajor
fun getVersionName(): String {
if (versionMajor != null && versionMinor != null)
return "$versionMajor.$versionMinor"
return getGitHash() ?: "1.0"
}
fun getVersionCode(): Int = System.getenv("VERSION_CODE")?.toIntOrNull() ?: 1
val cemuDataFilesFolder = "../../../bin"
android {
namespace = "info.cemu.cemu"
compileSdk = 36
ndkVersion = "28.2.13676358"
defaultConfig {
applicationId = "info.cemu.cemu"
minSdk = 31
targetSdk = 36
versionCode = getVersionCode()
versionName = getVersionName()
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
androidResources {
ignoreAssetsPattern = "!*cemu.mo:"
}
sourceSets.getByName("main") {
assets {
srcDir(cemuDataFilesFolder)
}
}
packaging {
jniLibs.useLegacyPackaging = true
}
val keystoreFilePath: String? = System.getenv("ANDROID_STORE_FILE")
signingConfigs {
if (keystoreFilePath != null) {
create("release") {
storeFile = file(keystoreFilePath)
storePassword = System.getenv("ANDROID_KEY_STORE_PASSWORD")
keyAlias = System.getenv("ANDROID_KEY_ALIAS")
keyPassword = System.getenv("ANDROID_KEY_STORE_PASSWORD")
}
}
}
buildTypes {
debug {
applicationIdSuffix = ".debug"
}
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
signingConfig = if (keystoreFilePath != null) {
signingConfigs.getByName("release")
} else {
signingConfigs.getByName("debug")
}
}
}
compileOptions {
sourceCompatibility(JavaVersion.VERSION_17)
targetCompatibility(JavaVersion.VERSION_17)
}
externalNativeBuild {
cmake {
version = "3.25.0+"
path = file("../../../CMakeLists.txt")
}
}
defaultConfig {
externalNativeBuild {
cmake {
arguments(
"-DANDROID_STL=c++_shared",
"-DENABLE_VCPKG=ON",
"-DVCPKG_TARGET_ANDROID=ON",
"-DENABLE_SDL=OFF",
"-DENABLE_WXWIDGETS=OFF",
"-DENABLE_OPENGL=OFF",
"-DENABLE_BLUEZ=OFF",
"-DBUNDLE_SPEEX=ON",
"-DENABLE_DISCORD_RPC=OFF",
"-DENABLE_NSYSHID_LIBUSB=OFF",
"-DENABLE_WAYLAND=OFF",
"-DENABLE_HIDAPI=OFF"
)
if (versionMajor != null && versionMinor != null) {
arguments.addAll(
arrayOf(
"-DEMULATOR_VERSION_MAJOR=$versionMajor",
"-DEMULATOR_VERSION_MINOR=$versionMinor"
)
)
}
abiFilters("arm64-v8a")
}
}
}
buildFeatures {
buildConfig = true
dataBinding = true
viewBinding = true
compose = true
}
kotlinOptions {
jvmTarget = "17"
}
}
abstract class ComputeCemuDataFilesHashTask : DefaultTask() {
private val ignoreFilePatterns = arrayOf(
Pattern.compile(".*cemu\\.mo"),
Pattern.compile(".*Cemu_(?:debug|release)"),
)
@get:Input
abstract val cemuDataFolder: Property<String>
private fun isFileIgnored(file: File): Boolean {
return ignoreFilePatterns.any { pattern -> pattern.matcher(file.path).matches() }
}
@TaskAction
fun computeCemuDataFilesHash() {
val assetDir = File(project.projectDir, "src/main/assets")
if (!assetDir.exists()) {
assetDir.mkdirs()
}
val cemuDataFilesDir = File(project.projectDir, cemuDataFolder.get())
val hashFile = File(assetDir, "hash.txt")
val md = MessageDigest.getInstance("SHA-256")
if (!cemuDataFilesDir.isDirectory) {
hashFile.writeText("invalid")
return
}
val fileHashes = cemuDataFilesDir.walkTopDown()
.filter { it.isFile && !isFileIgnored(it) }
.sortedBy { it.path }
.map {
md.reset()
md.update(it.path.toByteArray())
md.update(it.readBytes())
md.digest()
}
.toList()
md.reset()
fileHashes.forEach { md.update(it) }
hashFile.writeText(DatatypeConverter.printHexBinary(md.digest()))
}
}
val computeCemuDataFilesHashTask =
tasks.register<ComputeCemuDataFilesHashTask>("computeCemuDataFilesHash") {
cemuDataFolder = cemuDataFilesFolder
}
tasks.preBuild.dependsOn(computeCemuDataFilesHashTask)
gettext {
potFile.set(File(projectDir, "cemu_kt.pot"))
keywords.set(listOf("tr", "trNoop"))
}
dependencies {
implementation(libs.aboutlibraries.compose.m3)
implementation(libs.kotlinx.gettext)
implementation(libs.kotlinx.serialization.json)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.compose.material3)
testImplementation(libs.junit)
testImplementation(libs.archunit.junit4)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)
implementation(libs.okhttp)
implementation(libs.okhttp.coroutines)
implementation(libs.androidx.appcompat)
implementation(libs.google.android.material)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.core.ktx)
}
+1
View File
@@ -0,0 +1 @@
-dontobfuscate
@@ -0,0 +1,14 @@
package info.cemu.cemu.tests
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PlaceholderInstrumentedTest {
@Test
fun test() {
assertTrue(true)
}
}
@@ -0,0 +1,15 @@
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="25%"
android:insetTop="25%"
android:insetRight="25%"
android:insetBottom="25%">
<vector
android:width="14dp"
android:height="14dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#ffffff"
android:pathData="m480,840q-65,0 -120.5,-32Q304,776 272,720L160,720v-80h84q-3,-20 -3.5,-40 -0.5,-20 -0.5,-40h-80v-80h80q0,-20 0.5,-40 0.5,-20 3.5,-40h-84v-80h112q14,-23 31.5,-43 17.5,-20 40.5,-35l-64,-66 56,-56 86,86q28,-9 57,-9 29,0 57,9l88,-86 56,56 -66,66q23,15 41.5,34.5 18.5,19.5 32.5,43.5h112v80h-84q3,20 3.5,40 0.5,20 0.5,40h80v80h-80q0,20 -0.5,40 -0.5,20 -3.5,40h84v80L688,720q-32,56 -87.5,88 -55.5,32 -120.5,32zM400,640h160v-80L400,560ZM400,480h160v-80L400,400Z" />
</vector>
</inset>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_debug" />
<monochrome android:drawable="@drawable/ic_debug" />
</adaptive-icon>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_debug" />
<monochrome android:drawable="@drawable/ic_debug" />
</adaptive-icon>
@@ -0,0 +1,3 @@
<resources>
<string name="app_name" translatable="false">Cemu debug</string>
</resources>
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-feature
android:name="android.hardware.vulkan.version"
android:required="true"
android:version="0x401000" />
<application
android:name=".CemuApplication"
android:allowBackup="true"
android:appCategory="game"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Cemu">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".emulation.EmulationActivity"
android:configChanges="orientation|screenSize"
android:exported="true"
android:launchMode="singleTop"
android:parentActivityName=".MainActivity"
android:screenOrientation="userLandscape"
tools:ignore="DiscouragedApi">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="content" />
<data android:mimeType="*/*" />
<data android:host="*" />
<data android:pathPattern=".*\\.wud" />
<data android:pathPattern=".*\\.wux" />
<data android:pathPattern=".*\\.wua" />
<data android:pathPattern=".*\\.wuhb" />
<data android:pathPattern=".*\\.iso" />
<data android:pathPattern=".*\\.elf" />
<data android:pathPattern=".*\\.rpx" />
<!--
TODO?
<data android:pathPattern=".*/title.tmd" />
-->
</intent-filter>
</activity>
<provider
android:name=".provider.DocumentsProvider"
android:authorities="${applicationId}.provider"
android:exported="true"
android:grantUriPermissions="true"
android:permission="android.permission.MANAGE_DOCUMENTS">
<intent-filter>
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
</intent-filter>
</provider>
</application>
</manifest>
@@ -0,0 +1,56 @@
#include "AndroidAudio.h"
#include "Cafe/OS/libs/snd_core/ax.h"
#include "audio/IAudioAPI.h"
#if HAS_CUBEB
#include "audio/CubebAPI.h"
#endif // HAS_CUBEB
namespace AndroidAudio
{
void createAudioDevice(IAudioAPI::AudioAPI audioApi, sint32 channels, sint32 volume, bool isTV)
{
static constexpr int AX_FRAMES_PER_GROUP = 4;
std::unique_lock lock(g_audioMutex);
auto& audioDevice = isTV ? g_tvAudio : g_padAudio;
switch (channels)
{
case 0:
channels = 1;
break;
case 2:
channels = 6;
break;
default: // stereo
channels = 2;
break;
}
switch (audioApi)
{
#if HAS_CUBEB
case IAudioAPI::AudioAPI::Cubeb:
{
audioDevice.reset();
std::shared_ptr<CubebAPI::CubebDeviceDescription> deviceDescriptionPtr = std::make_shared<CubebAPI::CubebDeviceDescription>(nullptr, std::string(), std::wstring());
audioDevice = IAudioAPI::CreateDevice(IAudioAPI::AudioAPI::Cubeb, deviceDescriptionPtr, 48000, channels, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
audioDevice->SetVolume(volume);
break;
}
#endif // HAS_CUBEB
default:
cemuLog_log(LogType::Force, "Invalid audio api: {}", audioApi);
break;
}
}
void setAudioVolume(sint32 volume, bool isTV)
{
std::shared_lock lock(g_audioMutex);
auto& audioDevice = isTV ? g_tvAudio : g_padAudio;
if (audioDevice)
audioDevice->SetVolume(volume);
}
}; // namespace AndroidAudio
@@ -0,0 +1,9 @@
#pragma once
#include "audio/IAudioAPI.h"
namespace AndroidAudio
{
void createAudioDevice(IAudioAPI::AudioAPI audioApi, sint32 channels, sint32 volume, bool isTV = true);
void setAudioVolume(sint32 volume, bool isTV = true);
}; // namespace AndroidAudio
@@ -0,0 +1,3 @@
#include "AndroidEmulatedController.h"
std::array<std::unique_ptr<AndroidEmulatedController>, InputManager::kMaxController> AndroidEmulatedController::s_emulatedControllers;
@@ -0,0 +1,123 @@
#pragma once
#include "input/InputManager.h"
#include "input/api/Controller.h"
#include "input/emulated/ClassicController.h"
#include "input/emulated/ProController.h"
#include "input/emulated/WiimoteController.h"
class AndroidEmulatedController {
private:
size_t m_index;
static std::array<std::unique_ptr<AndroidEmulatedController>, InputManager::kMaxController> s_emulatedControllers;
EmulatedControllerPtr m_emulatedController;
AndroidEmulatedController(size_t index)
: m_index(index)
{
m_emulatedController = InputManager::instance().get_controller(m_index);
}
public:
static AndroidEmulatedController& getAndroidEmulatedController(size_t index)
{
auto& controller = s_emulatedControllers.at(index);
if (!controller)
controller = std::unique_ptr<AndroidEmulatedController>(new AndroidEmulatedController(index));
return *controller;
}
void setButtonValue(uint64 mappingId, bool value)
{
if (!m_emulatedController)
return;
m_emulatedController->setButtonValue(mappingId, value);
}
void setAxisValue(uint64 mappingId, float value)
{
if (!m_emulatedController)
return;
m_emulatedController->setAxisValue(mappingId, value);
}
void setType(EmulatedController::Type type)
{
if (m_emulatedController && m_emulatedController->type() == type)
return;
m_emulatedController = InputManager::instance().set_controller(m_index, type);
InputManager::instance().save(m_index);
}
void setMapping(uint64 mappingId, ControllerPtr controller, uint64 buttonId)
{
if (m_emulatedController && controller)
{
const auto& controllers = m_emulatedController->get_controllers();
auto controllerIt = std::find_if(controllers.begin(), controllers.end(), [&](const ControllerPtr& c) { return c->api() == controller->api() && c->uuid() == controller->uuid(); });
if (controllerIt == controllers.end())
m_emulatedController->add_controller(controller);
else
controller = *controllerIt;
m_emulatedController->set_mapping(mappingId, controller, buttonId);
InputManager::instance().save(m_index);
}
}
std::optional<std::string> getMapping(uint64 mapping) const
{
if (!m_emulatedController)
return {};
auto controller = m_emulatedController->get_mapping_controller(mapping);
if (!controller)
return {};
auto mappingName = m_emulatedController->get_mapping_name(mapping);
return fmt::format("{}: {}", controller->display_name(), mappingName);
}
std::map<uint64, std::string> getMappings() const
{
if (!m_emulatedController)
return {};
std::map<uint64, std::string> mappings;
auto type = m_emulatedController->type();
uint64 mapping = 0;
uint64 maxMapping = 0;
if (type == EmulatedController::Type::VPAD)
{
mapping = VPADController::ButtonId::kButtonId_A;
maxMapping = VPADController::ButtonId::kButtonId_Max;
}
if (type == EmulatedController::Type::Pro)
{
mapping = ProController::ButtonId::kButtonId_A;
maxMapping = ProController::ButtonId::kButtonId_Max;
}
if (type == EmulatedController::Type::Classic)
{
mapping = ClassicController::ButtonId::kButtonId_A;
maxMapping = ClassicController::ButtonId::kButtonId_Max;
}
if (type == EmulatedController::Type::Wiimote)
{
mapping = WiimoteController::ButtonId::kButtonId_A;
maxMapping = WiimoteController::ButtonId::kButtonId_Max;
}
for (; mapping < maxMapping; mapping++)
{
auto mappingName = getMapping(mapping);
if (mappingName.has_value())
mappings[mapping] = mappingName.value();
}
return mappings;
}
void setDisabled()
{
InputManager::instance().delete_controller(m_index, true);
m_emulatedController.reset();
}
EmulatedControllerPtr getEmulatedController()
{
return m_emulatedController;
}
void clearMapping(uint64 mapping)
{
if (!m_emulatedController)
return;
m_emulatedController->delete_mapping(mapping);
}
};
@@ -0,0 +1,82 @@
#pragma once
#include "JNIUtils.h"
#include "Common/android/FilesystemAndroid.h"
class AndroidFilesystemCallbacks : public FilesystemAndroid::FilesystemCallbacks {
jmethodID m_openContentUriMid;
jmethodID m_listFilesMid;
jmethodID m_isDirectoryMid;
jmethodID m_isFileMid;
jmethodID m_existsMid;
JNIUtils::Scopedjclass m_fileUtilClass;
bool CallBooleanFunction(const std::filesystem::path& uri, jmethodID methodId)
{
bool result = false;
JNIUtils::fiberSafeJNICall([&](JNIEnv* env) {
jstring uriString = JNIUtils::toJString(env, uri);
result = env->CallStaticBooleanMethod(*m_fileUtilClass, methodId, uriString);
env->DeleteLocalRef(uriString);
});
return result;
}
public:
AndroidFilesystemCallbacks()
{
JNIUtils::ScopedJNIENV env;
m_fileUtilClass = JNIUtils::Scopedjclass("info/cemu/cemu/nativeinterface/NativeFiles");
m_openContentUriMid = env->GetStaticMethodID(*m_fileUtilClass, "openContentUri", "(Ljava/lang/String;)I");
m_listFilesMid = env->GetStaticMethodID(*m_fileUtilClass, "listFiles", "(Ljava/lang/String;)[Ljava/lang/String;");
m_isDirectoryMid = env->GetStaticMethodID(*m_fileUtilClass, "isDirectory", "(Ljava/lang/String;)Z");
m_isFileMid = env->GetStaticMethodID(*m_fileUtilClass, "isFile", "(Ljava/lang/String;)Z");
m_existsMid = env->GetStaticMethodID(*m_fileUtilClass, "exists", "(Ljava/lang/String;)Z");
}
int OpenContentUri(const std::filesystem::path& uri) override
{
int fd = -1;
JNIUtils::fiberSafeJNICall([&](JNIEnv* env) {
jstring uriString = JNIUtils::toJString(env, uri);
fd = env->CallStaticIntMethod(*m_fileUtilClass, m_openContentUriMid, uriString);
env->DeleteLocalRef(uriString);
});
return fd;
}
std::vector<std::filesystem::path> ListFiles(const std::filesystem::path& uri) override
{
std::vector<std::filesystem::path> paths;
JNIUtils::fiberSafeJNICall([&](JNIEnv* env) {
jstring uriString = JNIUtils::toJString(env, uri);
jobjectArray pathsObjArray = static_cast<jobjectArray>(env->CallStaticObjectMethod(*m_fileUtilClass, m_listFilesMid, uriString));
env->DeleteLocalRef(uriString);
jsize arrayLength = env->GetArrayLength(pathsObjArray);
paths.reserve(arrayLength);
for (jsize i = 0; i < arrayLength; i++)
{
jstring pathStr = static_cast<jstring>(env->GetObjectArrayElement(pathsObjArray, i));
paths.push_back(JNIUtils::toString(env, pathStr));
env->DeleteLocalRef(pathStr);
}
env->DeleteLocalRef(pathsObjArray);
});
return paths;
}
bool IsDirectory(const std::filesystem::path& uri) override
{
return CallBooleanFunction(uri, m_isDirectoryMid);
}
bool IsFile(const std::filesystem::path& uri) override
{
return CallBooleanFunction(uri, m_isFileMid);
}
bool Exists(const std::filesystem::path& uri) override
{
return CallBooleanFunction(uri, m_existsMid);
}
};
@@ -0,0 +1,71 @@
#pragma once
#include "GameTitleLoader.h"
#include "JNIUtils.h"
#include <android/bitmap.h>
// TODO: Refactor this:
class AndroidGameTitleLoadedCallback : public GameTitleLoadedCallback
{
jmethodID m_onGameTitleLoadedMID;
JNIUtils::Scopedjobject m_gameTitleLoadedCallbackObj;
jmethodID m_gameConstructorMID;
JNIUtils::Scopedjclass m_gamejclass{"info/cemu/cemu/nativeinterface/NativeGameTitles$Game"};
jmethodID m_createBitmapMID;
JNIUtils::Scopedjclass m_bitmapClass{"android/graphics/Bitmap"};
JNIUtils::Scopedjobject m_bitmapFormat;
public:
AndroidGameTitleLoadedCallback(jmethodID onGameTitleLoadedMID, jobject gameTitleLoadedCallbackObj)
: m_onGameTitleLoadedMID(onGameTitleLoadedMID),
m_gameTitleLoadedCallbackObj(gameTitleLoadedCallbackObj)
{
JNIUtils::ScopedJNIENV env;
m_bitmapFormat = JNIUtils::getEnumValue(env, "android/graphics/Bitmap$Config", "ARGB_8888");
m_gameConstructorMID = env->GetMethodID(*m_gamejclass, "<init>", "(JLjava/lang/String;Ljava/lang/String;SSISSSIZLandroid/graphics/Bitmap;)V");
m_createBitmapMID = env->GetStaticMethodID(*m_bitmapClass, "createBitmap", "([IIILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
}
void onTitleLoaded(const Game& game, const std::shared_ptr<Image>& icon) override
{
static JNIUtils::ScopedJNIENV env;
jstring name = JNIUtils::toJString(env, game.name);
jstring path = game.path.has_value() ? JNIUtils::toJString(env, game.path.value()) : nullptr;
jobject bitmap = nullptr;
sint32 lastPlayedYear = 0, lastPlayedMonth = 0, lastPlayedDay = 0;
if (game.lastPlayed.has_value())
{
lastPlayedYear = static_cast<int>(game.lastPlayed->year());
lastPlayedMonth = static_cast<unsigned int>(game.lastPlayed->month());
lastPlayedDay = static_cast<unsigned int>(game.lastPlayed->day());
}
if (icon)
{
jintArray jIconData = env->NewIntArray(icon->m_width * icon->m_height);
env->SetIntArrayRegion(jIconData, 0, icon->m_width * icon->m_height, icon->m_colors);
bitmap = env->CallStaticObjectMethod(*m_bitmapClass, m_createBitmapMID, jIconData, icon->m_width, icon->m_height, *m_bitmapFormat);
env->DeleteLocalRef(jIconData);
}
jobject gamejobject = env->NewObject(
*m_gamejclass,
m_gameConstructorMID,
game.titleId,
path,
name,
game.version,
game.dlc,
static_cast<sint32>(game.region),
lastPlayedYear,
lastPlayedMonth,
lastPlayedDay,
game.minutesPlayed,
game.isFavorite,
bitmap);
env->CallVoidMethod(*m_gameTitleLoadedCallbackObj, m_onGameTitleLoadedMID, gamejobject);
env->DeleteLocalRef(gamejobject);
if (bitmap != nullptr)
env->DeleteLocalRef(bitmap);
if (path != nullptr)
env->DeleteLocalRef(path);
env->DeleteLocalRef(name);
}
};
@@ -0,0 +1,26 @@
#include "AndroidSwkbdCallbacks.h"
#include "JNIUtils.h"
AndroidSwkbdCallbacks::AndroidSwkbdCallbacks()
{
JNIUtils::ScopedJNIENV env;
m_emulationActivityClass = JNIUtils::Scopedjclass("info/cemu/cemu/emulation/EmulationActivity");
m_showSoftwareKeyboardMethodID = env->GetStaticMethodID(*m_emulationActivityClass, "showEmulationTextInput", "(Ljava/lang/String;I)V");
m_hideSoftwareKeyboardMethodID = env->GetStaticMethodID(*m_emulationActivityClass, "hideEmulationTextInput", "()V");
}
void AndroidSwkbdCallbacks::showSoftwareKeyboard(const std::string& initialText, sint32 maxLength)
{
JNIUtils::fiberSafeJNICall([&](JNIEnv* env) {
jstring j_initialText = JNIUtils::toJString(env, initialText);
JNIUtils::ScopedJNIENV()->CallStaticVoidMethod(*m_emulationActivityClass, m_showSoftwareKeyboardMethodID, j_initialText, maxLength);
env->DeleteLocalRef(j_initialText);
});
}
void AndroidSwkbdCallbacks::hideSoftwareKeyboard()
{
JNIUtils::fiberSafeJNICall([&](JNIEnv* env) {
env->CallStaticVoidMethod(*m_emulationActivityClass, m_hideSoftwareKeyboardMethodID);
});
}
@@ -0,0 +1,15 @@
#pragma once
#include "Cafe/OS/libs/swkbd/swkbd.h"
#include "JNIUtils.h"
class AndroidSwkbdCallbacks : public swkbd::swkbdCallbacks
{
JNIUtils::Scopedjclass m_emulationActivityClass;
jmethodID m_showSoftwareKeyboardMethodID;
jmethodID m_hideSoftwareKeyboardMethodID;
public:
AndroidSwkbdCallbacks();
void showSoftwareKeyboard(const std::string& initialText, sint32 maxLength) override;
void hideSoftwareKeyboard() override;
};
@@ -0,0 +1,33 @@
add_library(CemuAndroid SHARED
AndroidAudio.cpp
AndroidEmulatedController.cpp
AndroidSwkbdCallbacks.cpp
CompressTitleCallbacks.cpp
GameTitleLoader.cpp
Image.cpp
JNIUtils.cpp
NativeActiveSettings.cpp
NativeAccount.cpp
NativeEmulation.cpp
NativeGameTitles.cpp
NativeGraphicPacks.cpp
NativeInput.cpp
NativeLib.cpp
NativeLocalization.cpp
NativeLogging.cpp
NativeSettings.cpp
NativeSwkbd.cpp
WuaConverter.cpp
)
target_link_libraries(CemuAndroid PRIVATE
-landroid
CemuCommon
CemuAudio
CemuComponents
CemuCafe
CemuBin
CemuGui
ZArchive::zarchive
stb
)
@@ -0,0 +1,20 @@
#include "CompressTitleCallbacks.h"
CompressTitleCallbacks::CompressTitleCallbacks(jobject compressTitleCallbacks)
: m_compressTitleCallbacks{compressTitleCallbacks}
{
JNIUtils::ScopedJNIENV env;
JNIUtils::Scopedjclass compressTitleCallbacksClass("info/cemu/cemu/nativeinterface/NativeGameTitles$TitleCompressCallbacks");
m_onFinishedMID = env->GetMethodID(*compressTitleCallbacksClass, "onFinished", "()V");
m_onErrorMID = env->GetMethodID(*compressTitleCallbacksClass, "onError", "()V");
}
void CompressTitleCallbacks::onFinished()
{
JNIUtils::ScopedJNIENV()->CallVoidMethod(*m_compressTitleCallbacks, m_onFinishedMID);
}
void CompressTitleCallbacks::onError()
{
JNIUtils::ScopedJNIENV()->CallVoidMethod(*m_compressTitleCallbacks, m_onErrorMID);
}

Some files were not shown because too many files have changed in this diff Show More