Compare commits
34 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71a0fc21ab | ||
|
|
9b2aebee25 | ||
|
|
c793a9c95c | ||
|
|
8a1f3b7c41 | ||
|
|
adad1d88d6 | ||
|
|
6be08026c0 | ||
|
|
aa4003cdc8 | ||
|
|
ca6e4b8802 | ||
|
|
d5129d9347 | ||
|
|
17693651b3 | ||
|
|
c4a0dbfb20 | ||
|
|
b6f2490288 | ||
|
|
ea716eb5cc | ||
|
|
6b898c6d69 | ||
|
|
bfdc9e312f | ||
|
|
fa5dfcb712 | ||
|
|
cb1fd1bad8 | ||
|
|
d8609eef89 | ||
|
|
f759ff3a5c | ||
|
|
72d9dc9a3f | ||
|
|
55b543f466 | ||
|
|
3cce51d25b | ||
|
|
4d395b3b72 | ||
|
|
5a0d4e1d38 | ||
|
|
b3e2c9f9f1 | ||
|
|
2a1acbfb4d | ||
|
|
a57150afbd | ||
|
|
60cc611f38 | ||
|
|
064bad6ddf | ||
|
|
007c3fa7df | ||
|
|
74671186bf | ||
|
|
6c34adb1de | ||
|
|
107aa52cdb | ||
|
|
a64ad8315f |
@@ -3,4 +3,4 @@
|
||||
|
||||
[codespell]
|
||||
skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES
|
||||
ignore-words-list = aci,allright,ba,deques,froms,hda,inout,lod,masia,nam,nax,nd,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink
|
||||
ignore-words-list = aci,allright,ba,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink
|
||||
|
||||
10
src/android/app/src/ea/res/drawable/ic_multiplayer.xml
Normal file
10
src/android/app/src/ea/res/drawable/ic_multiplayer.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M0,720L0,667Q0,628.43 41.5,604.22Q83,580 150.38,580Q162.54,580 173.77,580.5Q185,581 196,582.65Q188,600 184,617.82Q180,635.63 180,655L180,720L0,720ZM240,720L240,655Q240,623 257.5,596.5Q275,570 307,550Q339,530 383.5,520Q428,510 480,510Q533,510 577.5,520Q622,530 654,550Q686,570 703,596.5Q720,623 720,655L720,720L240,720ZM780,720L780,655Q780,635.14 776.5,617.57Q773,600 765,582.73Q776,581 787.17,580.5Q798.34,580 810,580Q877.5,580 918.75,603.77Q960,627.54 960,667L960,720L780,720ZM300,660L660,660L660,654Q660,617 609.5,593.5Q559,570 480,570Q401,570 350.5,593.5Q300,617 300,655L300,660ZM149.57,550Q121,550 100.5,529.44Q80,508.88 80,480Q80,451 100.56,430.5Q121.13,410 150,410Q179,410 199.5,430.5Q220,451 220,480.43Q220,509 199.5,529.5Q179,550 149.57,550ZM809.57,550Q781,550 760.5,529.44Q740,508.88 740,480Q740,451 760.56,430.5Q781.13,410 810,410Q839,410 859.5,430.5Q880,451 880,480.43Q880,509 859.5,529.5Q839,550 809.57,550ZM480,480Q430,480 395,445Q360,410 360,360Q360,309 395,274.5Q430,240 480,240Q531,240 565.5,274.5Q600,309 600,360Q600,410 565.5,445Q531,480 480,480ZM480.35,420Q506,420 523,402.65Q540,385.3 540,359.65Q540,334 522.85,317Q505.7,300 480.35,300Q455,300 437.5,317.15Q420,334.3 420,359.65Q420,385 437.35,402.5Q454.7,420 480.35,420ZM480,660L480,660Q480,660 480,660Q480,660 480,660Q480,660 480,660Q480,660 480,660L480,660L480,660ZM480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Z"/>
|
||||
</vector>
|
||||
@@ -11,6 +11,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
<uses-feature android:name="android.software.leanback" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.vulkan.version" android:version="0x401000" android:required="true" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
|
||||
@@ -223,6 +223,8 @@ object NativeLibrary {
|
||||
|
||||
external fun getCompany(filename: String): String
|
||||
|
||||
external fun isHomebrew(filename: String): Boolean
|
||||
|
||||
external fun setAppDirectory(directory: String)
|
||||
|
||||
external fun initializeGpuDriver(
|
||||
@@ -464,6 +466,17 @@ object NativeLibrary {
|
||||
*/
|
||||
external fun submitInlineKeyboardInput(key_code: Int)
|
||||
|
||||
/**
|
||||
* Connects to a room (similar with desktop version's "direct connect")
|
||||
*/
|
||||
external fun connectToRoom(nickname: String, server_addr: String, server_port: Int, password: String)
|
||||
|
||||
/**
|
||||
* Returns the state of the room member (client)
|
||||
* @return The state as a string
|
||||
*/
|
||||
external fun getRoomMemberState(): String
|
||||
|
||||
/**
|
||||
* Button type for use in onTouchEvent
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,8 @@ import android.content.Context
|
||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||
import org.yuzu.yuzu_emu.utils.DocumentsTree
|
||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||
import org.yuzu.yuzu_emu.utils.MultiplayerHelper
|
||||
import org.yuzu.yuzu_emu.utils.NetworkHelper
|
||||
import java.io.File
|
||||
|
||||
fun Context.getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir
|
||||
@@ -46,6 +48,8 @@ class YuzuApplication : Application() {
|
||||
documentsTree = DocumentsTree()
|
||||
DirectoryInitialization.start(applicationContext)
|
||||
GpuDriverHelper.initializeDriverParameters(applicationContext)
|
||||
NetworkHelper.getRoute(applicationContext)
|
||||
MultiplayerHelper.initRoom(applicationContext)
|
||||
NativeLibrary.logDeviceInfo()
|
||||
|
||||
createNotificationChannels();
|
||||
|
||||
@@ -6,15 +6,12 @@ package org.yuzu.yuzu_emu.activities
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Rect
|
||||
import android.hardware.Sensor
|
||||
import android.hardware.SensorEvent
|
||||
import android.hardware.SensorEventListener
|
||||
import android.hardware.SensorManager
|
||||
import android.hardware.display.DisplayManager
|
||||
import android.os.Bundle
|
||||
import android.view.Display
|
||||
import android.view.InputDevice
|
||||
import android.view.KeyEvent
|
||||
import android.view.MotionEvent
|
||||
@@ -23,7 +20,6 @@ import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
@@ -39,7 +35,6 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
|
||||
import org.yuzu.yuzu_emu.fragments.EmulationFragment
|
||||
import org.yuzu.yuzu_emu.model.Game
|
||||
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
|
||||
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
|
||||
import org.yuzu.yuzu_emu.utils.ForegroundService
|
||||
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||
import org.yuzu.yuzu_emu.utils.NfcReader
|
||||
@@ -148,11 +143,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
super.onResume()
|
||||
nfcReader.startScanning()
|
||||
startMotionSensorListener()
|
||||
|
||||
NativeLibrary.notifyOrientationChange(
|
||||
EmulationMenuSettings.landscapeScreenLayout,
|
||||
getAdjustedRotation()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
@@ -258,23 +248,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
|
||||
override fun onAccuracyChanged(sensor: Sensor, i: Int) {}
|
||||
|
||||
private fun getAdjustedRotation():Int {
|
||||
val rotation = getSystemService<DisplayManager>()!!.getDisplay(Display.DEFAULT_DISPLAY).rotation
|
||||
val config: Configuration = resources.configuration
|
||||
|
||||
if ((config.screenLayout and Configuration.SCREENLAYOUT_LONG_YES) != 0 ||
|
||||
(config.screenLayout and Configuration.SCREENLAYOUT_LONG_NO) == 0) {
|
||||
return rotation
|
||||
}
|
||||
when (rotation) {
|
||||
Surface.ROTATION_0 -> return Surface.ROTATION_90
|
||||
Surface.ROTATION_90 -> return Surface.ROTATION_0
|
||||
Surface.ROTATION_180 -> return Surface.ROTATION_270
|
||||
Surface.ROTATION_270 -> return Surface.ROTATION_180
|
||||
}
|
||||
return rotation
|
||||
}
|
||||
|
||||
private fun restoreState(savedInstanceState: Bundle) {
|
||||
game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!!
|
||||
}
|
||||
|
||||
@@ -107,7 +107,9 @@ class Settings {
|
||||
const val SECTION_RENDERER = "Renderer"
|
||||
const val SECTION_AUDIO = "Audio"
|
||||
const val SECTION_CPU = "Cpu"
|
||||
const val SECTION_NETWORK = "Network"
|
||||
const val SECTION_THEME = "Theme"
|
||||
const val SECTION_MULTIPLAYER = "Multiplayer"
|
||||
const val SECTION_DEBUG = "Debug"
|
||||
|
||||
const val PREF_OVERLAY_INIT = "OverlayInit"
|
||||
@@ -130,6 +132,14 @@ class Settings {
|
||||
const val PREF_BUTTON_TOGGLE_13 = "buttonToggle13"
|
||||
const val PREF_BUTTON_TOGGLE_14 = "buttonToggle14"
|
||||
|
||||
const val PREF_FORCE_WIFI = "Network_ForceWifi"
|
||||
|
||||
const val PREF_ROOM_ADDRESS = "MultiplayerRoom_ServerAddress"
|
||||
const val PREF_ROOM_PORT = "MultiplayerRoom_ServerPort"
|
||||
const val PREF_ROOM_NICKNAME = "MultiplayerRoom_Nickname"
|
||||
const val PREF_ROOM_PASSWORD = "MultiplayerRoom_Password"
|
||||
const val PREF_ROOM_CONNECT_ON_START = "MultiplayerRoom_ConnectOnStart"
|
||||
|
||||
const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
|
||||
const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
|
||||
const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics"
|
||||
@@ -151,6 +161,7 @@ class Settings {
|
||||
SECTION_SYSTEM,
|
||||
SECTION_RENDERER,
|
||||
SECTION_AUDIO,
|
||||
SECTION_NETWORK,
|
||||
SECTION_CPU
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@ enum class StringSetting(
|
||||
override val section: String,
|
||||
override val defaultValue: String
|
||||
) : AbstractStringSetting {
|
||||
CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0");
|
||||
CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0"),
|
||||
NETWORK_ROUTE("network_route", Settings.SECTION_NETWORK, ";;;;");
|
||||
|
||||
override var string: String = defaultValue
|
||||
|
||||
@@ -27,7 +28,8 @@ enum class StringSetting(
|
||||
|
||||
companion object {
|
||||
private val NOT_RUNTIME_EDITABLE = listOf(
|
||||
CUSTOM_RTC
|
||||
CUSTOM_RTC,
|
||||
NETWORK_ROUTE
|
||||
)
|
||||
|
||||
fun from(key: String): StringSetting? = StringSetting.values().firstOrNull { it.key == key }
|
||||
|
||||
@@ -35,5 +35,6 @@ abstract class SettingsItem(
|
||||
const val TYPE_STRING_SINGLE_CHOICE = 5
|
||||
const val TYPE_DATETIME_SETTING = 6
|
||||
const val TYPE_RUNNABLE = 7
|
||||
const val TYPE_TEXT_SETTING = 8
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.model.view
|
||||
|
||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
|
||||
|
||||
class TextSetting(
|
||||
setting: AbstractSetting?,
|
||||
titleId: Int,
|
||||
descriptionId: Int,
|
||||
val key: String? = null,
|
||||
private val defaultValue: String? = null
|
||||
) : SettingsItem(setting, titleId, descriptionId) {
|
||||
override val type = TYPE_TEXT_SETTING
|
||||
|
||||
val value: String
|
||||
get() = if (setting != null) {
|
||||
val setting = setting as AbstractStringSetting
|
||||
setting.string
|
||||
} else {
|
||||
defaultValue!!
|
||||
}
|
||||
|
||||
fun setSelectedValue(string: String): AbstractStringSetting {
|
||||
val stringSetting = setting as AbstractStringSetting
|
||||
stringSetting.string = string
|
||||
return stringSetting
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,14 @@ import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.icu.util.Calendar
|
||||
import android.icu.util.TimeZone
|
||||
import android.text.TextWatcher
|
||||
import android.text.format.DateFormat
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.setFragmentResultListener
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.datepicker.MaterialDatePicker
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
@@ -21,6 +22,7 @@ import com.google.android.material.slider.Slider
|
||||
import com.google.android.material.timepicker.MaterialTimePicker
|
||||
import com.google.android.material.timepicker.TimeFormat
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.DialogEditTextBinding
|
||||
import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
|
||||
@@ -83,6 +85,10 @@ class SettingsAdapter(
|
||||
RunnableViewHolder(ListItemSettingBinding.inflate(inflater), this)
|
||||
}
|
||||
|
||||
SettingsItem.TYPE_TEXT_SETTING -> {
|
||||
TextSettingViewHolder(ListItemSettingBinding.inflate(inflater), this)
|
||||
}
|
||||
|
||||
else -> {
|
||||
// TODO: Create an error view since we can't return null now
|
||||
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
|
||||
@@ -167,6 +173,7 @@ class SettingsAdapter(
|
||||
.setSelection(storedTime)
|
||||
.setTitleText(R.string.select_rtc_date)
|
||||
.build()
|
||||
|
||||
val timePicker: MaterialTimePicker = MaterialTimePicker.Builder()
|
||||
.setTimeFormat(timeFormat)
|
||||
.setHour(calendar.get(Calendar.HOUR_OF_DAY))
|
||||
@@ -199,6 +206,38 @@ class SettingsAdapter(
|
||||
)
|
||||
}
|
||||
|
||||
fun onTextSettingClick(item: TextSetting, position: Int) {
|
||||
clickedItem = item
|
||||
clickedPosition = position
|
||||
var value = item.value
|
||||
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val editTextBinding = DialogEditTextBinding.inflate(inflater)
|
||||
|
||||
editTextBinding.editText.setText(value)
|
||||
|
||||
editTextBinding.editText.doAfterTextChanged {
|
||||
value = it.toString()
|
||||
}
|
||||
|
||||
dialog = MaterialAlertDialogBuilder(context)
|
||||
.setTitle(item.nameId)
|
||||
.setView(editTextBinding.root)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
if (item.value != value) {
|
||||
fragmentView.onSettingChanged()
|
||||
}
|
||||
notifyItemChanged(clickedPosition)
|
||||
|
||||
val setting = item.setSelectedValue(value)
|
||||
fragmentView.putSetting(setting)
|
||||
clickedItem = null
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, defaultCancelListener)
|
||||
.show()
|
||||
|
||||
}
|
||||
|
||||
fun onSliderClick(item: SliderSetting, position: Int) {
|
||||
clickedItem = item
|
||||
clickedPosition = position
|
||||
|
||||
@@ -7,12 +7,12 @@ import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import android.text.TextUtils
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||
@@ -67,6 +67,8 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
||||
Settings.SECTION_SYSTEM -> addSystemSettings(sl)
|
||||
Settings.SECTION_RENDERER -> addGraphicsSettings(sl)
|
||||
Settings.SECTION_AUDIO -> addAudioSettings(sl)
|
||||
Settings.SECTION_NETWORK -> addNetworkSettings(sl)
|
||||
Settings.SECTION_MULTIPLAYER -> addMultiplayerSettings(sl)
|
||||
Settings.SECTION_THEME -> addThemeSettings(sl)
|
||||
Settings.SECTION_DEBUG -> addDebugSettings(sl)
|
||||
else -> {
|
||||
@@ -102,6 +104,13 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
||||
Settings.SECTION_RENDERER
|
||||
)
|
||||
)
|
||||
add(
|
||||
SubmenuSetting(
|
||||
R.string.preferences_network,
|
||||
0,
|
||||
Settings.SECTION_NETWORK
|
||||
)
|
||||
)
|
||||
add(
|
||||
SubmenuSetting(
|
||||
R.string.preferences_audio,
|
||||
@@ -437,6 +446,179 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addNetworkSettings(sl: ArrayList<SettingsItem>) {
|
||||
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_network))
|
||||
|
||||
sl.apply {
|
||||
val forceWifi: AbstractBooleanSetting = object : AbstractBooleanSetting {
|
||||
override var boolean: Boolean
|
||||
get() = preferences.getBoolean(Settings.PREF_FORCE_WIFI, false)
|
||||
set(value) {
|
||||
preferences.edit()
|
||||
.putBoolean(Settings.PREF_FORCE_WIFI, value)
|
||||
.apply()
|
||||
}
|
||||
override val key: String? = null
|
||||
override val section: String? = null
|
||||
override val isRuntimeEditable: Boolean = false
|
||||
override val valueAsString: String
|
||||
get() = preferences.getBoolean(Settings.PREF_FORCE_WIFI, false).toString()
|
||||
override val defaultValue: Any = false
|
||||
}
|
||||
|
||||
add(
|
||||
SwitchSetting(
|
||||
forceWifi,
|
||||
R.string.set_force_wifi,
|
||||
R.string.force_wifi_desc,
|
||||
"",
|
||||
false
|
||||
)
|
||||
)
|
||||
add(
|
||||
TextSetting(
|
||||
StringSetting.NETWORK_ROUTE,
|
||||
R.string.set_network_route,
|
||||
R.string.network_route_desc,
|
||||
StringSetting.NETWORK_ROUTE.key,
|
||||
StringSetting.NETWORK_ROUTE.defaultValue
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addMultiplayerSettings(sl: ArrayList<SettingsItem>) {
|
||||
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_multiplayer))
|
||||
|
||||
sl.apply {
|
||||
val serverAddress: AbstractStringSetting = object : AbstractStringSetting {
|
||||
override var string: String
|
||||
get() = preferences.getString(Settings.PREF_ROOM_ADDRESS, "") ?: ""
|
||||
set(value) {
|
||||
preferences.edit()
|
||||
.putString(Settings.PREF_ROOM_ADDRESS, value)
|
||||
.apply()
|
||||
}
|
||||
override val key: String? = null
|
||||
override val section: String? = null
|
||||
override val isRuntimeEditable: Boolean = false
|
||||
override val valueAsString: String
|
||||
get() = preferences.getString(Settings.PREF_ROOM_ADDRESS, "") ?: ""
|
||||
override val defaultValue: Any = -1
|
||||
}
|
||||
|
||||
val serverPort: AbstractStringSetting = object : AbstractStringSetting {
|
||||
override var string: String
|
||||
get() = preferences.getString(Settings.PREF_ROOM_PORT, "24872") ?: "24872"
|
||||
set(value) {
|
||||
preferences.edit()
|
||||
.putString(Settings.PREF_ROOM_PORT, value)
|
||||
.apply()
|
||||
}
|
||||
override val key: String? = null
|
||||
override val section: String? = null
|
||||
override val isRuntimeEditable: Boolean = false
|
||||
override val valueAsString: String
|
||||
get() = preferences.getString(Settings.PREF_ROOM_PORT, "24872") ?: "24872"
|
||||
override val defaultValue: Any = "24872"
|
||||
}
|
||||
|
||||
val nickname: AbstractStringSetting = object : AbstractStringSetting {
|
||||
override var string: String
|
||||
get() = preferences.getString(Settings.PREF_ROOM_NICKNAME, "") ?: ""
|
||||
set(value) {
|
||||
preferences.edit()
|
||||
.putString(Settings.PREF_ROOM_NICKNAME, value)
|
||||
.apply()
|
||||
}
|
||||
override val key: String? = null
|
||||
override val section: String? = null
|
||||
override val isRuntimeEditable: Boolean = false
|
||||
override val valueAsString: String
|
||||
get() = preferences.getString(Settings.PREF_ROOM_NICKNAME, "") ?: ""
|
||||
override val defaultValue: Any = -1
|
||||
}
|
||||
|
||||
val password: AbstractStringSetting = object : AbstractStringSetting {
|
||||
override var string: String
|
||||
get() = preferences.getString(Settings.PREF_ROOM_PASSWORD, "") ?: ""
|
||||
set(value) {
|
||||
preferences.edit()
|
||||
.putString(Settings.PREF_ROOM_PASSWORD, value)
|
||||
.apply()
|
||||
}
|
||||
override val key: String? = null
|
||||
override val section: String? = null
|
||||
override val isRuntimeEditable: Boolean = false
|
||||
override val valueAsString: String
|
||||
get() = preferences.getString(Settings.PREF_ROOM_PASSWORD, "") ?: ""
|
||||
override val defaultValue: Any = -1
|
||||
}
|
||||
|
||||
val connectOnStart: AbstractBooleanSetting = object : AbstractBooleanSetting {
|
||||
override var boolean: Boolean
|
||||
get() = preferences.getBoolean(Settings.PREF_ROOM_CONNECT_ON_START, false)
|
||||
set(value) {
|
||||
preferences.edit()
|
||||
.putBoolean(Settings.PREF_ROOM_CONNECT_ON_START, value)
|
||||
.apply()
|
||||
}
|
||||
override val key: String? = null
|
||||
override val section: String? = null
|
||||
override val isRuntimeEditable: Boolean = false
|
||||
override val valueAsString: String
|
||||
get() = preferences.getBoolean(Settings.PREF_ROOM_CONNECT_ON_START, false).toString()
|
||||
override val defaultValue: Any = -1
|
||||
}
|
||||
|
||||
add(
|
||||
TextSetting(
|
||||
serverAddress,
|
||||
R.string.multiplayer_room_server_address,
|
||||
0,
|
||||
"",
|
||||
""
|
||||
)
|
||||
)
|
||||
add(
|
||||
TextSetting(
|
||||
serverPort,
|
||||
R.string.multiplayer_room_server_port,
|
||||
0,
|
||||
"",
|
||||
""
|
||||
)
|
||||
)
|
||||
add(
|
||||
TextSetting(
|
||||
nickname,
|
||||
R.string.multiplayer_room_nickname,
|
||||
0,
|
||||
"",
|
||||
""
|
||||
)
|
||||
)
|
||||
add(
|
||||
TextSetting(
|
||||
password,
|
||||
R.string.multiplayer_room_password,
|
||||
0,
|
||||
"",
|
||||
""
|
||||
)
|
||||
)
|
||||
add(
|
||||
SwitchSetting(
|
||||
connectOnStart,
|
||||
R.string.multiplayer_room_connect_on_start,
|
||||
0,
|
||||
"",
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addDebugSettings(sl: ArrayList<SettingsItem>) {
|
||||
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug))
|
||||
sl.apply {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
|
||||
|
||||
import android.view.View
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.TextSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
|
||||
class TextSettingViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
|
||||
SettingViewHolder(binding.root, adapter) {
|
||||
private lateinit var setting: TextSetting
|
||||
|
||||
override fun bind(item: SettingsItem) {
|
||||
setting = item as TextSetting
|
||||
|
||||
binding.textSettingName.setText(item.nameId)
|
||||
if (item.descriptionId != 0) {
|
||||
binding.textSettingDescription.setText(item.descriptionId)
|
||||
binding.textSettingDescription.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClick(clicked: View) {
|
||||
if (setting.isEditable) {
|
||||
adapter.onTextSettingClick(setting, bindingAdapterPosition)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLongClick(clicked: View): Boolean {
|
||||
if (setting.isEditable) {
|
||||
return adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -84,6 +84,11 @@ class HomeSettingsFragment : Fragment() {
|
||||
R.string.theme_and_color_description,
|
||||
R.drawable.ic_palette
|
||||
) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") },
|
||||
HomeSetting(
|
||||
R.string.preferences_multiplayer,
|
||||
R.string.multiplayer_description,
|
||||
R.drawable.ic_multiplayer
|
||||
) { SettingsActivity.launch(requireContext(), Settings.SECTION_MULTIPLAYER, "") },
|
||||
HomeSetting(
|
||||
R.string.install_gpu_driver,
|
||||
R.string.install_gpu_driver_description,
|
||||
|
||||
@@ -127,13 +127,7 @@ class SearchFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
R.id.chip_homebrew -> {
|
||||
baseList.filter {
|
||||
Log.error("Guh - ${it.path}")
|
||||
FileUtil.hasExtension(it.path, "nro")
|
||||
|| FileUtil.hasExtension(it.path, "nso")
|
||||
}
|
||||
}
|
||||
R.id.chip_homebrew -> baseList.filter { it.isHomebrew }
|
||||
|
||||
R.id.chip_retail -> baseList.filter {
|
||||
FileUtil.hasExtension(it.path, "xci")
|
||||
|
||||
@@ -16,7 +16,8 @@ class Game(
|
||||
val regions: String,
|
||||
val path: String,
|
||||
val gameId: String,
|
||||
val company: String
|
||||
val company: String,
|
||||
val isHomebrew: Boolean
|
||||
) : Parcelable {
|
||||
val keyAddedToLibraryTime get() = "${gameId}_AddedToLibraryTime"
|
||||
val keyLastPlayedTime get() = "${gameId}_LastPlayed"
|
||||
@@ -31,6 +32,7 @@ class Game(
|
||||
&& path == other.path
|
||||
&& gameId == other.gameId
|
||||
&& company == other.company
|
||||
&& isHomebrew == other.isHomebrew
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -13,6 +13,8 @@ import androidx.preference.PreferenceManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.MissingFieldException
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
@@ -20,6 +22,7 @@ import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.utils.GameHelper
|
||||
import java.util.Locale
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
class GamesViewModel : ViewModel() {
|
||||
private val _games = MutableLiveData<List<Game>>(emptyList())
|
||||
val games: LiveData<List<Game>> get() = _games
|
||||
@@ -49,7 +52,13 @@ class GamesViewModel : ViewModel() {
|
||||
if (storedGames!!.isNotEmpty()) {
|
||||
val deserializedGames = mutableSetOf<Game>()
|
||||
storedGames.forEach {
|
||||
val game: Game = Json.decodeFromString(it)
|
||||
val game: Game
|
||||
try {
|
||||
game = Json.decodeFromString(it)
|
||||
} catch (e: MissingFieldException) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
val gameExists =
|
||||
DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(game.path))
|
||||
?.exists()
|
||||
|
||||
@@ -765,18 +765,20 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
|
||||
// If we have API access, calculate the safe area to draw the overlay
|
||||
var cutoutLeft = 0
|
||||
var cutoutBottom = 0
|
||||
val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout
|
||||
if (insets != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
if (insets.boundingRectTop.bottom != 0 && insets.boundingRectTop.bottom > maxY / 2)
|
||||
insets.boundingRectTop.bottom.toFloat() else maxY
|
||||
if (insets.boundingRectRight.left != 0 && insets.boundingRectRight.left > maxX / 2)
|
||||
insets.boundingRectRight.left.toFloat() else maxX
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout
|
||||
if (insets != null) {
|
||||
if (insets.boundingRectTop.bottom != 0 && insets.boundingRectTop.bottom > maxY / 2)
|
||||
insets.boundingRectTop.bottom.toFloat() else maxY
|
||||
if (insets.boundingRectRight.left != 0 && insets.boundingRectRight.left > maxX / 2)
|
||||
insets.boundingRectRight.left.toFloat() else maxX
|
||||
|
||||
minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left
|
||||
minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom
|
||||
minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left
|
||||
minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom
|
||||
|
||||
cutoutLeft = insets.boundingRectRight.right - insets.boundingRectRight.left
|
||||
cutoutBottom = insets.boundingRectTop.top - insets.boundingRectTop.bottom
|
||||
cutoutLeft = insets.boundingRectRight.right - insets.boundingRectRight.left
|
||||
cutoutBottom = insets.boundingRectTop.top - insets.boundingRectTop.bottom
|
||||
}
|
||||
}
|
||||
|
||||
// This makes sure that if we have an inset on one side of the screen, we mirror it on
|
||||
|
||||
@@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.utils
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import androidx.preference.PreferenceManager
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
@@ -83,7 +82,8 @@ object GameHelper {
|
||||
NativeLibrary.getRegions(filePath),
|
||||
filePath,
|
||||
gameId,
|
||||
NativeLibrary.getCompany(filePath)
|
||||
NativeLibrary.getCompany(filePath),
|
||||
NativeLibrary.isHomebrew(filePath)
|
||||
)
|
||||
|
||||
val addedTime = preferences.getLong(newGame.keyAddedToLibraryTime, 0L)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.utils
|
||||
|
||||
import android.content.Context
|
||||
import androidx.preference.PreferenceManager
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||
|
||||
object MultiplayerHelper {
|
||||
fun initRoom(context: Context) {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
if(preferences.getBoolean(Settings.PREF_ROOM_CONNECT_ON_START, false)) {
|
||||
val addr = preferences.getString(Settings.PREF_ROOM_ADDRESS, "") ?: ""
|
||||
val port = preferences.getString(Settings.PREF_ROOM_PORT, "24872") ?: "24872"
|
||||
val nickname = preferences.getString(Settings.PREF_ROOM_NICKNAME, "") ?: ""
|
||||
val password = preferences.getString(Settings.PREF_ROOM_PASSWORD, "") ?: ""
|
||||
|
||||
NativeLibrary.connectToRoom(nickname, addr, port.toIntOrNull() ?: 0, password)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import androidx.preference.PreferenceManager
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||
|
||||
object NetworkHelper {
|
||||
/**
|
||||
* Gets available network interface info/route info - currently the active network info.
|
||||
* @return The route info separated by semicolons (interface, address, netmask, gateway), or null if no networks are available.
|
||||
*/
|
||||
fun getRoute(context: Context): String? {
|
||||
val connectivity =
|
||||
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
if(connectivity.isActiveNetworkMetered && preferences.getBoolean(Settings.PREF_FORCE_WIFI, false))
|
||||
return null
|
||||
|
||||
val lp = connectivity.getLinkProperties(connectivity.activeNetwork) ?: return null
|
||||
|
||||
val ifName = lp.interfaceName
|
||||
val addr = lp.linkAddresses[0]
|
||||
val cidr = addr.prefixLength
|
||||
|
||||
val bits = 0xffffffff xor ((1 shl 32 - cidr)).toLong() - 1
|
||||
val mask = String.format(
|
||||
"%d.%d.%d.%d",
|
||||
bits and 0x0000000000ff000000L shr 24,
|
||||
bits and 0x000000000000ff0000L shr 16,
|
||||
bits and 0x00000000000000ff00L shr 8,
|
||||
bits and 0x0000000000000000ffL shr 0
|
||||
)
|
||||
|
||||
val gw = lp.routes.last { it.isDefaultRoute }.gateway?.hostAddress
|
||||
|
||||
return "$ifName;$addr;$mask;$gw"
|
||||
}
|
||||
}
|
||||
@@ -244,6 +244,10 @@ void Config::ReadValues() {
|
||||
ReadSetting("Audio", Settings::values.audio_output_device_id);
|
||||
ReadSetting("Audio", Settings::values.volume);
|
||||
|
||||
// Network
|
||||
|
||||
Settings::values.network_route = config->GetString("Network", "network_route", "");
|
||||
|
||||
// Miscellaneous
|
||||
// log_filter has a different default here than from common
|
||||
Settings::values.log_filter = "*:Info";
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
|
||||
#include <android/api-level.h>
|
||||
#include <android/native_window_jni.h>
|
||||
#include <core/loader/nro.h>
|
||||
#include <network/network.h>
|
||||
#include <network/room_member.h>
|
||||
|
||||
#include "common/detached_tasks.h"
|
||||
#include "common/dynamic_library.h"
|
||||
@@ -93,14 +96,6 @@ public:
|
||||
m_native_window = native_window;
|
||||
}
|
||||
|
||||
u32 ScreenRotation() const {
|
||||
return m_screen_rotation;
|
||||
}
|
||||
|
||||
void SetScreenRotation(u32 screen_rotation) {
|
||||
m_screen_rotation = screen_rotation;
|
||||
}
|
||||
|
||||
void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir,
|
||||
const std::string& custom_driver_name,
|
||||
const std::string& file_redirect_dir) {
|
||||
@@ -281,6 +276,10 @@ public:
|
||||
return GetRomMetadata(path).icon;
|
||||
}
|
||||
|
||||
bool GetIsHomebrew(const std::string& path) {
|
||||
return GetRomMetadata(path).isHomebrew;
|
||||
}
|
||||
|
||||
void ResetRomMetadata() {
|
||||
m_rom_metadata_cache.clear();
|
||||
}
|
||||
@@ -344,10 +343,35 @@ public:
|
||||
return m_software_keyboard;
|
||||
}
|
||||
|
||||
void DirectConnectToRoom(const std::string& nickname, const char* server_addr = "127.0.0.1",
|
||||
u16 server_port = Network::DefaultRoomPort,
|
||||
const std::string& password = "") {
|
||||
auto room_network = m_system.GetRoomNetwork();
|
||||
|
||||
if (const auto member = room_network.GetRoomMember().lock()) {
|
||||
// Prevent the user from trying to join a room while they are already joining.
|
||||
if (member->GetState() == Network::RoomMember::State::Joining ||
|
||||
member->IsConnected()) {
|
||||
return;
|
||||
} else {
|
||||
member->Join(nickname, server_addr, server_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Network::RoomMember::State GetRoomMemberState() {
|
||||
if (const auto member = m_system.GetRoomNetwork().GetRoomMember().lock()) {
|
||||
return member->GetState();
|
||||
} else {
|
||||
return Network::RoomMember::State::Idle;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct RomMetadata {
|
||||
std::string title;
|
||||
std::vector<u8> icon;
|
||||
bool isHomebrew;
|
||||
};
|
||||
|
||||
RomMetadata GetRomMetadata(const std::string& path) {
|
||||
@@ -360,11 +384,17 @@ private:
|
||||
|
||||
RomMetadata CacheRomMetadata(const std::string& path) {
|
||||
const auto file = Core::GetGameFileFromPath(m_vfs, path);
|
||||
const auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
|
||||
auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
|
||||
|
||||
RomMetadata entry;
|
||||
loader->ReadTitle(entry.title);
|
||||
loader->ReadIcon(entry.icon);
|
||||
if (loader->GetFileType() == Loader::FileType::NRO) {
|
||||
auto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get());
|
||||
entry.isHomebrew = loader_nro->IsHomebrew();
|
||||
} else {
|
||||
entry.isHomebrew = false;
|
||||
}
|
||||
|
||||
m_rom_metadata_cache[path] = entry;
|
||||
|
||||
@@ -388,7 +418,6 @@ private:
|
||||
// Window management
|
||||
std::unique_ptr<EmuWindow_Android> m_window;
|
||||
ANativeWindow* m_native_window{};
|
||||
u32 m_screen_rotation{};
|
||||
|
||||
// Core emulation
|
||||
Core::System m_system;
|
||||
@@ -414,10 +443,6 @@ private:
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
u32 GetAndroidScreenRotation() {
|
||||
return EmulationSession::GetInstance().ScreenRotation();
|
||||
}
|
||||
|
||||
static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
|
||||
Common::Log::Initialize();
|
||||
Common::Log::SetColorConsoleBackendEnabled(true);
|
||||
@@ -461,13 +486,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env,
|
||||
EmulationSession::GetInstance().SurfaceChanged();
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_notifyOrientationChange(JNIEnv* env,
|
||||
[[maybe_unused]] jclass clazz,
|
||||
jint layout_option,
|
||||
jint rotation) {
|
||||
return EmulationSession::GetInstance().SetScreenRotation(static_cast<u32>(rotation));
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env,
|
||||
[[maybe_unused]] jclass clazz,
|
||||
jstring j_directory) {
|
||||
@@ -662,6 +680,12 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany([[maybe_unused]] JNIEnv
|
||||
return env->NewStringUTF("");
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew([[maybe_unused]] JNIEnv* env,
|
||||
[[maybe_unused]] jclass clazz,
|
||||
[[maybe_unused]] jstring j_filename) {
|
||||
return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename));
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation
|
||||
[[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) {
|
||||
// Create the default config.ini.
|
||||
@@ -771,4 +795,40 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_submitInlineKeyboardInput(JNIEnv* env
|
||||
EmulationSession::GetInstance().SoftwareKeyboard()->SubmitInlineKeyboardInput(j_key_code);
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_connectToRoom(JNIEnv* env, jclass clazz,
|
||||
jstring nickname, jstring server_addr,
|
||||
jint server_port, jstring password) {
|
||||
EmulationSession::GetInstance().DirectConnectToRoom(GetJString(env, nickname),
|
||||
GetJString(env, server_addr).c_str(),
|
||||
server_port, GetJString(env, password));
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRoomMemberState(JNIEnv* env, jclass clazz) {
|
||||
auto state = EmulationSession::GetInstance().GetRoomMemberState();
|
||||
|
||||
std::string state_str{};
|
||||
|
||||
switch (state) {
|
||||
using State = Network::RoomMember::State;
|
||||
|
||||
case State::Uninitialized:
|
||||
state_str = "Uninitialized";
|
||||
break;
|
||||
case State::Idle:
|
||||
state_str = "Idle";
|
||||
break;
|
||||
case State::Joining:
|
||||
state_str = "Joining";
|
||||
break;
|
||||
case State::Joined:
|
||||
state_str = "Joined";
|
||||
break;
|
||||
case State::Moderator:
|
||||
state_str = "Moderator";
|
||||
break;
|
||||
}
|
||||
|
||||
return env->NewStringUTF(state_str.c_str());
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
10
src/android/app/src/main/res/drawable/ic_multiplayer.xml
Normal file
10
src/android/app/src/main/res/drawable/ic_multiplayer.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M0,720L0,667Q0,628.43 41.5,604.22Q83,580 150.38,580Q162.54,580 173.77,580.5Q185,581 196,582.65Q188,600 184,617.82Q180,635.63 180,655L180,720L0,720ZM240,720L240,655Q240,623 257.5,596.5Q275,570 307,550Q339,530 383.5,520Q428,510 480,510Q533,510 577.5,520Q622,530 654,550Q686,570 703,596.5Q720,623 720,655L720,720L240,720ZM780,720L780,655Q780,635.14 776.5,617.57Q773,600 765,582.73Q776,581 787.17,580.5Q798.34,580 810,580Q877.5,580 918.75,603.77Q960,627.54 960,667L960,720L780,720ZM300,660L660,660L660,654Q660,617 609.5,593.5Q559,570 480,570Q401,570 350.5,593.5Q300,617 300,655L300,660ZM149.57,550Q121,550 100.5,529.44Q80,508.88 80,480Q80,451 100.56,430.5Q121.13,410 150,410Q179,410 199.5,430.5Q220,451 220,480.43Q220,509 199.5,529.5Q179,550 149.57,550ZM809.57,550Q781,550 760.5,529.44Q740,508.88 740,480Q740,451 760.56,430.5Q781.13,410 810,410Q839,410 859.5,430.5Q880,451 880,480.43Q880,509 859.5,529.5Q839,550 809.57,550ZM480,480Q430,480 395,445Q360,410 360,360Q360,309 395,274.5Q430,240 480,240Q531,240 565.5,274.5Q600,309 600,360Q600,410 565.5,445Q531,480 480,480ZM480.35,420Q506,420 523,402.65Q540,385.3 540,359.65Q540,334 522.85,317Q505.7,300 480.35,300Q455,300 437.5,317.15Q420,334.3 420,359.65Q420,385 437.35,402.5Q454.7,420 480.35,420ZM480,660L480,660Q480,660 480,660Q480,660 480,660Q480,660 480,660Q480,660 480,660L480,660L480,660ZM480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Z"/>
|
||||
</vector>
|
||||
@@ -84,6 +84,7 @@
|
||||
<string name="open_user_folder">Open yuzu folder</string>
|
||||
<string name="open_user_folder_description">Manage yuzu\'s internal files</string>
|
||||
<string name="theme_and_color_description">Modify the look of the app</string>
|
||||
<string name="multiplayer_description">Play with your friends!</string>
|
||||
<string name="no_file_manager">No file manager found</string>
|
||||
<string name="notification_no_directory_link">Could not open yuzu directory</string>
|
||||
<string name="notification_no_directory_link_description">Please locate the user folder with the file manager\'s side panel manually.</string>
|
||||
@@ -151,6 +152,19 @@
|
||||
<string name="use_custom_rtc_description">Allows you to set a custom real-time clock separate from your current system time.</string>
|
||||
<string name="set_custom_rtc">Set custom RTC</string>
|
||||
|
||||
<!-- Network settings strings -->
|
||||
<string name="network_route_desc">Sets the default network route</string>
|
||||
<string name="set_network_route">Set network route</string>
|
||||
<string name="set_force_wifi">Force Wi-Fi</string>
|
||||
<string name="force_wifi_desc">Forces yuzu to use only Wi-Fi (non-metered connections in general)</string>
|
||||
|
||||
<!-- Multiplayer settings strings -->
|
||||
<string name="multiplayer_room_server_address">Server address</string>
|
||||
<string name="multiplayer_room_server_port">Port</string>
|
||||
<string name="multiplayer_room_nickname">Nickname</string>
|
||||
<string name="multiplayer_room_password">Password</string>
|
||||
<string name="multiplayer_room_connect_on_start">Connect on start</string>
|
||||
|
||||
<!-- Graphics settings strings -->
|
||||
<string name="renderer_api">API</string>
|
||||
<string name="renderer_accuracy">Accuracy level</string>
|
||||
@@ -203,9 +217,11 @@
|
||||
<string name="preferences_settings">Settings</string>
|
||||
<string name="preferences_general">General</string>
|
||||
<string name="preferences_system">System</string>
|
||||
<string name="preferences_network">Network</string>
|
||||
<string name="preferences_graphics">Graphics</string>
|
||||
<string name="preferences_audio">Audio</string>
|
||||
<string name="preferences_theme">Theme and color</string>
|
||||
<string name="preferences_multiplayer">Multiplayer</string>
|
||||
<string name="preferences_debug">Debug</string>
|
||||
|
||||
<!-- ROM loading errors -->
|
||||
|
||||
@@ -47,12 +47,4 @@ AudioRenderer::ADSP::ADSP& AudioCore::GetADSP() {
|
||||
return *adsp;
|
||||
}
|
||||
|
||||
void AudioCore::SetNVDECActive(bool active) {
|
||||
nvdec_active = active;
|
||||
}
|
||||
|
||||
bool AudioCore::IsNVDECActive() const {
|
||||
return nvdec_active;
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
@@ -57,18 +57,6 @@ public:
|
||||
*/
|
||||
AudioRenderer::ADSP::ADSP& GetADSP();
|
||||
|
||||
/**
|
||||
* Toggle NVDEC state, used to avoid stall in playback.
|
||||
*
|
||||
* @param active - Set true if nvdec is active, otherwise false.
|
||||
*/
|
||||
void SetNVDECActive(bool active);
|
||||
|
||||
/**
|
||||
* Get NVDEC state.
|
||||
*/
|
||||
bool IsNVDECActive() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Create the sinks on startup.
|
||||
@@ -83,8 +71,6 @@ private:
|
||||
std::unique_ptr<Sink::Sink> input_sink;
|
||||
/// The ADSP in the sysmodule
|
||||
std::unique_ptr<AudioRenderer::ADSP::ADSP> adsp;
|
||||
/// Is NVDec currently active?
|
||||
bool nvdec_active{false};
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
// Sub-directories contained within a yuzu data directory
|
||||
|
||||
#define AMIIBO_DIR "amiibo"
|
||||
#define CACHE_DIR "cache"
|
||||
#define CONFIG_DIR "config"
|
||||
#define DUMP_DIR "dump"
|
||||
|
||||
@@ -114,6 +114,7 @@ public:
|
||||
#endif
|
||||
|
||||
GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path);
|
||||
GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR);
|
||||
GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache);
|
||||
GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config);
|
||||
GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR);
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Common::FS {
|
||||
|
||||
enum class YuzuPath {
|
||||
YuzuDir, // Where yuzu stores its data.
|
||||
AmiiboDir, // Where Amiibo backups are stored.
|
||||
CacheDir, // Where cached filesystem data is stored.
|
||||
ConfigDir, // Where config files are stored.
|
||||
DumpDir, // Where dumped data is stored.
|
||||
|
||||
@@ -235,6 +235,7 @@ void RestoreGlobalState(bool is_powered_on) {
|
||||
values.bg_green.SetGlobal(true);
|
||||
values.bg_blue.SetGlobal(true);
|
||||
values.enable_compute_pipelines.SetGlobal(true);
|
||||
values.use_video_framerate.SetGlobal(true);
|
||||
|
||||
// System
|
||||
values.language_index.SetGlobal(true);
|
||||
|
||||
@@ -482,6 +482,7 @@ struct Values {
|
||||
SwitchableSetting<AstcRecompression, true> astc_recompression{
|
||||
AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3,
|
||||
"astc_recompression"};
|
||||
SwitchableSetting<bool> use_video_framerate{false, "use_video_framerate"};
|
||||
|
||||
SwitchableSetting<u8> bg_red{0, "bg_red"};
|
||||
SwitchableSetting<u8> bg_green{0, "bg_green"};
|
||||
@@ -580,6 +581,7 @@ struct Values {
|
||||
|
||||
// Network
|
||||
Setting<std::string> network_interface{std::string(), "network_interface"};
|
||||
Setting<std::string> network_route{std::string(), "network_route"};
|
||||
|
||||
// WebService
|
||||
Setting<bool> enable_telemetry{true, "enable_telemetry"};
|
||||
|
||||
@@ -48,7 +48,7 @@ std::array<u8, 0x10> ConstructFromRawString(std::string_view raw_string) {
|
||||
}
|
||||
|
||||
std::array<u8, 0x10> ConstructFromFormattedString(std::string_view formatted_string) {
|
||||
std::array<u8, 0x10> uuid;
|
||||
std::array<u8, 0x10> uuid{};
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
|
||||
@@ -216,6 +216,14 @@ struct System::Impl {
|
||||
}
|
||||
}
|
||||
|
||||
void SetNVDECActive(bool is_nvdec_active) {
|
||||
nvdec_active = is_nvdec_active;
|
||||
}
|
||||
|
||||
bool GetNVDECActive() {
|
||||
return nvdec_active;
|
||||
}
|
||||
|
||||
void InitializeDebugger(System& system, u16 port) {
|
||||
debugger = std::make_unique<Debugger>(system, port);
|
||||
}
|
||||
@@ -485,6 +493,8 @@ struct System::Impl {
|
||||
std::atomic_bool is_powered_on{};
|
||||
bool exit_lock = false;
|
||||
|
||||
bool nvdec_active{};
|
||||
|
||||
Reporter reporter;
|
||||
std::unique_ptr<Memory::CheatEngine> cheat_engine;
|
||||
std::unique_ptr<Tools::Freezer> memory_freezer;
|
||||
@@ -594,6 +604,14 @@ void System::UnstallApplication() {
|
||||
impl->UnstallApplication();
|
||||
}
|
||||
|
||||
void System::SetNVDECActive(bool is_nvdec_active) {
|
||||
impl->SetNVDECActive(is_nvdec_active);
|
||||
}
|
||||
|
||||
bool System::GetNVDECActive() {
|
||||
return impl->GetNVDECActive();
|
||||
}
|
||||
|
||||
void System::InitializeDebugger() {
|
||||
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
||||
}
|
||||
|
||||
@@ -189,6 +189,9 @@ public:
|
||||
std::unique_lock<std::mutex> StallApplication();
|
||||
void UnstallApplication();
|
||||
|
||||
void SetNVDECActive(bool is_nvdec_active);
|
||||
[[nodiscard]] bool GetNVDECActive();
|
||||
|
||||
/**
|
||||
* Initialize the debugger.
|
||||
*/
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/input.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -136,7 +141,7 @@ bool NfcDevice::LoadNfcTag(std::span<const u8> data) {
|
||||
if (!NFP::AmiiboCrypto::IsKeyAvailable()) {
|
||||
LOG_INFO(Service_NFC, "Loading amiibo without keys");
|
||||
memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
|
||||
BuildAmiiboWithoutKeys();
|
||||
BuildAmiiboWithoutKeys(tag_data, encrypted_tag_data);
|
||||
is_plain_amiibo = true;
|
||||
is_write_protected = true;
|
||||
return true;
|
||||
@@ -366,16 +371,25 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target
|
||||
|
||||
// The loaded amiibo is not encrypted
|
||||
if (is_plain_amiibo) {
|
||||
std::vector<u8> data(sizeof(NFP::NTAG215File));
|
||||
memcpy(data.data(), &tag_data, sizeof(tag_data));
|
||||
WriteBackupData(tag_data.uid, data);
|
||||
|
||||
device_state = DeviceState::TagMounted;
|
||||
mount_target = mount_target_;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
|
||||
LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state);
|
||||
return ResultCorruptedData;
|
||||
bool has_backup = HasBackup(encrypted_tag_data.uuid.uid).IsSuccess();
|
||||
LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup);
|
||||
return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData;
|
||||
}
|
||||
|
||||
std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
|
||||
memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
|
||||
WriteBackupData(encrypted_tag_data.uuid.uid, data);
|
||||
|
||||
device_state = DeviceState::TagMounted;
|
||||
mount_target = mount_target_;
|
||||
return ResultSuccess;
|
||||
@@ -470,6 +484,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) {
|
||||
std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
|
||||
if (is_plain_amiibo) {
|
||||
memcpy(data.data(), &tag_data, sizeof(tag_data));
|
||||
WriteBackupData(tag_data.uid, data);
|
||||
} else {
|
||||
if (!NFP::AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) {
|
||||
LOG_ERROR(Service_NFP, "Failed to encode data");
|
||||
@@ -477,6 +492,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) {
|
||||
}
|
||||
|
||||
memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
|
||||
WriteBackupData(encrypted_tag_data.uuid.uid, data);
|
||||
}
|
||||
|
||||
if (!npad_device->WriteNfc(data)) {
|
||||
@@ -488,7 +504,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) {
|
||||
}
|
||||
|
||||
Result NfcDevice::Restore() {
|
||||
if (device_state != DeviceState::TagMounted) {
|
||||
if (device_state != DeviceState::TagFound) {
|
||||
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
|
||||
if (device_state == DeviceState::TagRemoved) {
|
||||
return ResultTagRemoved;
|
||||
@@ -496,13 +512,59 @@ Result NfcDevice::Restore() {
|
||||
return ResultWrongDeviceState;
|
||||
}
|
||||
|
||||
if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
|
||||
LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
|
||||
return ResultWrongDeviceState;
|
||||
NFC::TagInfo tag_info{};
|
||||
std::array<u8, sizeof(NFP::EncryptedNTAG215File)> data{};
|
||||
Result result = GetTagInfo(tag_info, false);
|
||||
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: Load amiibo from backup on system
|
||||
LOG_ERROR(Service_NFP, "Not Implemented");
|
||||
result = ReadBackupData(tag_info.uuid, data);
|
||||
|
||||
if (result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
NFP::NTAG215File temporary_tag_data{};
|
||||
NFP::EncryptedNTAG215File temporary_encrypted_tag_data{};
|
||||
|
||||
// Fallback for encrypted amiibos without keys
|
||||
if (is_write_protected) {
|
||||
return ResultWriteAmiiboFailed;
|
||||
}
|
||||
|
||||
// Fallback for plain amiibos
|
||||
if (is_plain_amiibo) {
|
||||
LOG_INFO(Service_NFP, "Restoring backup of plain amiibo");
|
||||
memcpy(&temporary_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
|
||||
temporary_encrypted_tag_data = NFP::AmiiboCrypto::EncodedDataToNfcData(temporary_tag_data);
|
||||
}
|
||||
|
||||
if (!is_plain_amiibo) {
|
||||
LOG_INFO(Service_NFP, "Restoring backup of encrypted amiibo");
|
||||
temporary_tag_data = {};
|
||||
memcpy(&temporary_encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
|
||||
}
|
||||
|
||||
if (!NFP::AmiiboCrypto::IsAmiiboValid(temporary_encrypted_tag_data)) {
|
||||
return ResultNotAnAmiibo;
|
||||
}
|
||||
|
||||
if (!is_plain_amiibo) {
|
||||
if (!NFP::AmiiboCrypto::DecodeAmiibo(temporary_encrypted_tag_data, temporary_tag_data)) {
|
||||
LOG_ERROR(Service_NFP, "Can't decode amiibo");
|
||||
return ResultCorruptedData;
|
||||
}
|
||||
}
|
||||
|
||||
// Overwrite tag contents with backup and mount the tag
|
||||
tag_data = temporary_tag_data;
|
||||
encrypted_tag_data = temporary_encrypted_tag_data;
|
||||
device_state = DeviceState::TagMounted;
|
||||
mount_target = NFP::MountTarget::All;
|
||||
is_data_moddified = true;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
@@ -1132,13 +1194,69 @@ Result NfcDevice::BreakTag(NFP::BreakType break_type) {
|
||||
return FlushWithBreak(break_type);
|
||||
}
|
||||
|
||||
Result NfcDevice::ReadBackupData(std::span<u8> data) const {
|
||||
// Not implemented
|
||||
Result NfcDevice::HasBackup(const NFC::UniqueSerialNumber& uid) const {
|
||||
constexpr auto backup_dir = "backup";
|
||||
const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
|
||||
const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, ""));
|
||||
|
||||
if (!Common::FS::Exists(yuzu_amiibo_dir / backup_dir / file_name)) {
|
||||
return ResultUnableToAccessBackupFile;
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result NfcDevice::WriteBackupData(std::span<const u8> data) {
|
||||
// Not implemented
|
||||
Result NfcDevice::ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const {
|
||||
constexpr auto backup_dir = "backup";
|
||||
const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
|
||||
const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, ""));
|
||||
|
||||
const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name,
|
||||
Common::FS::FileAccessMode::Read,
|
||||
Common::FS::FileType::BinaryFile};
|
||||
|
||||
if (!keys_file.IsOpen()) {
|
||||
LOG_ERROR(Service_NFP, "Failed to open amiibo backup");
|
||||
return ResultUnableToAccessBackupFile;
|
||||
}
|
||||
|
||||
if (keys_file.Read(data) != data.size()) {
|
||||
LOG_ERROR(Service_NFP, "Failed to read amiibo backup");
|
||||
return ResultUnableToAccessBackupFile;
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result NfcDevice::WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data) {
|
||||
constexpr auto backup_dir = "backup";
|
||||
const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
|
||||
const auto file_name = fmt::format("{0:02x}.bin", fmt::join(uid, ""));
|
||||
|
||||
if (HasBackup(uid).IsError()) {
|
||||
if (!Common::FS::CreateDir(yuzu_amiibo_dir / backup_dir)) {
|
||||
return ResultBackupPathAlreadyExist;
|
||||
}
|
||||
|
||||
if (!Common::FS::NewFile(yuzu_amiibo_dir / backup_dir / file_name)) {
|
||||
return ResultBackupPathAlreadyExist;
|
||||
}
|
||||
}
|
||||
|
||||
const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name,
|
||||
Common::FS::FileAccessMode::ReadWrite,
|
||||
Common::FS::FileType::BinaryFile};
|
||||
|
||||
if (!keys_file.IsOpen()) {
|
||||
LOG_ERROR(Service_NFP, "Failed to open amiibo backup");
|
||||
return ResultUnableToAccessBackupFile;
|
||||
}
|
||||
|
||||
if (keys_file.Write(data) != data.size()) {
|
||||
LOG_ERROR(Service_NFP, "Failed to write amiibo backup");
|
||||
return ResultUnableToAccessBackupFile;
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
@@ -1177,7 +1295,8 @@ NFP::AmiiboName NfcDevice::GetAmiiboName(const NFP::AmiiboSettings& settings) co
|
||||
return amiibo_name;
|
||||
}
|
||||
|
||||
void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) {
|
||||
void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings,
|
||||
const NFP::AmiiboName& amiibo_name) const {
|
||||
std::array<char16_t, NFP::amiibo_name_length> settings_amiibo_name{};
|
||||
|
||||
// Convert from utf8 to utf16
|
||||
@@ -1258,22 +1377,23 @@ void NfcDevice::UpdateRegisterInfoCrc() {
|
||||
tag_data.register_info_crc = crc.checksum();
|
||||
}
|
||||
|
||||
void NfcDevice::BuildAmiiboWithoutKeys() {
|
||||
void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data,
|
||||
const NFP::EncryptedNTAG215File& encrypted_file) const {
|
||||
Service::Mii::MiiManager manager;
|
||||
auto& settings = tag_data.settings;
|
||||
auto& settings = stubbed_tag_data.settings;
|
||||
|
||||
tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_tag_data);
|
||||
stubbed_tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_file);
|
||||
|
||||
// Common info
|
||||
tag_data.write_counter = 0;
|
||||
tag_data.amiibo_version = 0;
|
||||
stubbed_tag_data.write_counter = 0;
|
||||
stubbed_tag_data.amiibo_version = 0;
|
||||
settings.write_date = GetAmiiboDate(GetCurrentPosixTime());
|
||||
|
||||
// Register info
|
||||
SetAmiiboName(settings, {'y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o'});
|
||||
settings.settings.font_region.Assign(0);
|
||||
settings.init_date = GetAmiiboDate(GetCurrentPosixTime());
|
||||
tag_data.owner_mii = manager.BuildFromStoreData(manager.BuildDefault(0));
|
||||
stubbed_tag_data.owner_mii = manager.BuildFromStoreData(manager.BuildDefault(0));
|
||||
|
||||
// Admin info
|
||||
settings.settings.amiibo_initialized.Assign(1);
|
||||
|
||||
@@ -86,8 +86,9 @@ public:
|
||||
Result GetAll(NFP::NfpData& data) const;
|
||||
Result SetAll(const NFP::NfpData& data);
|
||||
Result BreakTag(NFP::BreakType break_type);
|
||||
Result ReadBackupData(std::span<u8> data) const;
|
||||
Result WriteBackupData(std::span<const u8> data);
|
||||
Result HasBackup(const NFC::UniqueSerialNumber& uid) const;
|
||||
Result ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const;
|
||||
Result WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data);
|
||||
Result WriteNtf(std::span<const u8> data);
|
||||
|
||||
u64 GetHandle() const;
|
||||
@@ -103,14 +104,15 @@ private:
|
||||
void CloseNfcTag();
|
||||
|
||||
NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const;
|
||||
void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name);
|
||||
void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) const;
|
||||
NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const;
|
||||
u64 GetCurrentPosixTime() const;
|
||||
u64 RemoveVersionByte(u64 application_id) const;
|
||||
void UpdateSettingsCrc();
|
||||
void UpdateRegisterInfoCrc();
|
||||
|
||||
void BuildAmiiboWithoutKeys();
|
||||
void BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data,
|
||||
const NFP::EncryptedNTAG215File& encrypted_file) const;
|
||||
|
||||
bool is_controller_set{};
|
||||
int callback_key;
|
||||
|
||||
@@ -543,9 +543,14 @@ Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) cons
|
||||
|
||||
std::shared_ptr<NfcDevice> device = nullptr;
|
||||
auto result = GetDeviceHandle(device_handle, device);
|
||||
NFC::TagInfo tag_info{};
|
||||
|
||||
if (result.IsSuccess()) {
|
||||
result = device->ReadBackupData(data);
|
||||
result = device->GetTagInfo(tag_info, false);
|
||||
}
|
||||
|
||||
if (result.IsSuccess()) {
|
||||
result = device->ReadBackupData(tag_info.uuid, data);
|
||||
result = VerifyDeviceResult(device, result);
|
||||
}
|
||||
|
||||
@@ -557,9 +562,14 @@ Result DeviceManager::WriteBackupData(u64 device_handle, std::span<const u8> dat
|
||||
|
||||
std::shared_ptr<NfcDevice> device = nullptr;
|
||||
auto result = GetDeviceHandle(device_handle, device);
|
||||
NFC::TagInfo tag_info{};
|
||||
|
||||
if (result.IsSuccess()) {
|
||||
result = device->WriteBackupData(data);
|
||||
result = device->GetTagInfo(tag_info, false);
|
||||
}
|
||||
|
||||
if (result.IsSuccess()) {
|
||||
result = device->WriteBackupData(tag_info.uuid, data);
|
||||
result = VerifyDeviceResult(device, result);
|
||||
}
|
||||
|
||||
|
||||
@@ -302,7 +302,7 @@ Result NfcInterface::TranslateResultToServiceError(Result result) const {
|
||||
return TranslateResultToNfp(result);
|
||||
}
|
||||
default:
|
||||
if (result != ResultUnknown216) {
|
||||
if (result != ResultBackupPathAlreadyExist) {
|
||||
return result;
|
||||
}
|
||||
return ResultUnknown74;
|
||||
@@ -343,6 +343,9 @@ Result NfcInterface::TranslateResultToNfp(Result result) const {
|
||||
if (result == ResultApplicationAreaIsNotInitialized) {
|
||||
return NFP::ResultApplicationAreaIsNotInitialized;
|
||||
}
|
||||
if (result == ResultCorruptedDataWithBackup) {
|
||||
return NFP::ResultCorruptedDataWithBackup;
|
||||
}
|
||||
if (result == ResultCorruptedData) {
|
||||
return NFP::ResultCorruptedData;
|
||||
}
|
||||
@@ -355,6 +358,9 @@ Result NfcInterface::TranslateResultToNfp(Result result) const {
|
||||
if (result == ResultNotAnAmiibo) {
|
||||
return NFP::ResultNotAnAmiibo;
|
||||
}
|
||||
if (result == ResultUnableToAccessBackupFile) {
|
||||
return NFP::ResultUnableToAccessBackupFile;
|
||||
}
|
||||
LOG_WARNING(Service_NFC, "Result conversion not handled");
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -9,20 +9,22 @@ namespace Service::NFC {
|
||||
|
||||
constexpr Result ResultDeviceNotFound(ErrorModule::NFC, 64);
|
||||
constexpr Result ResultInvalidArgument(ErrorModule::NFC, 65);
|
||||
constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFP, 68);
|
||||
constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFC, 68);
|
||||
constexpr Result ResultWrongDeviceState(ErrorModule::NFC, 73);
|
||||
constexpr Result ResultUnknown74(ErrorModule::NFC, 74);
|
||||
constexpr Result ResultUnknown76(ErrorModule::NFC, 76);
|
||||
constexpr Result ResultNfcNotInitialized(ErrorModule::NFC, 77);
|
||||
constexpr Result ResultNfcDisabled(ErrorModule::NFC, 80);
|
||||
constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88);
|
||||
constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFC, 88);
|
||||
constexpr Result ResultTagRemoved(ErrorModule::NFC, 97);
|
||||
constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120);
|
||||
constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
|
||||
constexpr Result ResultCorruptedData(ErrorModule::NFP, 144);
|
||||
constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152);
|
||||
constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168);
|
||||
constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178);
|
||||
constexpr Result ResultUnknown216(ErrorModule::NFC, 216);
|
||||
constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFC, 113);
|
||||
constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFC, 120);
|
||||
constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFC, 128);
|
||||
constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFC, 136);
|
||||
constexpr Result ResultCorruptedData(ErrorModule::NFC, 144);
|
||||
constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFC, 152);
|
||||
constexpr Result ResultApplicationAreaExist(ErrorModule::NFC, 168);
|
||||
constexpr Result ResultNotAnAmiibo(ErrorModule::NFC, 178);
|
||||
constexpr Result ResultBackupPathAlreadyExist(ErrorModule::NFC, 216);
|
||||
|
||||
} // namespace Service::NFC
|
||||
|
||||
@@ -126,7 +126,7 @@ void Interface::Flush(HLERequestContext& ctx) {
|
||||
void Interface::Restore(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
|
||||
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
|
||||
|
||||
auto result = GetManager()->Restore(device_handle);
|
||||
result = TranslateResultToServiceError(result);
|
||||
@@ -394,7 +394,7 @@ void Interface::BreakTag(HLERequestContext& ctx) {
|
||||
void Interface::ReadBackupData(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
|
||||
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
|
||||
|
||||
std::vector<u8> backup_data{};
|
||||
auto result = GetManager()->ReadBackupData(device_handle, backup_data);
|
||||
@@ -412,7 +412,7 @@ void Interface::WriteBackupData(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto device_handle{rp.Pop<u64>()};
|
||||
const auto backup_data_buffer{ctx.ReadBuffer()};
|
||||
LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
|
||||
LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
|
||||
|
||||
auto result = GetManager()->WriteBackupData(device_handle, backup_data_buffer);
|
||||
result = TranslateResultToServiceError(result);
|
||||
|
||||
@@ -17,9 +17,11 @@ constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88);
|
||||
constexpr Result ResultTagRemoved(ErrorModule::NFP, 97);
|
||||
constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120);
|
||||
constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
|
||||
constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFP, 136);
|
||||
constexpr Result ResultCorruptedData(ErrorModule::NFP, 144);
|
||||
constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152);
|
||||
constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168);
|
||||
constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178);
|
||||
constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFP, 200);
|
||||
|
||||
} // namespace Service::NFP
|
||||
|
||||
@@ -69,7 +69,7 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
|
||||
|
||||
void nvhost_nvdec::OnOpen(DeviceFD fd) {
|
||||
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
|
||||
system.AudioCore().SetNVDECActive(true);
|
||||
system.SetNVDECActive(true);
|
||||
}
|
||||
|
||||
void nvhost_nvdec::OnClose(DeviceFD fd) {
|
||||
@@ -79,7 +79,7 @@ void nvhost_nvdec::OnClose(DeviceFD fd) {
|
||||
if (iter != host1x_file.fd_to_id.end()) {
|
||||
system.GPU().ClearCdmaInstance(iter->second);
|
||||
}
|
||||
system.AudioCore().SetNVDECActive(false);
|
||||
system.SetNVDECActive(false);
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia::Devices
|
||||
|
||||
@@ -324,6 +324,10 @@ s64 Nvnflinger::GetNextTicks() const {
|
||||
speed_scale = 0.01f;
|
||||
}
|
||||
}
|
||||
if (system.GetNVDECActive() && settings.use_video_framerate.GetValue()) {
|
||||
// Run at intended presentation rate during video playback.
|
||||
speed_scale = 1.f;
|
||||
}
|
||||
|
||||
// As an extension, treat nonpositive swap interval as framerate multiplier.
|
||||
const f32 effective_fps = swap_interval <= 0 ? 120.f * static_cast<f32>(1 - swap_interval)
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "common/bit_cast.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
@@ -18,8 +20,10 @@
|
||||
#include <iphlpapi.h>
|
||||
#else
|
||||
#include <cerrno>
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#endif
|
||||
|
||||
namespace Network {
|
||||
@@ -91,8 +95,28 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
return result;
|
||||
}
|
||||
|
||||
#else
|
||||
#elif defined(__ANDROID__)
|
||||
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
std::vector<NetworkInterface> result;
|
||||
|
||||
std::vector<std::string> route_parts;
|
||||
|
||||
boost::split(route_parts, Settings::values.network_route.GetValue(), boost::is_any_of(";"));
|
||||
|
||||
struct in_addr ip {
|
||||
}, sm{}, gw{};
|
||||
|
||||
inet_pton(AF_INET, route_parts[1].c_str(), &ip);
|
||||
inet_pton(AF_INET, route_parts[2].c_str(), &sm);
|
||||
inet_pton(AF_INET, route_parts[3].c_str(), &gw);
|
||||
|
||||
result.emplace_back(
|
||||
NetworkInterface{.name{route_parts[0]}, .ip_address{ip}, .subnet_mask{sm}, .gateway{gw}});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#else
|
||||
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
struct ifaddrs* ifaddr = nullptr;
|
||||
|
||||
@@ -187,6 +211,10 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
#endif
|
||||
|
||||
std::optional<NetworkInterface> GetSelectedNetworkInterface() {
|
||||
#ifdef __ANDROID__
|
||||
Network::SelectFirstNetworkInterface(); // TODO ANDROID
|
||||
#endif
|
||||
|
||||
const auto& selected_network_interface = Settings::values.network_interface.GetValue();
|
||||
const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
|
||||
if (network_interfaces.empty()) {
|
||||
|
||||
@@ -33,7 +33,8 @@ static_assert(sizeof(NroSegmentHeader) == 0x8, "NroSegmentHeader has incorrect s
|
||||
struct NroHeader {
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
u32_le module_header_offset;
|
||||
INSERT_PADDING_BYTES(0x8);
|
||||
u32 magic_ext1;
|
||||
u32 magic_ext2;
|
||||
u32_le magic;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
u32_le file_size;
|
||||
@@ -124,6 +125,16 @@ FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& nro_file) {
|
||||
return FileType::Error;
|
||||
}
|
||||
|
||||
bool AppLoader_NRO::IsHomebrew() {
|
||||
// Read NSO header
|
||||
NroHeader nro_header{};
|
||||
if (sizeof(NroHeader) != file->ReadObject(&nro_header)) {
|
||||
return false;
|
||||
}
|
||||
return nro_header.magic_ext1 == Common::MakeMagic('H', 'O', 'M', 'E') &&
|
||||
nro_header.magic_ext2 == Common::MakeMagic('B', 'R', 'E', 'W');
|
||||
}
|
||||
|
||||
static constexpr u32 PageAlignSize(u32 size) {
|
||||
return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ public:
|
||||
*/
|
||||
static FileType IdentifyType(const FileSys::VirtualFile& nro_file);
|
||||
|
||||
bool IsHomebrew();
|
||||
|
||||
FileType GetFileType() const override {
|
||||
return IdentifyType(file);
|
||||
}
|
||||
|
||||
@@ -37,10 +37,6 @@
|
||||
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
#ifdef ANDROID
|
||||
extern u32 GetAndroidScreenRotation();
|
||||
#endif
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
namespace {
|
||||
@@ -78,47 +74,6 @@ struct ScreenRectVertex {
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
|
||||
constexpr u32 ROTATION_0 = 0;
|
||||
constexpr u32 ROTATION_90 = 1;
|
||||
constexpr u32 ROTATION_180 = 2;
|
||||
constexpr u32 ROTATION_270 = 3;
|
||||
|
||||
// clang-format off
|
||||
switch (GetAndroidScreenRotation()) {
|
||||
case ROTATION_0:
|
||||
// Desktop
|
||||
return { 2.f / width, 0.f, 0.f, 0.f,
|
||||
0.f, 2.f / height, 0.f, 0.f,
|
||||
0.f, 0.f, 1.f, 0.f,
|
||||
-1.f, -1.f, 0.f, 1.f};
|
||||
case ROTATION_180:
|
||||
// Reverse desktop
|
||||
return {-2.f / width, 0.f, 0.f, 0.f,
|
||||
0.f, -2.f / height, 0.f, 0.f,
|
||||
0.f, 0.f, 1.f, 0.f,
|
||||
1.f, 1.f, 0.f, 1.f};
|
||||
case ROTATION_270:
|
||||
// Reverse landscape
|
||||
return { 0.f, -2.f / width, 0.f, 0.f,
|
||||
2.f / height, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 1.f, 0.f,
|
||||
-1.f, 1.f, 0.f, 1.f};
|
||||
case ROTATION_90:
|
||||
default:
|
||||
// Landscape
|
||||
return { 0.f, 2.f / width, 0.f, 0.f,
|
||||
-2.f / height, 0.f, 0.f, 0.f,
|
||||
0.f, 0.f, 1.f, 0.f,
|
||||
1.f, -1.f, 0.f, 1.f};
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
|
||||
// clang-format off
|
||||
return { 2.f / width, 0.f, 0.f, 0.f,
|
||||
@@ -128,8 +83,6 @@ std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) {
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) {
|
||||
using namespace VideoCore::Surface;
|
||||
return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format));
|
||||
@@ -1159,7 +1112,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = GetFormat(framebuffer),
|
||||
.format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : GetFormat(framebuffer),
|
||||
.extent =
|
||||
{
|
||||
.width = (up_scale * framebuffer.width) >> down_shift,
|
||||
@@ -1180,14 +1133,14 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
|
||||
const auto create_commit = [&](vk::Image& image) {
|
||||
return memory_allocator.Commit(image, MemoryUsage::DeviceLocal);
|
||||
};
|
||||
const auto create_image_view = [&](vk::Image& image) {
|
||||
const auto create_image_view = [&](vk::Image& image, bool used_on_framebuffer = false) {
|
||||
return device.GetLogical().CreateImageView(VkImageViewCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.image = *image,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = GetFormat(framebuffer),
|
||||
.format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : GetFormat(framebuffer),
|
||||
.components =
|
||||
{
|
||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
@@ -1217,7 +1170,7 @@ void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) {
|
||||
const u32 down_shift = Settings::values.resolution_info.down_shift;
|
||||
aa_image = create_image(true, up_scale, down_shift);
|
||||
aa_commit = create_commit(aa_image);
|
||||
aa_image_view = create_image_view(aa_image);
|
||||
aa_image_view = create_image_view(aa_image, true);
|
||||
VkExtent2D size{
|
||||
.width = (up_scale * framebuffer.width) >> down_shift,
|
||||
.height = (up_scale * framebuffer.height) >> down_shift,
|
||||
|
||||
@@ -231,7 +231,12 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
|
||||
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
#ifdef ANDROID
|
||||
// On Android, do not allow surface rotation to deviate from the frontend.
|
||||
.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
|
||||
#else
|
||||
.preTransform = capabilities.currentTransform,
|
||||
#endif
|
||||
.compositeAlpha = alpha_flags,
|
||||
.presentMode = present_mode,
|
||||
.clipped = VK_FALSE,
|
||||
|
||||
@@ -101,6 +101,12 @@ const std::map<Settings::RendererBackend, QString> Config::renderer_backend_text
|
||||
{Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))},
|
||||
};
|
||||
|
||||
const std::map<Settings::ShaderBackend, QString> Config::shader_backend_texts_map = {
|
||||
{Settings::ShaderBackend::GLSL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))},
|
||||
{Settings::ShaderBackend::GLASM, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))},
|
||||
{Settings::ShaderBackend::SPIRV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))},
|
||||
};
|
||||
|
||||
// This shouldn't have anything except static initializers (no functions). So
|
||||
// QKeySequence(...).toString() is NOT ALLOWED HERE.
|
||||
// This must be in alphabetical order according to action name as it must have the same order as
|
||||
@@ -754,6 +760,7 @@ void Config::ReadRendererValues() {
|
||||
ReadGlobalSetting(Settings::values.use_fast_gpu_time);
|
||||
ReadGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
|
||||
ReadGlobalSetting(Settings::values.enable_compute_pipelines);
|
||||
ReadGlobalSetting(Settings::values.use_video_framerate);
|
||||
ReadGlobalSetting(Settings::values.bg_red);
|
||||
ReadGlobalSetting(Settings::values.bg_green);
|
||||
ReadGlobalSetting(Settings::values.bg_blue);
|
||||
@@ -1409,6 +1416,7 @@ void Config::SaveRendererValues() {
|
||||
WriteGlobalSetting(Settings::values.use_fast_gpu_time);
|
||||
WriteGlobalSetting(Settings::values.use_vulkan_driver_pipeline_cache);
|
||||
WriteGlobalSetting(Settings::values.enable_compute_pipelines);
|
||||
WriteGlobalSetting(Settings::values.use_video_framerate);
|
||||
WriteGlobalSetting(Settings::values.bg_red);
|
||||
WriteGlobalSetting(Settings::values.bg_green);
|
||||
WriteGlobalSetting(Settings::values.bg_blue);
|
||||
|
||||
@@ -54,6 +54,7 @@ public:
|
||||
static const std::map<bool, QString> use_docked_mode_texts_map;
|
||||
static const std::map<Settings::GPUAccuracy, QString> gpu_accuracy_texts_map;
|
||||
static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map;
|
||||
static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map;
|
||||
|
||||
static constexpr UISettings::Theme default_theme{
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -42,6 +42,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
|
||||
Settings::values.use_vulkan_driver_pipeline_cache.GetValue());
|
||||
ui->enable_compute_pipelines_checkbox->setChecked(
|
||||
Settings::values.enable_compute_pipelines.GetValue());
|
||||
ui->use_video_framerate_checkbox->setChecked(Settings::values.use_video_framerate.GetValue());
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->gpu_accuracy->setCurrentIndex(
|
||||
@@ -91,6 +92,8 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_compute_pipelines,
|
||||
ui->enable_compute_pipelines_checkbox,
|
||||
enable_compute_pipelines);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_video_framerate,
|
||||
ui->use_video_framerate_checkbox, use_video_framerate);
|
||||
}
|
||||
|
||||
void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {
|
||||
@@ -125,6 +128,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
|
||||
Settings::values.max_anisotropy.UsingGlobal());
|
||||
ui->enable_compute_pipelines_checkbox->setEnabled(
|
||||
Settings::values.enable_compute_pipelines.UsingGlobal());
|
||||
ui->use_video_framerate_checkbox->setEnabled(
|
||||
Settings::values.use_video_framerate.UsingGlobal());
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -149,6 +154,9 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
|
||||
ConfigurationShared::SetColoredTristate(ui->enable_compute_pipelines_checkbox,
|
||||
Settings::values.enable_compute_pipelines,
|
||||
enable_compute_pipelines);
|
||||
ConfigurationShared::SetColoredTristate(ui->use_video_framerate_checkbox,
|
||||
Settings::values.use_video_framerate,
|
||||
use_video_framerate);
|
||||
ConfigurationShared::SetColoredComboBox(
|
||||
ui->gpu_accuracy, ui->label_gpu_accuracy,
|
||||
static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));
|
||||
|
||||
@@ -47,6 +47,7 @@ private:
|
||||
ConfigurationShared::CheckState use_fast_gpu_time;
|
||||
ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache;
|
||||
ConfigurationShared::CheckState enable_compute_pipelines;
|
||||
ConfigurationShared::CheckState use_video_framerate;
|
||||
|
||||
const Core::System& system;
|
||||
};
|
||||
|
||||
@@ -191,6 +191,16 @@ Compute pipelines are always enabled on all other drivers.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="use_video_framerate_checkbox">
|
||||
<property name="toolTip">
|
||||
<string>Run the game at normal speed during video playback, even when the framerate is unlocked.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Sync to framerate of video playback</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="af_layout" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_1">
|
||||
|
||||
@@ -4116,7 +4116,13 @@ void GMainWindow::UpdateDockedButton() {
|
||||
void GMainWindow::UpdateAPIText() {
|
||||
const auto api = Settings::values.renderer_backend.GetValue();
|
||||
const auto renderer_status_text = Config::renderer_backend_texts_map.find(api)->second;
|
||||
renderer_status_button->setText(renderer_status_text.toUpper());
|
||||
renderer_status_button->setText(
|
||||
api == Settings::RendererBackend::OpenGL
|
||||
? tr("%1 %2").arg(
|
||||
renderer_status_text.toUpper(),
|
||||
Config::shader_backend_texts_map.find(Settings::values.shader_backend.GetValue())
|
||||
->second)
|
||||
: renderer_status_text.toUpper());
|
||||
}
|
||||
|
||||
void GMainWindow::UpdateFilterText() {
|
||||
|
||||
@@ -81,6 +81,7 @@ void DirectConnectWindow::Connect() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ui->ip->hasAcceptableInput()) {
|
||||
NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::IP_ADDRESS_NOT_VALID);
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user