mirror of
https://github.com/izzy2lost/WeeU.git
synced 2026-07-06 00:19:59 -07:00
devlo: snapshot for thread user-thread-20251020073048
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
/build
|
||||
/src/main/assets/hash.txt
|
||||
*.po
|
||||
*.pot
|
||||
@@ -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)
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
-dontobfuscate
|
||||
+14
@@ -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
Reference in New Issue
Block a user