Merge branch 'master' of https://github.com/MarioPartyNetplay/Dolphin-MPN
|
@ -8,10 +8,11 @@ plugins {
|
|||
@Suppress("UnstableApiUsage")
|
||||
android {
|
||||
compileSdkVersion = "android-34"
|
||||
ndkVersion = "26.1.10909125"
|
||||
ndkVersion = "27.0.12077973"
|
||||
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
buildConfig = true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
|
@ -116,12 +117,11 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
"baselineProfile"(project(":benchmark"))
|
||||
baselineProfile(project(":benchmark"))
|
||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")
|
||||
|
||||
implementation("androidx.core:core-ktx:1.13.0")
|
||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||
implementation("androidx.exifinterface:exifinterface:1.3.7")
|
||||
implementation("androidx.cardview:cardview:1.0.0")
|
||||
implementation("androidx.recyclerview:recyclerview:1.3.2")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
|
@ -135,7 +135,6 @@ dependencies {
|
|||
// Kotlin extensions for lifecycle components
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
|
||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0")
|
||||
|
||||
// Android TV UI libraries.
|
||||
implementation("androidx.leanback:leanback:1.0.0")
|
||||
|
@ -171,7 +170,7 @@ fun getGitVersion(): String {
|
|||
|
||||
fun getBuildVersionCode(): Int {
|
||||
try {
|
||||
return Integer.valueOf(
|
||||
val commitCount = Integer.valueOf(
|
||||
ProcessBuilder("git", "rev-list", "--first-parent", "--count", "HEAD")
|
||||
.directory(project.rootDir)
|
||||
.redirectOutput(ProcessBuilder.Redirect.PIPE)
|
||||
|
@ -179,6 +178,15 @@ fun getBuildVersionCode(): Int {
|
|||
.start().inputStream.bufferedReader().use { it.readText() }
|
||||
.trim()
|
||||
)
|
||||
|
||||
val isRelease = ProcessBuilder("git", "describe", "--exact-match", "HEAD")
|
||||
.directory(project.rootDir)
|
||||
.redirectOutput(ProcessBuilder.Redirect.PIPE)
|
||||
.redirectError(ProcessBuilder.Redirect.PIPE)
|
||||
.start()
|
||||
.waitFor() == 0
|
||||
|
||||
return commitCount * 2 + (if (isRelease) 0 else 1)
|
||||
} catch (e: Exception) {
|
||||
logger.error("Cannot find git, defaulting to dummy version code")
|
||||
}
|
||||
|
|
40
Source/Android/app/proguard-rules.pro
vendored
|
@ -2,46 +2,6 @@
|
|||
# than the space savings obfuscation could give us
|
||||
-dontobfuscate
|
||||
|
||||
#
|
||||
# Kotlin Serialization
|
||||
#
|
||||
|
||||
# Keep `Companion` object fields of serializable classes.
|
||||
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
|
||||
-if @kotlinx.serialization.Serializable class **
|
||||
-keepclassmembers class <1> {
|
||||
static <1>$Companion Companion;
|
||||
}
|
||||
|
||||
# Keep `serializer()` on companion objects (both default and named) of serializable classes.
|
||||
-if @kotlinx.serialization.Serializable class ** {
|
||||
static **$* *;
|
||||
}
|
||||
-keepclassmembers class <2>$<3> {
|
||||
kotlinx.serialization.KSerializer serializer(...);
|
||||
}
|
||||
|
||||
# Keep `INSTANCE.serializer()` of serializable objects.
|
||||
-if @kotlinx.serialization.Serializable class ** {
|
||||
public static ** INSTANCE;
|
||||
}
|
||||
-keepclassmembers class <1> {
|
||||
public static <1> INSTANCE;
|
||||
kotlinx.serialization.KSerializer serializer(...);
|
||||
}
|
||||
|
||||
# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
|
||||
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
|
||||
|
||||
# Don't print notes about potential mistakes or omissions in the configuration for kotlinx-serialization classes
|
||||
# See also https://github.com/Kotlin/kotlinx.serialization/issues/1900
|
||||
-dontnote kotlinx.serialization.**
|
||||
|
||||
# Serialization core uses `java.lang.ClassValue` for caching inside these specified classes.
|
||||
# If there is no `java.lang.ClassValue` (for example, in Android), then R8/ProGuard will print a warning.
|
||||
# However, since in this case they will not be used, we can disable these warnings
|
||||
-dontwarn kotlinx.serialization.internal.ClassValueReferences
|
||||
|
||||
# Required for R8 full mode
|
||||
-dontwarn org.bouncycastle.jsse.BCSSLParameters
|
||||
-dontwarn org.bouncycastle.jsse.BCSSLSocket
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.app.Application;
|
|||
import android.content.Context;
|
||||
import android.hardware.usb.UsbManager;
|
||||
|
||||
import org.dolphinemu.dolphinemu.utils.ActivityTracker;
|
||||
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
|
||||
import org.dolphinemu.dolphinemu.utils.Java_GCAdapter;
|
||||
import org.dolphinemu.dolphinemu.utils.Java_WiimoteAdapter;
|
||||
|
@ -20,6 +21,7 @@ public class DolphinApplication extends Application
|
|||
{
|
||||
super.onCreate();
|
||||
application = this;
|
||||
registerActivityLifecycleCallbacks(new ActivityTracker());
|
||||
VolleyUtil.init(getApplicationContext());
|
||||
System.loadLibrary("main");
|
||||
|
||||
|
|
|
@ -80,7 +80,6 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
|
|||
private var infinityFigureData = Figure(-1, "Position")
|
||||
private var skylanderSlot = -1
|
||||
private var infinityPosition = -1
|
||||
private var infinityListPosition = -1
|
||||
private lateinit var skylandersBinding: DialogNfcFiguresManagerBinding
|
||||
private lateinit var infinityBinding: DialogNfcFiguresManagerBinding
|
||||
|
||||
|
@ -140,12 +139,14 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
|
|||
if (infinityFigures.isEmpty()) {
|
||||
infinityFigures.apply {
|
||||
add(FigureSlot(getString(R.string.infinity_hexagon_label), 0))
|
||||
add(FigureSlot(getString(R.string.infinity_p1_label), 1))
|
||||
add(FigureSlot(getString(R.string.infinity_p1a1_label), 3))
|
||||
add(FigureSlot(getString(R.string.infinity_power_hex_two_label), 1))
|
||||
add(FigureSlot(getString(R.string.infinity_power_hex_three_label), 2))
|
||||
add(FigureSlot(getString(R.string.infinity_p1_label), 3))
|
||||
add(FigureSlot(getString(R.string.infinity_p1a1_label), 4))
|
||||
add(FigureSlot(getString(R.string.infinity_p1a2_label), 5))
|
||||
add(FigureSlot(getString(R.string.infinity_p2_label), 2))
|
||||
add(FigureSlot(getString(R.string.infinity_p2a1_label), 4))
|
||||
add(FigureSlot(getString(R.string.infinity_p2a2_label), 6))
|
||||
add(FigureSlot(getString(R.string.infinity_p2_label), 6))
|
||||
add(FigureSlot(getString(R.string.infinity_p2a1_label), 7))
|
||||
add(FigureSlot(getString(R.string.infinity_p2a2_label), 8))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +165,6 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
|
|||
putInt(EXTRA_SKYLANDER_VAR, skylanderData.variant)
|
||||
putString(EXTRA_SKYLANDER_NAME, skylanderData.name)
|
||||
putInt(EXTRA_INFINITY_POSITION, infinityPosition)
|
||||
putInt(EXTRA_INFINITY_LIST_POSITION, infinityListPosition)
|
||||
putLong(EXTRA_INFINITY_NUM, infinityFigureData.number)
|
||||
putString(EXTRA_INFINITY_NAME, infinityFigureData.name)
|
||||
}
|
||||
|
@ -183,7 +183,6 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
|
|||
savedInstanceState.getString(EXTRA_SKYLANDER_NAME)!!
|
||||
)
|
||||
infinityPosition = savedInstanceState.getInt(EXTRA_INFINITY_POSITION)
|
||||
infinityListPosition = savedInstanceState.getInt(EXTRA_INFINITY_LIST_POSITION)
|
||||
infinityFigureData = Figure(
|
||||
savedInstanceState.getLong(EXTRA_INFINITY_NUM),
|
||||
savedInstanceState.getString(EXTRA_INFINITY_NAME)!!
|
||||
|
@ -297,11 +296,10 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
|
|||
} else if (requestCode == REQUEST_INFINITY_FIGURE_FILE) {
|
||||
val label = InfinityConfig.loadFigure(infinityPosition, result!!.data.toString())
|
||||
if (label != null && label != "Unknown Figure") {
|
||||
clearInfinityFigure(infinityListPosition)
|
||||
infinityFigures[infinityListPosition].label = label
|
||||
infinityBinding.figureManager.adapter?.notifyItemChanged(infinityListPosition)
|
||||
clearInfinityFigure(infinityPosition)
|
||||
infinityFigures[infinityPosition].label = label
|
||||
infinityBinding.figureManager.adapter?.notifyItemChanged(infinityPosition)
|
||||
infinityPosition = -1
|
||||
infinityListPosition = -1
|
||||
infinityFigureData = Figure.BLANK_FIGURE
|
||||
} else {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
|
@ -317,11 +315,10 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
|
|||
result!!.data.toString(),
|
||||
infinityPosition
|
||||
)
|
||||
clearInfinityFigure(infinityListPosition)
|
||||
infinityFigures[infinityListPosition].label = label!!
|
||||
infinityBinding.figureManager.adapter?.notifyItemChanged(infinityListPosition)
|
||||
clearInfinityFigure(infinityPosition)
|
||||
infinityFigures[infinityPosition].label = label!!
|
||||
infinityBinding.figureManager.adapter?.notifyItemChanged(infinityPosition)
|
||||
infinityPosition = -1
|
||||
infinityListPosition = -1
|
||||
infinityFigureData = Figure.BLANK_FIGURE
|
||||
}
|
||||
}
|
||||
|
@ -906,18 +903,19 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
|
|||
fun setInfinityFigureData(num: Long, name: String, position: Int, listPosition: Int) {
|
||||
infinityFigureData = Figure(num, name)
|
||||
infinityPosition = position
|
||||
infinityListPosition = listPosition
|
||||
}
|
||||
|
||||
fun clearInfinityFigure(position: Int) {
|
||||
when (position) {
|
||||
0 -> infinityFigures[position].label = getString(R.string.infinity_hexagon_label)
|
||||
1 -> infinityFigures[position].label = getString(R.string.infinity_p1_label)
|
||||
2 -> infinityFigures[position].label = getString(R.string.infinity_p1a1_label)
|
||||
3 -> infinityFigures[position].label = getString(R.string.infinity_p1a2_label)
|
||||
4 -> infinityFigures[position].label = getString(R.string.infinity_p2_label)
|
||||
5 -> infinityFigures[position].label = getString(R.string.infinity_p2a1_label)
|
||||
6 -> infinityFigures[position].label = getString(R.string.infinity_p2a2_label)
|
||||
1 -> infinityFigures[position].label = getString(R.string.infinity_power_hex_two_label)
|
||||
2 -> infinityFigures[position].label = getString(R.string.infinity_power_hex_three_label)
|
||||
3 -> infinityFigures[position].label = getString(R.string.infinity_p1_label)
|
||||
4 -> infinityFigures[position].label = getString(R.string.infinity_p1a1_label)
|
||||
5 -> infinityFigures[position].label = getString(R.string.infinity_p1a2_label)
|
||||
6 -> infinityFigures[position].label = getString(R.string.infinity_p2_label)
|
||||
7 -> infinityFigures[position].label = getString(R.string.infinity_p2a1_label)
|
||||
8 -> infinityFigures[position].label = getString(R.string.infinity_p2a2_label)
|
||||
}
|
||||
infinityBinding.figureManager.adapter?.notifyItemChanged(position)
|
||||
}
|
||||
|
|
|
@ -75,6 +75,11 @@ class FigureSlotAdapter(
|
|||
}
|
||||
|
||||
1, 2 -> {
|
||||
// Hexagon Power Discs
|
||||
validFigures.filter { (_, value) -> value in 4000000..4999999 }
|
||||
}
|
||||
|
||||
3, 6 -> {
|
||||
// Characters
|
||||
validFigures.filter { (_, value) -> value in 1000000..1999999 }
|
||||
}
|
||||
|
|
|
@ -19,19 +19,31 @@ class DolphinSensorEventListener : SensorEventListener {
|
|||
private class AxisSetDetails(val firstAxisOfSet: Int, val axisSetType: Int)
|
||||
|
||||
private class SensorDetails(
|
||||
val sensor: Sensor,
|
||||
val sensorType: Int,
|
||||
val axisNames: Array<String>,
|
||||
val axisSetDetails: Array<AxisSetDetails>
|
||||
) {
|
||||
var isSuspended = true
|
||||
var hasRegisteredListener = false
|
||||
}
|
||||
|
||||
private val sensorManager: SensorManager?
|
||||
|
||||
private val sensorDetails = HashMap<Sensor, SensorDetails>()
|
||||
private val sensorDetails = ArrayList<SensorDetails>()
|
||||
|
||||
private val rotateCoordinatesForScreenOrientation: Boolean
|
||||
|
||||
/**
|
||||
* AOSP has a bug in InputDeviceSensorManager where
|
||||
* InputSensorEventListenerDelegate.removeSensor attempts to modify an ArrayList it's iterating
|
||||
* through in a way that throws a ConcurrentModificationException. Because of this, we can't
|
||||
* suspend individual sensors for InputDevices, but we can suspend all sensors at once.
|
||||
*/
|
||||
private val canSuspendSensorsIndividually: Boolean
|
||||
|
||||
private var unsuspendedSensors = 0
|
||||
|
||||
private var deviceQualifier = ""
|
||||
|
||||
@Keep
|
||||
|
@ -39,25 +51,22 @@ class DolphinSensorEventListener : SensorEventListener {
|
|||
sensorManager = DolphinApplication.getAppContext()
|
||||
.getSystemService(Context.SENSOR_SERVICE) as SensorManager?
|
||||
rotateCoordinatesForScreenOrientation = true
|
||||
canSuspendSensorsIndividually = true
|
||||
addSensors()
|
||||
sortSensorDetails()
|
||||
}
|
||||
|
||||
@Keep
|
||||
constructor(inputDevice: InputDevice) {
|
||||
rotateCoordinatesForScreenOrientation = false
|
||||
sensorManager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
inputDevice.sensorManager
|
||||
|
||||
// TODO: There is a bug where after suspending sensors, onSensorChanged can get called for
|
||||
// a sensor that we never registered as a listener for. The way our code is currently written,
|
||||
// this causes a NullPointerException, but if we checked for null we would instead have the
|
||||
// problem of being spammed with onSensorChanged calls even though the sensor shouldn't be
|
||||
// enabled. For now, let's comment out the ability to use InputDevice sensors.
|
||||
|
||||
//addSensors();
|
||||
canSuspendSensorsIndividually = false
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
sensorManager = inputDevice.sensorManager
|
||||
addSensors()
|
||||
} else {
|
||||
null
|
||||
sensorManager = null
|
||||
}
|
||||
sortSensorDetails()
|
||||
}
|
||||
|
||||
private fun addSensors() {
|
||||
|
@ -254,15 +263,22 @@ class DolphinSensorEventListener : SensorEventListener {
|
|||
) {
|
||||
val sensor = sensorManager!!.getDefaultSensor(sensorType)
|
||||
if (sensor != null) {
|
||||
sensorDetails[sensor] = SensorDetails(sensorType, axisNames, axisSetDetails)
|
||||
sensorDetails.add(SensorDetails(sensor, sensorType, axisNames, axisSetDetails))
|
||||
}
|
||||
}
|
||||
|
||||
private fun sortSensorDetails() {
|
||||
Collections.sort(
|
||||
sensorDetails,
|
||||
Comparator.comparingInt { s: SensorDetails -> s.sensorType }
|
||||
)
|
||||
}
|
||||
|
||||
override fun onSensorChanged(sensorEvent: SensorEvent) {
|
||||
val sensorDetails = sensorDetails[sensorEvent.sensor]
|
||||
val sensorDetails = sensorDetails.first{s -> sensorsAreEqual(s.sensor, sensorEvent.sensor)}
|
||||
|
||||
val values = sensorEvent.values
|
||||
val axisNames = sensorDetails!!.axisNames
|
||||
val axisNames = sensorDetails.axisNames
|
||||
val axisSetDetails = sensorDetails.axisSetDetails
|
||||
|
||||
var eventAxisIndex = 0
|
||||
|
@ -356,7 +372,7 @@ class DolphinSensorEventListener : SensorEventListener {
|
|||
}
|
||||
}
|
||||
if (!keepSensorAlive) {
|
||||
setSensorSuspended(sensorEvent.sensor, sensorDetails, true)
|
||||
setSensorSuspended(sensorDetails, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -381,18 +397,14 @@ class DolphinSensorEventListener : SensorEventListener {
|
|||
*/
|
||||
@Keep
|
||||
fun requestUnsuspendSensor(axisName: String) {
|
||||
for ((key, value) in sensorDetails) {
|
||||
if (listOf(*value.axisNames).contains(axisName)) {
|
||||
setSensorSuspended(key, value, false)
|
||||
for (sd in sensorDetails) {
|
||||
if (listOf(*sd.axisNames).contains(axisName)) {
|
||||
setSensorSuspended(sd, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setSensorSuspended(
|
||||
sensor: Sensor,
|
||||
sensorDetails: SensorDetails,
|
||||
suspend: Boolean
|
||||
) {
|
||||
private fun setSensorSuspended(sensorDetails: SensorDetails, suspend: Boolean) {
|
||||
var changeOccurred = false
|
||||
|
||||
synchronized(sensorDetails) {
|
||||
|
@ -403,10 +415,46 @@ class DolphinSensorEventListener : SensorEventListener {
|
|||
suspend
|
||||
)
|
||||
|
||||
if (suspend)
|
||||
sensorManager!!.unregisterListener(this, sensor)
|
||||
else
|
||||
sensorManager!!.registerListener(this, sensor, SAMPLING_PERIOD_US)
|
||||
if (suspend) {
|
||||
unsuspendedSensors -= 1
|
||||
} else {
|
||||
unsuspendedSensors += 1
|
||||
}
|
||||
|
||||
if (canSuspendSensorsIndividually) {
|
||||
if (suspend) {
|
||||
sensorManager!!.unregisterListener(this, sensorDetails.sensor)
|
||||
} else {
|
||||
sensorManager!!.registerListener(
|
||||
this,
|
||||
sensorDetails.sensor,
|
||||
SAMPLING_PERIOD_US
|
||||
)
|
||||
}
|
||||
sensorDetails.hasRegisteredListener = !suspend
|
||||
} else {
|
||||
if (suspend) {
|
||||
// If there are no unsuspended sensors left, unregister them all.
|
||||
// Otherwise, leave unregistering for later. A possible alternative could be
|
||||
// to unregister everything and then re-register the sensors we still want,
|
||||
// but I fear this could lead to dropped inputs.
|
||||
if (unsuspendedSensors == 0) {
|
||||
sensorManager!!.unregisterListener(this)
|
||||
for (sd in this.sensorDetails) {
|
||||
sd.hasRegisteredListener = false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!sensorDetails.hasRegisteredListener) {
|
||||
sensorManager!!.registerListener(
|
||||
this,
|
||||
sensorDetails.sensor,
|
||||
SAMPLING_PERIOD_US
|
||||
)
|
||||
sensorDetails.hasRegisteredListener = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sensorDetails.isSuspended = suspend
|
||||
|
||||
|
@ -415,14 +463,14 @@ class DolphinSensorEventListener : SensorEventListener {
|
|||
}
|
||||
|
||||
if (changeOccurred) {
|
||||
Log.info((if (suspend) "Suspended sensor " else "Unsuspended sensor ") + sensor.name)
|
||||
Log.info((if (suspend) "Suspended sensor " else "Unsuspended sensor ") + sensorDetails.sensor.name)
|
||||
}
|
||||
}
|
||||
|
||||
@Keep
|
||||
fun getAxisNames(): Array<String> {
|
||||
val axisNames = ArrayList<String>()
|
||||
for (sensorDetails in sensorDetailsSorted) {
|
||||
for (sensorDetails in sensorDetails) {
|
||||
sensorDetails.axisNames.forEach { axisNames.add(it) }
|
||||
}
|
||||
return axisNames.toArray(arrayOf())
|
||||
|
@ -432,7 +480,7 @@ class DolphinSensorEventListener : SensorEventListener {
|
|||
fun getNegativeAxes(): BooleanArray {
|
||||
val negativeAxes = ArrayList<Boolean>()
|
||||
|
||||
for (sensorDetails in sensorDetailsSorted) {
|
||||
for (sensorDetails in sensorDetails) {
|
||||
var eventAxisIndex = 0
|
||||
var detailsAxisIndex = 0
|
||||
var detailsAxisSetIndex = 0
|
||||
|
@ -467,22 +515,13 @@ class DolphinSensorEventListener : SensorEventListener {
|
|||
return result
|
||||
}
|
||||
|
||||
private val sensorDetailsSorted: List<SensorDetails>
|
||||
get() {
|
||||
val sensorDetails = ArrayList(sensorDetails.values)
|
||||
Collections.sort(
|
||||
sensorDetails,
|
||||
Comparator.comparingInt { s: SensorDetails -> s.sensorType }
|
||||
)
|
||||
return sensorDetails
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Set of three axes. Creates a negative companion to each axis, and corrects for device rotation.
|
||||
private const val AXIS_SET_TYPE_DEVICE_COORDINATES = 0
|
||||
|
||||
// Set of three axes. Creates a negative companion to each axis.
|
||||
private const val AXIS_SET_TYPE_OTHER_COORDINATES = 1
|
||||
|
||||
private var deviceRotation = Surface.ROTATION_0
|
||||
|
||||
// The fastest sampling rate Android lets us use without declaring the HIGH_SAMPLING_RATE_SENSORS
|
||||
|
@ -500,5 +539,13 @@ class DolphinSensorEventListener : SensorEventListener {
|
|||
fun setDeviceRotation(deviceRotation: Int) {
|
||||
this.deviceRotation = deviceRotation
|
||||
}
|
||||
|
||||
private fun sensorsAreEqual(s1: Sensor, s2: Sensor): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && s1.id > 0 && s2.id > 0) {
|
||||
s1.type == s2.type && s1.id == s2.id
|
||||
} else {
|
||||
s1.type == s2.type && s1.name == s2.name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,8 +101,8 @@ class AdvancedMappingDialog(
|
|||
return
|
||||
} else if (!isInput) {
|
||||
// Find the first device that has an output. (Most built-in devices don't have any)
|
||||
val deviceWithOutputs = devices.first { deviceHasOutputs(it) }
|
||||
if (deviceWithOutputs.isNotEmpty()) {
|
||||
val deviceWithOutputs = devices.firstOrNull { deviceHasOutputs(it) }
|
||||
if (deviceWithOutputs != null) {
|
||||
setSelectedDevice(deviceWithOutputs)
|
||||
binding.dropdownDevice.setText(deviceWithOutputs, false)
|
||||
return
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package org.dolphinemu.dolphinemu.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application.ActivityLifecycleCallbacks
|
||||
import android.os.Bundle
|
||||
|
||||
class ActivityTracker : ActivityLifecycleCallbacks {
|
||||
val resumedActivities = HashSet<Activity>()
|
||||
var backgroundExecutionAllowed = false
|
||||
|
||||
override fun onActivityCreated(activity: Activity, bundle: Bundle?) {}
|
||||
|
||||
override fun onActivityStarted(activity: Activity) {}
|
||||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
resumedActivities.add(activity)
|
||||
if (!backgroundExecutionAllowed && !resumedActivities.isEmpty()) {
|
||||
backgroundExecutionAllowed = true
|
||||
setBackgroundExecutionAllowedNative(true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityPaused(activity: Activity) {
|
||||
resumedActivities.remove(activity)
|
||||
if (backgroundExecutionAllowed && resumedActivities.isEmpty()) {
|
||||
backgroundExecutionAllowed = false
|
||||
setBackgroundExecutionAllowedNative(false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityStopped(activity: Activity) {}
|
||||
|
||||
override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) {}
|
||||
|
||||
override fun onActivityDestroyed(activity: Activity) {}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
external fun setBackgroundExecutionAllowedNative(allowed: Boolean)
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 15 KiB |
|
@ -1,27 +1,33 @@
|
|||
<vector
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="192dp"
|
||||
android:height="192dp"
|
||||
android:viewportHeight="2048"
|
||||
android:viewportWidth="2048">
|
||||
<path
|
||||
android:fillColor="#0057ab"
|
||||
android:pathData="m2046.8,1538.7c-6.8,-22.8 -49.3,-160.5 -120.8,-292.6 -94.3,-173.9 -175.3,-298.3 -310.4,-424.6 -43.4,-40.6 -84.6,-76.9 -127.4,-109.5l0,0c0,0 -0.3,-0.2 -0.9,-0.7 -0.8,-0.6 -1.6,-1.2 -2.4,-1.8 -16,-12.4 -83.3,-69.1 -59.4,-131.8 8,-21 27.4,-38.5 50.5,-52.6l124.1,-1v-46l-0,0c0,0 0,-0 0,-0 0,0 -72.7,-24.7 -199.8,-16.5 -119.3,7.7 -226.5,77.4 -246.6,87.2 -64.3,-18.4 -137.6,-34.6 -223.3,-49.2 -296.6,-50.3 -547.6,29.9 -673.6,117.3 -165.1,114.6 -160.5,221.4 -174.1,274.8 -8.4,33.1 -83.4,94.3 -82.5,137.2v45.2l15.5,16 58.4,-19.8 7,-24.4c24,-8.6 50.3,-20.5 74.2,-30.4 59.6,-24.5 64.2,-37.9 227.1,-62.2 75,-11.2 153.8,-14.4 212.6,-14.9 22.9,48.3 79.7,147.5 178.1,195 64,30.9 135.7,46.8 192.9,54.9l-7.5,37.7 113.7,16.8v-46l-0.1,-0c0,0 0,0 0,0 0,0 -95.4,-39.6 -154.1,-98.4 -40,-40 -49.5,-100.6 -51.6,-135.3l0.4,0c96.2,18.3 180.2,31.5 381.2,108.4 37.2,14.2 71.8,29.8 103.4,45.6l-5.9,3.3 90.6,73 108.2,89.5v-46l-0.4,-0.3c-1.6,-2 -124.8,-154.6 -331.8,-256.7 -171.1,-84.4 -311.6,-126.1 -506.2,-135.5 -212.8,-10.3 -369.5,17 -369.5,17 0,0 4.4,-94.5 165,-169.9 139.7,-65.5 359.4,-76.5 611.6,-12.1 356.3,90.9 477.8,245.6 631,405.6 97.2,101.5 186.6,244.2 243,343l-9.5,-4.3 29.2,75.4 41.8,89.1v-46.3l-1.2,-3.6z" />
|
||||
<path android:pathData="m1926,1292c-94.3,-173.9 -175.3,-298.3 -310.4,-424.6 -43.4,-40.6 -84.6,-76.9 -127.4,-109.5l0,0c0,0 -0.3,-0.2 -1,-0.7 -0.8,-0.6 -1.5,-1.2 -2.3,-1.8 -15.9,-12.3 -83.4,-69.1 -59.4,-131.8 26.3,-68.8 174.6,-99.6 174.6,-99.6 0,0 -72.7,-24.7 -199.8,-16.5 -119.3,7.7 -226.5,77.4 -246.6,87.2 -64.3,-18.4 -137.6,-34.6 -223.3,-49.2 -296.6,-50.3 -547.6,29.9 -673.6,117.3 -165.1,114.6 -160.5,221.4 -174.1,274.8 -9.8,38.4 -109.4,114.8 -75.5,156.4 21.1,26 91.4,-9.3 148.1,-32.6 59.6,-24.5 64.2,-37.9 227.1,-62.2 75,-11.2 153.8,-14.4 212.6,-14.9 22.9,48.3 79.7,147.5 178.1,195 132.9,64.2 299,63.4 299,63.4 0,0 -95.4,-39.6 -154.1,-98.4 -40,-40 -49.5,-100.6 -51.6,-135.3l0.4,0c96.2,18.3 180.2,31.5 381.2,108.4 175.8,67.3 295.9,165.3 295.9,165.3 0,0 -123.5,-154 -331.9,-256.8 -171.1,-84.4 -311.6,-126.1 -506.2,-135.5 -212.8,-10.3 -369.5,17 -369.5,17 0,0 4.4,-94.5 165,-169.9 139.7,-65.5 359.4,-76.5 611.6,-12.1 356.3,90.9 477.8,245.6 631,405.6 163.1,170.2 304.1,456.7 304.1,456.7 0,0 -43.5,-151.4 -121.8,-295.9z">
|
||||
<vector android:height="107dp" android:viewportHeight="571.3"
|
||||
android:viewportWidth="1024" android:width="192dp"
|
||||
xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillType="evenOdd"
|
||||
android:pathData="M1022.6,571.2c-1.3,0.2 -38.5,-170 -134.5,-264.6 -78.3,-77.1 -255.3,-227.8 -485.3,-211.4 -180,12.8 -155.3,99.2 -145.3,95.1 28.2,-11.8 160.4,-49.3 301.5,29.6 124.7,69.7 172.5,186.6 165,195.7 -1.6,1.9 -45.2,-108.8 -244.5,-151 -154.5,-32.7 -316.6,-5.9 -385.6,21.5 -28.1,11.1 -73.9,41.6 -82.8,11.1 -8.3,-28.5 33.5,-51.5 38.4,-70.4 6.4,-24.7 2,-147.1 179.8,-199.5 69.1,-20.4 206.7,-40 356.8,6.1 7.4,1.6 16.3,0.9 22.1,-2.6 12.9,-8 39,-24.9 91.2,-29.6 100.3,-8.9 129,34.5 124.2,42.9 -2.4,4.2 -80.1,-20.5 -96.6,28.1 -6.1,18.1 6.8,28.3 15.9,34.6 32.7,21.3 63.4,45.4 91.8,72.1 66.5,62.2 112.8,129 150.4,219.7 29.9,72.2 43.6,171.9 37.4,172.8Z" android:strokeWidth="0">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="0"
|
||||
android:endY="1588"
|
||||
android:startX="0"
|
||||
android:startY="506"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FF46D4FF"
|
||||
android:offset="0" />
|
||||
<item
|
||||
android:color="#FF1792FF"
|
||||
android:offset="1" />
|
||||
<gradient android:endX="225.6" android:endY="-23.7"
|
||||
android:startX="921.6" android:startY="672.3" android:type="linear">
|
||||
<item android:color="#FF3217FF" android:offset="0"/>
|
||||
<item android:color="#FF2B38FF" android:offset="0.3"/>
|
||||
<item android:color="#FF2455FF" android:offset="0.5"/>
|
||||
<item android:color="#FF1D74FF" android:offset="0.8"/>
|
||||
<item android:color="#FF1792FF" android:offset="1"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path android:fillType="evenOdd"
|
||||
android:pathData="M1022.7,571.3c-2.2,0.3 -73.6,-154.8 -169.6,-249.4 -78.3,-77.1 -220.9,-203.5 -451.3,-194.6 -102.8,4 -151.1,51.4 -144.1,63 6.8,11.3 140.6,-31 283.6,44.4 130.1,68.6 185.5,179.3 182.8,181.3 -2.2,1.7 -101.1,-86.7 -250.5,-125.8 -0.6,-0.1 -1.1,-0.2 -1.7,-0.3 -26.8,-6.6 -29,29.3 -1.5,63.5 31.9,39.7 46.9,43.2 45.3,48.3 -2,6.4 -63.1,8.8 -122.2,-32.5 -52,-36.4 -75.4,-85.8 -82.3,-92.1 -5.8,-5.3 -12.5,-4.1 -20.2,-3.9 -0.3,0 -0.6,0 -0.9,0 -83,3.2 -132.4,12.4 -196.1,37.7 -28.1,11.1 -66.8,13.6 -88.8,-9.2 -22.9,-23.6 39,-57 44,-75.9 6.4,-24.7 2.5,-121.3 180.4,-173.7 69.2,-20.4 207.1,-40.1 357.5,6.3 7.6,1.9 14.4,2.1 21.4,-2.8 18.6,-13.1 49.7,-30.8 101.9,-35.4 87.7,-7.8 115.7,19.1 113.4,24.1 -2,4.4 -80.1,4.2 -96.6,52.7 -6.1,18.1 6.8,28.3 15.9,34.6 32.7,21.3 63.4,45.4 91.8,72.1 66.6,62.2 112.8,129 150.4,219.7 29.9,72.2 40,147.8 37.5,148.2Z" android:strokeWidth="0">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient android:endX="438.4" android:endY="12.1"
|
||||
android:startX="551.7" android:startY="654.3" android:type="linear">
|
||||
<item android:color="#FF0E80FF" android:offset="0"/>
|
||||
<item android:color="#FF0789FF" android:offset="0.1"/>
|
||||
<item android:color="#FF0090FF" android:offset="0.3"/>
|
||||
<item android:color="#FF009DFF" android:offset="0.4"/>
|
||||
<item android:color="#FF00AAFF" android:offset="0.5"/>
|
||||
<item android:color="#FF00B7FF" android:offset="0.6"/>
|
||||
<item android:color="#FF00C4FF" android:offset="0.8"/>
|
||||
<item android:color="#FF21CCFF" android:offset="0.9"/>
|
||||
<item android:color="#FF46D4FF" android:offset="1"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
|
|
@ -1,19 +1,48 @@
|
|||
<vector android:height="192dp" android:viewportHeight="500"
|
||||
android:viewportWidth="500" android:width="192dp"
|
||||
xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#0057AB" android:fillType="nonZero"
|
||||
android:pathData="M344.28,301.46C343.6,299.18 339.35,285.41 332.2,272.21C322.77,254.81 314.66,242.38 301.16,229.75C296.81,225.69 292.7,222.06 288.41,218.8L288.42,218.8C288.42,218.8 288.38,218.78 288.32,218.73C288.24,218.67 288.16,218.61 288.08,218.55C286.48,217.32 279.75,211.64 282.15,205.38C282.95,203.28 284.89,201.53 287.19,200.12L299.6,200.02L299.6,195.42L299.6,195.42C299.6,195.42 299.6,195.42 299.6,195.42C299.6,195.42 292.33,192.95 279.62,193.77C267.69,194.53 256.97,201.51 254.96,202.49C248.54,200.65 241.2,199.03 232.63,197.57C202.97,192.54 177.86,200.56 165.27,209.3C148.76,220.76 149.21,231.44 147.85,236.78C147.01,240.09 139.51,246.21 139.6,250.5L139.6,255.02L141.15,256.61L146.99,254.63L147.69,252.19C150.09,251.34 152.72,250.14 155.11,249.16C161.07,246.71 161.53,245.37 177.82,242.94C185.32,241.82 193.21,241.5 199.08,241.45C201.37,246.28 207.05,256.2 216.89,260.96C223.3,264.05 230.47,265.64 236.18,266.45L235.43,270.22L246.8,271.9L246.8,267.3L246.79,267.3C246.79,267.3 246.79,267.3 246.79,267.3C246.79,267.3 237.25,263.34 231.38,257.47C227.38,253.47 226.43,247.4 226.23,243.93L226.26,243.93C235.88,245.76 244.28,247.08 264.38,254.78C268.09,256.2 271.56,257.76 274.72,259.34L274.12,259.67L283.18,266.98L294,275.93L294,271.33L293.96,271.3C293.8,271.1 281.48,255.84 260.78,245.63C243.67,237.19 229.62,233.02 210.16,232.07C188.88,231.04 173.21,233.77 173.21,233.77C173.21,233.77 173.64,224.32 189.71,216.79C203.67,210.23 225.65,209.14 250.86,215.57C286.49,224.66 298.64,240.14 313.96,256.13C323.69,266.27 332.62,280.55 338.26,290.43L337.31,290L340.23,297.54L344.4,306.45L344.4,301.82L344.28,301.46z"
|
||||
android:strokeColor="#00000000" android:strokeLineCap="butt"
|
||||
android:strokeLineJoin="miter" android:strokeWidth="0.1"/>
|
||||
<path android:fillType="nonZero"
|
||||
android:pathData="M332.2,276.8C322.77,259.41 314.67,246.97 301.16,234.34C296.82,230.28 292.7,226.65 288.41,223.39L288.42,223.39C288.42,223.39 288.39,223.37 288.32,223.32C288.25,223.27 288.17,223.21 288.09,223.15C286.5,221.92 279.75,216.24 282.15,209.97C284.78,203.09 299.6,200.01 299.6,200.01C299.6,200.01 292.34,197.54 279.62,198.36C267.69,199.12 256.97,206.1 254.96,207.08C248.54,205.24 241.21,203.62 232.63,202.17C202.97,197.13 177.87,205.15 165.27,213.9C148.76,225.36 149.22,236.03 147.86,241.38C146.88,245.22 136.92,252.85 140.3,257.01C142.41,259.61 149.44,256.08 155.11,253.75C161.07,251.3 161.53,249.96 177.83,247.53C185.32,246.42 193.21,246.09 199.08,246.05C201.37,250.87 207.05,260.79 216.9,265.55C230.19,271.97 246.8,271.89 246.8,271.89C246.8,271.89 237.25,267.93 231.38,262.06C227.39,258.06 226.43,251.99 226.23,248.52L226.26,248.53C235.88,250.35 244.29,251.67 264.38,259.37C281.96,266.1 293.97,275.9 293.97,275.9C293.97,275.9 281.62,260.5 260.78,250.22C243.67,241.78 229.63,237.61 210.16,236.67C188.89,235.63 173.21,238.37 173.21,238.37C173.21,238.37 173.65,228.91 189.71,221.38C203.68,214.83 225.65,213.73 250.87,220.16C286.49,229.25 298.64,244.73 313.97,260.72C330.28,277.74 344.38,306.39 344.38,306.39C344.38,306.39 340.03,291.25 332.2,276.8z"
|
||||
android:strokeColor="#00000000" android:strokeLineCap="butt"
|
||||
android:strokeLineJoin="miter" android:strokeWidth="0.1">
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:pathData="M74.85,66.87c-0.05,0 -1.66,-7.31 -5.79,-11.38 -3.37,-3.32 -10.98,-9.8 -20.88,-9.09 -7.74,0.55 -6.68,4.27 -6.25,4.09 1.21,-0.51 6.9,-2.12 12.97,1.27 5.37,3 7.42,8.03 7.1,8.42 -0.07,0.08 -1.94,-4.68 -10.52,-6.5 -6.65,-1.41 -13.62,-0.26 -16.59,0.92 -1.21,0.48 -3.18,1.79 -3.56,0.48 -0.36,-1.23 1.44,-2.22 1.65,-3.03 0.28,-1.06 0.09,-6.33 7.74,-8.59 2.97,-0.88 8.89,-1.72 15.35,0.26 0.32,0.07 0.7,0.04 0.95,-0.11 0.55,-0.34 1.68,-1.07 3.92,-1.27 4.31,-0.38 5.55,1.48 5.34,1.84 -0.1,0.18 -3.45,-0.88 -4.15,1.21 -0.26,0.78 0.29,1.22 0.69,1.49 1.41,0.92 2.73,1.95 3.95,3.1 2.86,2.68 4.85,5.55 6.47,9.45 1.29,3.11 1.87,7.39 1.61,7.43Z"
|
||||
android:strokeWidth="0"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient android:endX="139.6" android:endY="306.4"
|
||||
android:startX="139.6" android:startY="198.2" android:type="linear">
|
||||
<item android:color="#FF46D4FF" android:offset="0"/>
|
||||
<item android:color="#FF1792FF" android:offset="1"/>
|
||||
<gradient
|
||||
android:startX="70.5"
|
||||
android:startY="71.22"
|
||||
android:endX="40.56"
|
||||
android:endY="41.28"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FF3217FF"/>
|
||||
<item android:offset="0.26" android:color="#FF2B38FF"/>
|
||||
<item android:offset="0.5" android:color="#FF2455FF"/>
|
||||
<item android:offset="0.75" android:color="#FF1D74FF"/>
|
||||
<item android:offset="1" android:color="#FF1792FF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M74.85,66.88c-0.09,0.01 -3.17,-6.66 -7.3,-10.73 -3.37,-3.32 -9.5,-8.76 -19.42,-8.37 -4.43,0.17 -6.5,2.21 -6.2,2.71 0.29,0.49 6.05,-1.33 12.2,1.91 5.6,2.95 7.98,7.71 7.86,7.8 -0.1,0.07 -4.35,-3.73 -10.78,-5.41 -0.02,0 -0.05,0 -0.07,-0.01 -1.15,-0.28 -1.25,1.26 -0.06,2.73 1.37,1.71 2.02,1.86 1.95,2.08 -0.08,0.28 -2.72,0.38 -5.26,-1.4 -2.24,-1.57 -3.24,-3.69 -3.54,-3.96 -0.25,-0.23 -0.54,-0.17 -0.87,-0.17 -0.01,0 -0.03,0 -0.04,0 -3.57,0.14 -5.7,0.53 -8.44,1.62 -1.21,0.48 -2.87,0.58 -3.82,-0.4 -0.98,-1.02 1.68,-2.45 1.89,-3.26 0.28,-1.06 0.11,-5.22 7.76,-7.48 2.98,-0.88 8.91,-1.72 15.38,0.27 0.33,0.08 0.62,0.09 0.92,-0.12 0.8,-0.56 2.14,-1.32 4.38,-1.52 3.77,-0.34 4.98,0.82 4.88,1.04 -0.09,0.19 -3.45,0.18 -4.15,2.27 -0.26,0.78 0.29,1.22 0.69,1.49 1.41,0.92 2.73,1.95 3.95,3.1 2.86,2.68 4.85,5.55 6.47,9.45 1.29,3.11 1.72,6.36 1.61,6.38Z"
|
||||
android:strokeWidth="0"
|
||||
android:fillType="evenOdd">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="54.59"
|
||||
android:startY="70.45"
|
||||
android:endX="49.71"
|
||||
android:endY="42.82"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FF0E80FF"/>
|
||||
<item android:offset="0.13" android:color="#FF0789FF"/>
|
||||
<item android:offset="0.25" android:color="#FF0090FF"/>
|
||||
<item android:offset="0.38" android:color="#FF009DFF"/>
|
||||
<item android:offset="0.5" android:color="#FF00AAFF"/>
|
||||
<item android:offset="0.62" android:color="#FF00B7FF"/>
|
||||
<item android:offset="0.75" android:color="#FF00C4FF"/>
|
||||
<item android:offset="0.87" android:color="#FF21CCFF"/>
|
||||
<item android:offset="1" android:color="#FF46D4FF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<vector android:height="192dp" android:viewportHeight="500"
|
||||
android:viewportWidth="500" android:width="192dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#000000" android:fillType="evenOdd"
|
||||
android:pathData="M226.26,250.63C235.88,252.46 244.28,253.78 264.38,261.48C281.96,268.21 293.97,278 293.97,278C293.97,278 281.62,262.6 260.78,252.32C243.67,243.89 229.62,239.72 210.16,238.77C188.88,237.74 173.2,240.47 173.2,240.47C173.2,240.47 173.64,231.02 189.7,223.48C203.67,216.93 225.64,215.84 250.86,222.27C286.49,231.36 298.64,246.83 313.96,262.83C330.27,279.85 344.38,308.49 344.38,308.49C344.38,308.49 340.03,293.36 332.2,278.91C322.77,261.51 314.66,249.08 301.15,236.45C283.48,219.92 269.59,210.54 232.63,204.27C202.96,199.24 177.86,207.26 165.27,216C148.76,227.46 149.21,238.14 147.85,243.48C146.87,247.32 136.91,254.96 140.3,259.12C142.41,261.71 149.44,258.19 155.11,255.86C161.07,253.4 161.53,252.07 177.82,249.64C185.49,248.5 193.56,248.19 199.47,248.15"
|
||||
android:strokeColor="#00000000" android:strokeLineCap="butt"
|
||||
android:strokeLineJoin="miter" android:strokeWidth="0.1"/>
|
||||
<path android:fillColor="#000000" android:fillType="evenOdd"
|
||||
android:pathData="M197.9,245.4C197.9,245.4 203.6,261.23 216.89,267.66C230.19,274.08 246.79,274 246.79,274C246.79,274 237.25,270.04 231.38,264.16C225.51,258.29 226.2,247.94 226.2,247.94ZM250.39,208.96C250.39,208.96 253.37,209.82 254.78,209.27C256.19,208.72 267.25,201.26 279.62,200.46C292.33,199.65 299.6,202.12 299.6,202.12C299.6,202.12 284.78,205.19 282.14,212.08C279.43,219.17 288.42,225.5 288.42,225.5L262.14,222.75Z"
|
||||
android:strokeColor="#00000000" android:strokeLineCap="butt"
|
||||
android:strokeLineJoin="miter" android:strokeWidth="0.1"/>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:pathData="M74.85,66.88c-0.09,0.01 -3.17,-6.66 -7.3,-10.73 -3.37,-3.32 -9.5,-8.76 -19.42,-8.37 -4.43,0.17 -6.5,2.21 -6.2,2.71 0.29,0.49 6.05,-1.33 12.2,1.91 5.6,2.95 7.98,7.71 7.86,7.8 -0.1,0.07 -4.35,-3.73 -10.78,-5.41 -0.02,0 -0.05,0 -0.07,-0.01 -1.15,-0.28 -1.25,1.26 -0.06,2.73 1.37,1.71 2.02,1.86 1.95,2.08 -0.08,0.28 -2.72,0.38 -5.26,-1.4 -2.24,-1.57 -3.24,-3.69 -3.54,-3.96 -0.25,-0.23 -0.54,-0.17 -0.87,-0.17 -0.01,0 -0.03,0 -0.04,0 -3.57,0.14 -5.7,0.53 -8.44,1.62 -1.21,0.48 -2.87,0.58 -3.82,-0.4 -0.98,-1.02 1.68,-2.45 1.89,-3.26 0.28,-1.06 0.11,-5.22 7.76,-7.48 2.98,-0.88 8.91,-1.72 15.38,0.27 0.33,0.08 0.62,0.09 0.92,-0.12 0.8,-0.56 2.14,-1.32 4.38,-1.52 3.77,-0.34 4.98,0.82 4.88,1.04 -0.09,0.19 -3.45,0.18 -4.15,2.27 -0.26,0.78 0.29,1.22 0.69,1.49 1.41,0.92 2.73,1.95 3.95,3.1 2.86,2.68 4.85,5.55 6.47,9.45 1.29,3.11 1.72,6.36 1.61,6.38Z"
|
||||
android:strokeWidth="0"
|
||||
android:fillColor="#000"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
|
|
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 4 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 7 KiB |
|
@ -69,7 +69,7 @@
|
|||
|
||||
<!-- General Preference Fragment -->
|
||||
<string name="general_submenu">General</string>
|
||||
<string name="dual_core">Dual Core</string>
|
||||
<string name="dual_core">Dual Core (speedhack)</string>
|
||||
<string name="dual_core_description">Split workload to two CPU cores instead of one. Increases speed.</string>
|
||||
<string name="enable_cheats">Enable Cheats</string>
|
||||
<string name="speed_limit">Speed Limit (0% = Unlimited)</string>
|
||||
|
@ -874,7 +874,7 @@ It can efficiently compress both junk data and encrypted Wii data.
|
|||
<string name="about_website"><a href="https://dolphin-emu.org/">Website</a></string>
|
||||
<string name="about_github"><a href="https://github.com/dolphin-emu/dolphin">GitHub</a></string>
|
||||
<string name="about_support"><a href="https://forums.dolphin-emu.org/">Support</a></string>
|
||||
<string name="about_copyright_warning">\u00A9 2003–2015+ Dolphin Team. \u201cGameCube\u201d and \u201cWii\u201d are trademarks of Nintendo. Dolphin is not affiliated with Nintendo in any way.</string>
|
||||
<string name="about_copyright_warning">\u00A9 2003–2024+ Dolphin Team. \u201cGameCube\u201d and \u201cWii\u201d are trademarks of Nintendo. Dolphin is not affiliated with Nintendo in any way.</string>
|
||||
<string name="system_driver">System driver</string>
|
||||
<string name="system_driver_desc">The GPU driver that is part of the OS.</string>
|
||||
|
||||
|
@ -914,6 +914,8 @@ It can efficiently compress both junk data and encrypted Wii data.
|
|||
<string name="infinity_number">Figure Number</string>
|
||||
<string name="invalid_infinity_figure">Invalid Figure Selection</string>
|
||||
<string name="infinity_hexagon_label">Power Disc/Play Set</string>
|
||||
<string name="infinity_power_hex_two_label">Power Disc Two</string>
|
||||
<string name="infinity_power_hex_three_label">Power Disc Three</string>
|
||||
<string name="infinity_p1_label">Player One</string>
|
||||
<string name="infinity_p2_label">Player Two</string>
|
||||
<string name="infinity_p1a1_label">P1 Ability One</string>
|
||||
|
|
|
@ -14,6 +14,5 @@ android.enableJetifier=true
|
|||
android.useAndroidX=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
android.defaults.buildfeatures.buildconfig=true
|
||||
android.nonTransitiveRClass=false
|
||||
android.nonFinalResIds=false
|
||||
|
|
21
Source/Android/jni/ActivityTracker.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2024 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Core/AchievementManager.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_utils_ActivityTracker_setBackgroundExecutionAllowedNative(
|
||||
JNIEnv*, jclass, jboolean allowed)
|
||||
{
|
||||
// This is called with allowed == false when the app goes into the background.
|
||||
// We use this to stop continuously running background threads so we don't waste battery.
|
||||
|
||||
INFO_LOG_FMT(CORE, "SetBackgroundExecutionAllowed {}", allowed);
|
||||
AchievementManager::GetInstance().SetBackgroundExecutionAllowed(allowed);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
add_library(main SHARED
|
||||
ActivityTracker.cpp
|
||||
Cheats/ARCheat.cpp
|
||||
Cheats/Cheats.h
|
||||
Cheats/GeckoCheat.cpp
|
||||
|
@ -11,6 +12,7 @@ add_library(main SHARED
|
|||
GameList/GameFile.cpp
|
||||
GameList/GameFile.h
|
||||
GameList/GameFileCache.cpp
|
||||
GpuDriver.cpp
|
||||
Host.cpp
|
||||
Host.h
|
||||
InfinityConfig.cpp
|
||||
|
@ -32,7 +34,6 @@ add_library(main SHARED
|
|||
RiivolutionPatches.cpp
|
||||
SkylanderConfig.cpp
|
||||
WiiUtils.cpp
|
||||
GpuDriver.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(main
|
||||
|
|
|
@ -53,7 +53,7 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_GraphicsModGroup_getMods(JN
|
|||
// If no group matches the mod's features, or if the mod has no features, skip it
|
||||
if (std::none_of(mod.m_features.begin(), mod.m_features.end(),
|
||||
[&groups](const GraphicsModFeatureConfig& feature) {
|
||||
return groups.count(feature.m_group) == 1;
|
||||
return groups.contains(feature.m_group);
|
||||
}))
|
||||
{
|
||||
continue;
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include "Core/IOS/USB/Emulated/Infinity.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
using FigureUIPosition = IOS::HLE::USB::FigureUIPosition;
|
||||
|
||||
extern "C" {
|
||||
|
||||
JNIEXPORT jobject JNICALL
|
||||
|
@ -66,7 +68,7 @@ Java_org_dolphinemu_dolphinemu_features_infinitybase_InfinityConfig_removeFigure
|
|||
jint position)
|
||||
{
|
||||
auto& system = Core::System::GetInstance();
|
||||
system.GetInfinityBase().RemoveFigure(position);
|
||||
system.GetInfinityBase().RemoveFigure(static_cast<FigureUIPosition>(position));
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
|
@ -87,9 +89,10 @@ Java_org_dolphinemu_dolphinemu_features_infinitybase_InfinityConfig_loadFigure(J
|
|||
}
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
system.GetInfinityBase().RemoveFigure(position);
|
||||
system.GetInfinityBase().RemoveFigure(static_cast<FigureUIPosition>(position));
|
||||
return ToJString(env,
|
||||
system.GetInfinityBase().LoadFigure(file_data, std::move(inf_file), position));
|
||||
system.GetInfinityBase().LoadFigure(file_data, std::move(inf_file),
|
||||
static_cast<FigureUIPosition>(position)));
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
|
@ -102,7 +105,7 @@ Java_org_dolphinemu_dolphinemu_features_infinitybase_InfinityConfig_createFigure
|
|||
|
||||
auto& system = Core::System::GetInstance();
|
||||
system.GetInfinityBase().CreateFigure(file_name, fig_num);
|
||||
system.GetInfinityBase().RemoveFigure(position);
|
||||
system.GetInfinityBase().RemoveFigure(static_cast<FigureUIPosition>(position));
|
||||
|
||||
File::IOFile inf_file(file_name, "r+b");
|
||||
if (!inf_file)
|
||||
|
@ -116,6 +119,7 @@ Java_org_dolphinemu_dolphinemu_features_infinitybase_InfinityConfig_createFigure
|
|||
}
|
||||
|
||||
return ToJString(env,
|
||||
system.GetInfinityBase().LoadFigure(file_data, std::move(inf_file), position));
|
||||
system.GetInfinityBase().LoadFigure(file_data, std::move(inf_file),
|
||||
static_cast<FigureUIPosition>(position)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -173,6 +173,11 @@ bool Host_RendererIsFullscreen()
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Host_TASInputHasFocus()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Host_YieldToUI()
|
||||
{
|
||||
}
|
||||
|
@ -279,7 +284,7 @@ JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunnin
|
|||
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunningAndStarted(JNIEnv*,
|
||||
jclass)
|
||||
{
|
||||
return static_cast<jboolean>(Core::IsRunningAndStarted());
|
||||
return static_cast<jboolean>(Core::IsRunning(Core::System::GetInstance()));
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
|
@ -412,11 +417,11 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteJitBloc
|
|||
HostThreadLock guard;
|
||||
auto& system = Core::System::GetInstance();
|
||||
auto& jit_interface = system.GetJitInterface();
|
||||
const Core::CPUThreadGuard cpu_guard(system);
|
||||
if (jit_interface.GetCore() == nullptr)
|
||||
{
|
||||
env->CallStaticVoidMethod(native_library_class, IDCache::GetDisplayToastMsg(),
|
||||
ToJString(env, Common::GetStringT("JIT is not active")),
|
||||
static_cast<jboolean>(false));
|
||||
ToJString(env, Common::GetStringT("JIT is not active")), JNI_FALSE);
|
||||
return;
|
||||
}
|
||||
const std::string filename = fmt::format("{}{}.txt", File::GetUserPath(D_DUMPDEBUG_JITBLOCKS_IDX),
|
||||
|
@ -427,13 +432,13 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteJitBloc
|
|||
env->CallStaticVoidMethod(
|
||||
native_library_class, IDCache::GetDisplayToastMsg(),
|
||||
ToJString(env, Common::FmtFormatT("Failed to open \"{0}\" for writing.", filename)),
|
||||
static_cast<jboolean>(false));
|
||||
JNI_FALSE);
|
||||
return;
|
||||
}
|
||||
jit_interface.JitBlockLogDump(Core::CPUThreadGuard{system}, f.GetHandle());
|
||||
jit_interface.JitBlockLogDump(cpu_guard, f.GetHandle());
|
||||
env->CallStaticVoidMethod(native_library_class, IDCache::GetDisplayToastMsg(),
|
||||
ToJString(env, Common::FmtFormatT("Wrote to \"{0}\".", filename)),
|
||||
static_cast<jboolean>(false));
|
||||
JNI_FALSE);
|
||||
}
|
||||
|
||||
// Surface Handling
|
||||
|
@ -696,7 +701,7 @@ JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_ConvertD
|
|||
blob_reader = DiscIO::CreateBlobReader(in_path);
|
||||
|
||||
if (!blob_reader)
|
||||
return static_cast<jboolean>(false);
|
||||
return JNI_FALSE;
|
||||
|
||||
jobject jCallbackGlobal = env->NewGlobalRef(jCallback);
|
||||
Common::ScopeGuard scope_guard([jCallbackGlobal, env] { env->DeleteGlobalRef(jCallbackGlobal); });
|
||||
|
|
|
@ -20,7 +20,7 @@ AlsaSound::~AlsaSound()
|
|||
m_thread_status.store(ALSAThreadStatus::STOPPING);
|
||||
|
||||
// Immediately lock and unlock mutex to prevent cv race.
|
||||
std::unique_lock<std::mutex>{cv_m};
|
||||
std::unique_lock<std::mutex>{cv_m}.unlock();
|
||||
|
||||
// Give the opportunity to the audio thread
|
||||
// to realize we are stopping the emulation
|
||||
|
@ -82,7 +82,7 @@ bool AlsaSound::SetRunning(bool running)
|
|||
m_thread_status.store(running ? ALSAThreadStatus::RUNNING : ALSAThreadStatus::PAUSED);
|
||||
|
||||
// Immediately lock and unlock mutex to prevent cv race.
|
||||
std::unique_lock<std::mutex>{cv_m};
|
||||
std::unique_lock<std::mutex>{cv_m}.unlock();
|
||||
|
||||
// Notify thread that status has changed
|
||||
cv.notify_one();
|
||||
|
|
|
@ -137,6 +137,12 @@ OpenSLESStream::~OpenSLESStream()
|
|||
}
|
||||
}
|
||||
|
||||
bool OpenSLESStream::SetRunning(bool running)
|
||||
{
|
||||
SLuint32 new_state = running ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_PAUSED;
|
||||
return (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, new_state) == SL_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
void OpenSLESStream::SetVolume(int volume)
|
||||
{
|
||||
const SLmillibel attenuation =
|
||||
|
|
|
@ -14,7 +14,7 @@ class OpenSLESStream final : public SoundStream
|
|||
public:
|
||||
~OpenSLESStream() override;
|
||||
bool Init() override;
|
||||
bool SetRunning(bool running) override { return true; }
|
||||
bool SetRunning(bool running) override;
|
||||
void SetVolume(int volume) override;
|
||||
static bool IsValid() { return true; }
|
||||
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "AudioCommon/WaveFile.h"
|
||||
#include "AudioCommon/Mixer.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "AudioCommon/Mixer.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/IOFile.h"
|
||||
|
|
|
@ -30,3 +30,59 @@ endif()
|
|||
if (WIN32 AND ENABLE_AUTOUPDATE)
|
||||
add_subdirectory(WinUpdater)
|
||||
endif()
|
||||
|
||||
if (APPLE AND ENABLE_QT)
|
||||
set(DOLPHIN_MAC_BUNDLE "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Dolphin.app")
|
||||
|
||||
add_custom_target(build_final_bundle ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory
|
||||
${DOLPHIN_MAC_BUNDLE}
|
||||
|
||||
COMMAND cp -R
|
||||
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/DolphinQt.app
|
||||
${DOLPHIN_MAC_BUNDLE}
|
||||
|
||||
# HACK: The Updater does not support setting the executable bit on new files,
|
||||
# so don't use the new executable name, and instead continue to use "Dolphin".
|
||||
COMMAND ${CMAKE_COMMAND} -E rename
|
||||
${DOLPHIN_MAC_BUNDLE}/Contents/MacOS/DolphinQt
|
||||
${DOLPHIN_MAC_BUNDLE}/Contents/MacOS/Dolphin
|
||||
|
||||
COMMAND plutil
|
||||
-replace CFBundleExecutable -string Dolphin
|
||||
${DOLPHIN_MAC_BUNDLE}/Contents/Info.plist
|
||||
|
||||
DEPENDS dolphin-emu)
|
||||
|
||||
if (ENABLE_AUTOUPDATE)
|
||||
add_dependencies(build_final_bundle MacUpdater)
|
||||
|
||||
add_custom_command(TARGET build_final_bundle
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||
"${DOLPHIN_MAC_BUNDLE}/Contents/Helpers"
|
||||
|
||||
COMMAND cp -R
|
||||
"$<TARGET_BUNDLE_DIR:MacUpdater>"
|
||||
"${DOLPHIN_MAC_BUNDLE}/Contents/Helpers/Dolphin Updater.app")
|
||||
|
||||
if (MACOS_CODE_SIGNING)
|
||||
add_custom_command(TARGET build_final_bundle
|
||||
POST_BUILD
|
||||
COMMAND "${CMAKE_SOURCE_DIR}/Tools/mac-codesign.sh"
|
||||
"-t"
|
||||
"${MACOS_CODE_SIGNING_IDENTITY}"
|
||||
"${DOLPHIN_MAC_BUNDLE}/Contents/Helpers/Dolphin Updater.app")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (MACOS_CODE_SIGNING)
|
||||
add_custom_command(TARGET build_final_bundle
|
||||
POST_BUILD
|
||||
COMMAND "${CMAKE_SOURCE_DIR}/Tools/mac-codesign.sh"
|
||||
"-t"
|
||||
"-e" "${CMAKE_SOURCE_DIR}/Source/Core/DolphinQt/DolphinEmu$<$<CONFIG:Debug>:Debug>.entitlements"
|
||||
"${MACOS_CODE_SIGNING_IDENTITY}"
|
||||
"${DOLPHIN_MAC_BUNDLE}")
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
@ -87,26 +87,6 @@ void ARM64XEmitter::SetCodePtr(u8* ptr, u8* end, bool write_failed)
|
|||
m_lastCacheFlushEnd = ptr;
|
||||
}
|
||||
|
||||
const u8* ARM64XEmitter::GetCodePtr() const
|
||||
{
|
||||
return m_code;
|
||||
}
|
||||
|
||||
u8* ARM64XEmitter::GetWritableCodePtr()
|
||||
{
|
||||
return m_code;
|
||||
}
|
||||
|
||||
const u8* ARM64XEmitter::GetCodeEnd() const
|
||||
{
|
||||
return m_code_end;
|
||||
}
|
||||
|
||||
u8* ARM64XEmitter::GetWritableCodeEnd()
|
||||
{
|
||||
return m_code_end;
|
||||
}
|
||||
|
||||
void ARM64XEmitter::ReserveCodeSpace(u32 bytes)
|
||||
{
|
||||
for (u32 i = 0; i < bytes / 4; i++)
|
||||
|
|
|
@ -680,10 +680,10 @@ public:
|
|||
void SetCodePtr(u8* ptr, u8* end, bool write_failed = false);
|
||||
|
||||
void SetCodePtrUnsafe(u8* ptr, u8* end, bool write_failed = false);
|
||||
const u8* GetCodePtr() const;
|
||||
u8* GetWritableCodePtr();
|
||||
const u8* GetCodeEnd() const;
|
||||
u8* GetWritableCodeEnd();
|
||||
const u8* GetCodePtr() const { return m_code; }
|
||||
u8* GetWritableCodePtr() { return m_code; }
|
||||
const u8* GetCodeEnd() const { return m_code_end; }
|
||||
u8* GetWritableCodeEnd() { return m_code_end; }
|
||||
void ReserveCodeSpace(u32 bytes);
|
||||
u8* AlignCode16();
|
||||
u8* AlignCodePage();
|
||||
|
|
|
@ -15,11 +15,16 @@
|
|||
#include <Windows.h>
|
||||
#include <arm64intr.h>
|
||||
#include "Common/WindowsRegistry.h"
|
||||
#else
|
||||
#ifndef __FreeBSD__
|
||||
#elif defined(__linux__)
|
||||
#include <asm/hwcap.h>
|
||||
#endif
|
||||
#include <sys/auxv.h>
|
||||
#elif defined(__FreeBSD__)
|
||||
#include <sys/auxv.h>
|
||||
#elif defined(__OpenBSD__)
|
||||
#include <machine/armreg.h>
|
||||
#include <machine/cpu.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
@ -183,7 +188,7 @@ static bool Read_MIDR_EL1(u64* value)
|
|||
|
||||
#endif
|
||||
|
||||
#ifndef __APPLE__
|
||||
#if defined(_WIN32) || defined(__linux__) || defined(__FreeBSD__)
|
||||
|
||||
static std::string MIDRToString(u64 midr)
|
||||
{
|
||||
|
@ -248,7 +253,7 @@ void CPUInfo::Detect()
|
|||
{
|
||||
cpu_id = MIDRToString(reg);
|
||||
}
|
||||
#else
|
||||
#elif defined(__linux__) || defined(__FreeBSD__)
|
||||
// Linux, Android, and FreeBSD
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
|
@ -277,6 +282,33 @@ void CPUInfo::Detect()
|
|||
{
|
||||
cpu_id = MIDRToString(midr);
|
||||
}
|
||||
#elif defined(__OpenBSD__)
|
||||
// OpenBSD
|
||||
int mib[2];
|
||||
size_t len;
|
||||
char hwmodel[256];
|
||||
uint64_t isar0;
|
||||
|
||||
mib[0] = CTL_HW;
|
||||
mib[1] = HW_MODEL;
|
||||
len = std::size(hwmodel);
|
||||
if (sysctl(mib, 2, &hwmodel, &len, nullptr, 0) != -1)
|
||||
model_name = std::string(hwmodel, len - 1);
|
||||
|
||||
mib[0] = CTL_MACHDEP;
|
||||
mib[1] = CPU_ID_AA64ISAR0;
|
||||
len = sizeof(isar0);
|
||||
if (sysctl(mib, 2, &isar0, &len, nullptr, 0) != -1)
|
||||
{
|
||||
if (ID_AA64ISAR0_AES(isar0) >= ID_AA64ISAR0_AES_BASE)
|
||||
bAES = true;
|
||||
if (ID_AA64ISAR0_SHA1(isar0) >= ID_AA64ISAR0_SHA1_BASE)
|
||||
bSHA1 = true;
|
||||
if (ID_AA64ISAR0_SHA2(isar0) >= ID_AA64ISAR0_SHA2_BASE)
|
||||
bSHA2 = true;
|
||||
if (ID_AA64ISAR0_CRC32(isar0) >= ID_AA64ISAR0_CRC32_BASE)
|
||||
bCRC32 = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
model_name = ReplaceAll(model_name, ",", "_");
|
||||
|
|
|
@ -42,40 +42,40 @@ constexpr ExtendedMnemonicDesc INVALID_EXT_MNEMONIC = {0, nullptr};
|
|||
|
||||
// All operands as referenced by the Gekko/Broadway user manual
|
||||
// See section 12.1.2 under Chapter 12
|
||||
constexpr OperandDesc _A = OperandDesc{Mask(11, 15), {16, false}};
|
||||
constexpr OperandDesc _B = OperandDesc{Mask(16, 20), {11, false}};
|
||||
constexpr OperandDesc _BD = OperandDesc{Mask(16, 29), {0, true}};
|
||||
constexpr OperandDesc _BI = OperandDesc{Mask(11, 15), {16, false}};
|
||||
constexpr OperandDesc _BO = OperandDesc{Mask(6, 10), {21, false}};
|
||||
constexpr OperandDesc _C = OperandDesc{Mask(21, 25), {6, false}};
|
||||
constexpr OperandDesc _Crba = OperandDesc{Mask(11, 15), {16, false}};
|
||||
constexpr OperandDesc _Crbb = OperandDesc{Mask(16, 20), {11, false}};
|
||||
constexpr OperandDesc _Crbd = OperandDesc{Mask(6, 10), {21, false}};
|
||||
constexpr OperandDesc _Crfd = OperandDesc{Mask(6, 8), {23, false}};
|
||||
constexpr OperandDesc _Crfs = OperandDesc{Mask(11, 13), {18, false}};
|
||||
constexpr OperandDesc _CRM = OperandDesc{Mask(12, 19), {12, false}};
|
||||
constexpr OperandDesc _D = OperandDesc{Mask(6, 10), {21, false}};
|
||||
constexpr OperandDesc _FM = OperandDesc{Mask(7, 14), {17, false}};
|
||||
constexpr OperandDesc _W1 = OperandDesc{Mask(16, 16), {15, false}};
|
||||
constexpr OperandDesc _W2 = OperandDesc{Mask(21, 21), {10, false}};
|
||||
constexpr OperandDesc _IMM = OperandDesc{Mask(16, 19), {12, false}};
|
||||
constexpr OperandDesc _L = OperandDesc{Mask(10, 10), {21, false}};
|
||||
constexpr OperandDesc _LI = OperandDesc{Mask(6, 29), {0, true}};
|
||||
constexpr OperandDesc _MB = OperandDesc{Mask(21, 25), {6, false}};
|
||||
constexpr OperandDesc _ME = OperandDesc{Mask(26, 30), {1, false}};
|
||||
constexpr OperandDesc _NB = OperandDesc{Mask(16, 20), {11, false}};
|
||||
constexpr OperandDesc _Offd = OperandDesc{Mask(16, 31), {0, true}};
|
||||
constexpr OperandDesc _OffdPs = OperandDesc{Mask(20, 31), {0, true}};
|
||||
constexpr OperandDesc _S = OperandDesc{Mask(6, 10), {21, false}};
|
||||
constexpr OperandDesc _SH = OperandDesc{Mask(16, 20), {11, false}};
|
||||
constexpr OperandDesc _SIMM = OperandDesc{Mask(16, 31), {0, true}};
|
||||
constexpr OperandDesc _SPR = OperandDesc{Mask(11, 20), {11, false}};
|
||||
constexpr OperandDesc _SR = OperandDesc{Mask(12, 15), {16, false}};
|
||||
constexpr OperandDesc _TO = OperandDesc{Mask(6, 10), {21, false}};
|
||||
constexpr OperandDesc _TPR = OperandDesc{Mask(11, 20), {11, false}};
|
||||
constexpr OperandDesc _UIMM = OperandDesc{Mask(16, 31), {0, false}};
|
||||
constexpr OperandDesc _I1 = OperandDesc{Mask(17, 19), {12, false}};
|
||||
constexpr OperandDesc _I2 = OperandDesc{Mask(22, 24), {7, false}};
|
||||
constexpr OperandDesc OpDesc_A = OperandDesc{Mask(11, 15), {16, false}};
|
||||
constexpr OperandDesc OpDesc_B = OperandDesc{Mask(16, 20), {11, false}};
|
||||
constexpr OperandDesc OpDesc_BD = OperandDesc{Mask(16, 29), {0, true}};
|
||||
constexpr OperandDesc OpDesc_BI = OperandDesc{Mask(11, 15), {16, false}};
|
||||
constexpr OperandDesc OpDesc_BO = OperandDesc{Mask(6, 10), {21, false}};
|
||||
constexpr OperandDesc OpDesc_C = OperandDesc{Mask(21, 25), {6, false}};
|
||||
constexpr OperandDesc OpDesc_Crba = OperandDesc{Mask(11, 15), {16, false}};
|
||||
constexpr OperandDesc OpDesc_Crbb = OperandDesc{Mask(16, 20), {11, false}};
|
||||
constexpr OperandDesc OpDesc_Crbd = OperandDesc{Mask(6, 10), {21, false}};
|
||||
constexpr OperandDesc OpDesc_Crfd = OperandDesc{Mask(6, 8), {23, false}};
|
||||
constexpr OperandDesc OpDesc_Crfs = OperandDesc{Mask(11, 13), {18, false}};
|
||||
constexpr OperandDesc OpDesc_CRM = OperandDesc{Mask(12, 19), {12, false}};
|
||||
constexpr OperandDesc OpDesc_D = OperandDesc{Mask(6, 10), {21, false}};
|
||||
constexpr OperandDesc OpDesc_FM = OperandDesc{Mask(7, 14), {17, false}};
|
||||
constexpr OperandDesc OpDesc_W1 = OperandDesc{Mask(16, 16), {15, false}};
|
||||
constexpr OperandDesc OpDesc_W2 = OperandDesc{Mask(21, 21), {10, false}};
|
||||
constexpr OperandDesc OpDesc_IMM = OperandDesc{Mask(16, 19), {12, false}};
|
||||
constexpr OperandDesc OpDesc_L = OperandDesc{Mask(10, 10), {21, false}};
|
||||
constexpr OperandDesc OpDesc_LI = OperandDesc{Mask(6, 29), {0, true}};
|
||||
constexpr OperandDesc OpDesc_MB = OperandDesc{Mask(21, 25), {6, false}};
|
||||
constexpr OperandDesc OpDesc_ME = OperandDesc{Mask(26, 30), {1, false}};
|
||||
constexpr OperandDesc OpDesc_NB = OperandDesc{Mask(16, 20), {11, false}};
|
||||
constexpr OperandDesc OpDesc_Offd = OperandDesc{Mask(16, 31), {0, true}};
|
||||
constexpr OperandDesc OpDesc_OffdPs = OperandDesc{Mask(20, 31), {0, true}};
|
||||
constexpr OperandDesc OpDesc_S = OperandDesc{Mask(6, 10), {21, false}};
|
||||
constexpr OperandDesc OpDesc_SH = OperandDesc{Mask(16, 20), {11, false}};
|
||||
constexpr OperandDesc OpDesc_SIMM = OperandDesc{Mask(16, 31), {0, true}};
|
||||
constexpr OperandDesc OpDesc_SPR = OperandDesc{Mask(11, 20), {11, false}};
|
||||
constexpr OperandDesc OpDesc_SR = OperandDesc{Mask(12, 15), {16, false}};
|
||||
constexpr OperandDesc OpDesc_TO = OperandDesc{Mask(6, 10), {21, false}};
|
||||
constexpr OperandDesc OpDesc_TPR = OperandDesc{Mask(11, 20), {11, false}};
|
||||
constexpr OperandDesc OpDesc_UIMM = OperandDesc{Mask(16, 31), {0, false}};
|
||||
constexpr OperandDesc OpDesc_I1 = OperandDesc{Mask(17, 19), {12, false}};
|
||||
constexpr OperandDesc OpDesc_I2 = OperandDesc{Mask(22, 24), {7, false}};
|
||||
} // namespace
|
||||
|
||||
void OperandList::Insert(size_t before, u32 val)
|
||||
|
@ -675,288 +675,293 @@ extern const CaseInsensitiveDict<ParseInfo, '.', '_', '+', '-'> extended_mnemoni
|
|||
// Defines all basic mnemonics that Broadway/Gekko supports
|
||||
extern const std::array<MnemonicDesc, NUM_MNEMONICS* VARIANT_PERMUTATIONS> mnemonics = {
|
||||
// A-2
|
||||
OERC_MNEMONIC(31, InsertVal(266, 22, 30), _D, _A, _B), // add
|
||||
OERC_MNEMONIC(31, InsertVal(10, 22, 30), _D, _A, _B), // addc
|
||||
OERC_MNEMONIC(31, InsertVal(138, 22, 30), _D, _A, _B), // adde
|
||||
BASIC_MNEMONIC(14, _D, _A, _SIMM), // addi
|
||||
BASIC_MNEMONIC(12, _D, _A, _SIMM), // addic
|
||||
BASIC_MNEMONIC(13, _D, _A, _SIMM), // addic.
|
||||
BASIC_MNEMONIC(15, _D, _A, _SIMM), // addis
|
||||
OERC_MNEMONIC(31, InsertVal(234, 22, 30), _D, _A), // addme
|
||||
OERC_MNEMONIC(31, InsertVal(202, 22, 30), _D, _A), // addze
|
||||
OERC_MNEMONIC(31, InsertVal(491, 22, 30), _D, _A, _B), // divw
|
||||
OERC_MNEMONIC(31, InsertVal(459, 22, 30), _D, _A, _B), // divwu
|
||||
RC_MNEMONIC(31, InsertVal(75, 22, 30), _D, _A, _B), // mulhw
|
||||
RC_MNEMONIC(31, InsertVal(11, 22, 30), _D, _A, _B), // mulhwu
|
||||
BASIC_MNEMONIC(7, _D, _A, _SIMM), // mulli
|
||||
OERC_MNEMONIC(31, InsertVal(235, 22, 30), _D, _A, _B), // mullw
|
||||
OERC_MNEMONIC(31, InsertVal(104, 22, 30), _D, _A), // neg
|
||||
OERC_MNEMONIC(31, InsertVal(40, 22, 30), _D, _A, _B), // subf
|
||||
OERC_MNEMONIC(31, InsertVal(8, 22, 30), _D, _A, _B), // subfc
|
||||
OERC_MNEMONIC(31, InsertVal(136, 22, 30), _D, _A, _B), // subfe
|
||||
BASIC_MNEMONIC(8, _D, _A, _SIMM), // subfic
|
||||
OERC_MNEMONIC(31, InsertVal(232, 22, 30), _D, _A), // subfme
|
||||
OERC_MNEMONIC(31, InsertVal(200, 22, 30), _D, _A), // subfze
|
||||
OERC_MNEMONIC(31, InsertVal(266, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // add
|
||||
OERC_MNEMONIC(31, InsertVal(10, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // addc
|
||||
OERC_MNEMONIC(31, InsertVal(138, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // adde
|
||||
BASIC_MNEMONIC(14, OpDesc_D, OpDesc_A, OpDesc_SIMM), // addi
|
||||
BASIC_MNEMONIC(12, OpDesc_D, OpDesc_A, OpDesc_SIMM), // addic
|
||||
BASIC_MNEMONIC(13, OpDesc_D, OpDesc_A, OpDesc_SIMM), // addic.
|
||||
BASIC_MNEMONIC(15, OpDesc_D, OpDesc_A, OpDesc_SIMM), // addis
|
||||
OERC_MNEMONIC(31, InsertVal(234, 22, 30), OpDesc_D, OpDesc_A), // addme
|
||||
OERC_MNEMONIC(31, InsertVal(202, 22, 30), OpDesc_D, OpDesc_A), // addze
|
||||
OERC_MNEMONIC(31, InsertVal(491, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // divw
|
||||
OERC_MNEMONIC(31, InsertVal(459, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // divwu
|
||||
RC_MNEMONIC(31, InsertVal(75, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // mulhw
|
||||
RC_MNEMONIC(31, InsertVal(11, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // mulhwu
|
||||
BASIC_MNEMONIC(7, OpDesc_D, OpDesc_A, OpDesc_SIMM), // mulli
|
||||
OERC_MNEMONIC(31, InsertVal(235, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // mullw
|
||||
OERC_MNEMONIC(31, InsertVal(104, 22, 30), OpDesc_D, OpDesc_A), // neg
|
||||
OERC_MNEMONIC(31, InsertVal(40, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // subf
|
||||
OERC_MNEMONIC(31, InsertVal(8, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // subfc
|
||||
OERC_MNEMONIC(31, InsertVal(136, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // subfe
|
||||
BASIC_MNEMONIC(8, OpDesc_D, OpDesc_A, OpDesc_SIMM), // subfic
|
||||
OERC_MNEMONIC(31, InsertVal(232, 22, 30), OpDesc_D, OpDesc_A), // subfme
|
||||
OERC_MNEMONIC(31, InsertVal(200, 22, 30), OpDesc_D, OpDesc_A), // subfze
|
||||
|
||||
// A-3
|
||||
MNEMONIC(31, InsertVal(0, 21, 30), _Crfd, _L, _A, _B), // cmp
|
||||
BASIC_MNEMONIC(11, _Crfd, _L, _A, _SIMM), // cmpi
|
||||
MNEMONIC(31, InsertVal(32, 21, 30), _Crfd, _L, _A, _B), // cmpl
|
||||
BASIC_MNEMONIC(10, _Crfd, _L, _A, _UIMM), // cmpli
|
||||
MNEMONIC(31, InsertVal(0, 21, 30), OpDesc_Crfd, OpDesc_L, OpDesc_A, OpDesc_B), // cmp
|
||||
BASIC_MNEMONIC(11, OpDesc_Crfd, OpDesc_L, OpDesc_A, OpDesc_SIMM), // cmpi
|
||||
MNEMONIC(31, InsertVal(32, 21, 30), OpDesc_Crfd, OpDesc_L, OpDesc_A, OpDesc_B), // cmpl
|
||||
BASIC_MNEMONIC(10, OpDesc_Crfd, OpDesc_L, OpDesc_A, OpDesc_UIMM), // cmpli
|
||||
|
||||
// A-4
|
||||
RC_MNEMONIC(31, InsertVal(28, 21, 30), _A, _S, _B), // and
|
||||
RC_MNEMONIC(31, InsertVal(60, 21, 30), _A, _S, _B), // andc
|
||||
BASIC_MNEMONIC(28, _A, _S, _UIMM), // andi.
|
||||
BASIC_MNEMONIC(29, _A, _S, _UIMM), // andis.
|
||||
RC_MNEMONIC(31, InsertVal(26, 21, 30), _A, _S), // cntlzw
|
||||
RC_MNEMONIC(31, InsertVal(284, 21, 30), _A, _S, _B), // eqv
|
||||
RC_MNEMONIC(31, InsertVal(954, 21, 30), _A, _S), // extsb
|
||||
RC_MNEMONIC(31, InsertVal(922, 21, 30), _A, _S), // extsh
|
||||
RC_MNEMONIC(31, InsertVal(476, 21, 30), _A, _S, _B), // nand
|
||||
RC_MNEMONIC(31, InsertVal(124, 21, 30), _A, _S, _B), // nor
|
||||
RC_MNEMONIC(31, InsertVal(444, 21, 30), _A, _S, _B), // or
|
||||
RC_MNEMONIC(31, InsertVal(412, 21, 30), _A, _S, _B), // orc
|
||||
BASIC_MNEMONIC(24, _A, _S, _UIMM), // ori
|
||||
BASIC_MNEMONIC(25, _A, _S, _UIMM), // oris
|
||||
RC_MNEMONIC(31, InsertVal(316, 21, 30), _A, _S, _B), // xor
|
||||
BASIC_MNEMONIC(26, _A, _S, _UIMM), // xori
|
||||
BASIC_MNEMONIC(27, _A, _S, _UIMM), // xoris
|
||||
RC_MNEMONIC(31, InsertVal(28, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // and
|
||||
RC_MNEMONIC(31, InsertVal(60, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // andc
|
||||
BASIC_MNEMONIC(28, OpDesc_A, OpDesc_S, OpDesc_UIMM), // andi.
|
||||
BASIC_MNEMONIC(29, OpDesc_A, OpDesc_S, OpDesc_UIMM), // andis.
|
||||
RC_MNEMONIC(31, InsertVal(26, 21, 30), OpDesc_A, OpDesc_S), // cntlzw
|
||||
RC_MNEMONIC(31, InsertVal(284, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // eqv
|
||||
RC_MNEMONIC(31, InsertVal(954, 21, 30), OpDesc_A, OpDesc_S), // extsb
|
||||
RC_MNEMONIC(31, InsertVal(922, 21, 30), OpDesc_A, OpDesc_S), // extsh
|
||||
RC_MNEMONIC(31, InsertVal(476, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // nand
|
||||
RC_MNEMONIC(31, InsertVal(124, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // nor
|
||||
RC_MNEMONIC(31, InsertVal(444, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // or
|
||||
RC_MNEMONIC(31, InsertVal(412, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // orc
|
||||
BASIC_MNEMONIC(24, OpDesc_A, OpDesc_S, OpDesc_UIMM), // ori
|
||||
BASIC_MNEMONIC(25, OpDesc_A, OpDesc_S, OpDesc_UIMM), // oris
|
||||
RC_MNEMONIC(31, InsertVal(316, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // xor
|
||||
BASIC_MNEMONIC(26, OpDesc_A, OpDesc_S, OpDesc_UIMM), // xori
|
||||
BASIC_MNEMONIC(27, OpDesc_A, OpDesc_S, OpDesc_UIMM), // xoris
|
||||
|
||||
// A-5
|
||||
RC_MNEMONIC(20, 0, _A, _S, _SH, _MB, _ME), // rlwimi
|
||||
RC_MNEMONIC(21, 0, _A, _S, _SH, _MB, _ME), // rlwinm
|
||||
RC_MNEMONIC(23, 0, _A, _S, _B, _MB, _ME), // rlwnm
|
||||
RC_MNEMONIC(20, 0, OpDesc_A, OpDesc_S, OpDesc_SH, OpDesc_MB, OpDesc_ME), // rlwimi
|
||||
RC_MNEMONIC(21, 0, OpDesc_A, OpDesc_S, OpDesc_SH, OpDesc_MB, OpDesc_ME), // rlwinm
|
||||
RC_MNEMONIC(23, 0, OpDesc_A, OpDesc_S, OpDesc_B, OpDesc_MB, OpDesc_ME), // rlwnm
|
||||
|
||||
// A-6
|
||||
RC_MNEMONIC(31, InsertVal(24, 21, 30), _A, _S, _B), // slw
|
||||
RC_MNEMONIC(31, InsertVal(792, 21, 30), _A, _S, _B), // sraw
|
||||
RC_MNEMONIC(31, InsertVal(824, 21, 30), _A, _S, _SH), // srawi
|
||||
RC_MNEMONIC(31, InsertVal(536, 21, 30), _A, _S, _B), // srw
|
||||
RC_MNEMONIC(31, InsertVal(24, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // slw
|
||||
RC_MNEMONIC(31, InsertVal(792, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // sraw
|
||||
RC_MNEMONIC(31, InsertVal(824, 21, 30), OpDesc_A, OpDesc_S, OpDesc_SH), // srawi
|
||||
RC_MNEMONIC(31, InsertVal(536, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // srw
|
||||
|
||||
// A-7
|
||||
RC_MNEMONIC(63, InsertVal(21, 26, 30), _D, _A, _B), // fadd
|
||||
RC_MNEMONIC(59, InsertVal(21, 26, 30), _D, _A, _B), // fadds
|
||||
RC_MNEMONIC(63, InsertVal(18, 26, 30), _D, _A, _B), // fdiv
|
||||
RC_MNEMONIC(59, InsertVal(18, 26, 30), _D, _A, _B), // fdivs
|
||||
RC_MNEMONIC(63, InsertVal(25, 26, 30), _D, _A, _C), // fmul
|
||||
RC_MNEMONIC(59, InsertVal(25, 26, 30), _D, _A, _C), // fmuls
|
||||
RC_MNEMONIC(59, InsertVal(24, 26, 30), _D, _B), // fres
|
||||
RC_MNEMONIC(63, InsertVal(26, 26, 30), _D, _B), // frsqrte
|
||||
RC_MNEMONIC(63, InsertVal(20, 26, 30), _D, _A, _B), // fsub
|
||||
RC_MNEMONIC(59, InsertVal(20, 26, 30), _D, _A, _B), // fsubs
|
||||
RC_MNEMONIC(63, InsertVal(23, 26, 30), _D, _A, _C, _B), // fsel
|
||||
RC_MNEMONIC(63, InsertVal(21, 26, 30), OpDesc_D, OpDesc_A, OpDesc_B), // fadd
|
||||
RC_MNEMONIC(59, InsertVal(21, 26, 30), OpDesc_D, OpDesc_A, OpDesc_B), // fadds
|
||||
RC_MNEMONIC(63, InsertVal(18, 26, 30), OpDesc_D, OpDesc_A, OpDesc_B), // fdiv
|
||||
RC_MNEMONIC(59, InsertVal(18, 26, 30), OpDesc_D, OpDesc_A, OpDesc_B), // fdivs
|
||||
RC_MNEMONIC(63, InsertVal(25, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C), // fmul
|
||||
RC_MNEMONIC(59, InsertVal(25, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C), // fmuls
|
||||
RC_MNEMONIC(59, InsertVal(24, 26, 30), OpDesc_D, OpDesc_B), // fres
|
||||
RC_MNEMONIC(63, InsertVal(26, 26, 30), OpDesc_D, OpDesc_B), // frsqrte
|
||||
RC_MNEMONIC(63, InsertVal(20, 26, 30), OpDesc_D, OpDesc_A, OpDesc_B), // fsub
|
||||
RC_MNEMONIC(59, InsertVal(20, 26, 30), OpDesc_D, OpDesc_A, OpDesc_B), // fsubs
|
||||
RC_MNEMONIC(63, InsertVal(23, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // fsel
|
||||
|
||||
// A-8
|
||||
RC_MNEMONIC(63, InsertVal(29, 26, 30), _D, _A, _C, _B), // fmadd
|
||||
RC_MNEMONIC(59, InsertVal(29, 26, 30), _D, _A, _C, _B), // fmadds
|
||||
RC_MNEMONIC(63, InsertVal(28, 26, 30), _D, _A, _C, _B), // fmsub
|
||||
RC_MNEMONIC(59, InsertVal(28, 26, 30), _D, _A, _C, _B), // fmsubs
|
||||
RC_MNEMONIC(63, InsertVal(31, 26, 30), _D, _A, _C, _B), // fnmadd
|
||||
RC_MNEMONIC(59, InsertVal(31, 26, 30), _D, _A, _C, _B), // fnmadds
|
||||
RC_MNEMONIC(63, InsertVal(30, 26, 30), _D, _A, _C, _B), // fnmsub
|
||||
RC_MNEMONIC(59, InsertVal(30, 26, 30), _D, _A, _C, _B), // fnmsubs
|
||||
RC_MNEMONIC(63, InsertVal(29, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // fmadd
|
||||
RC_MNEMONIC(59, InsertVal(29, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // fmadds
|
||||
RC_MNEMONIC(63, InsertVal(28, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // fmsub
|
||||
RC_MNEMONIC(59, InsertVal(28, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // fmsubs
|
||||
RC_MNEMONIC(63, InsertVal(31, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // fnmadd
|
||||
RC_MNEMONIC(59, InsertVal(31, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // fnmadds
|
||||
RC_MNEMONIC(63, InsertVal(30, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // fnmsub
|
||||
RC_MNEMONIC(59, InsertVal(30, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // fnmsubs
|
||||
|
||||
// A-9
|
||||
RC_MNEMONIC(63, InsertVal(14, 21, 30), _D, _B), // fctiw
|
||||
RC_MNEMONIC(63, InsertVal(15, 21, 30), _D, _B), // fctiwz
|
||||
RC_MNEMONIC(63, InsertVal(12, 21, 30), _D, _B), // frsp
|
||||
RC_MNEMONIC(63, InsertVal(14, 21, 30), OpDesc_D, OpDesc_B), // fctiw
|
||||
RC_MNEMONIC(63, InsertVal(15, 21, 30), OpDesc_D, OpDesc_B), // fctiwz
|
||||
RC_MNEMONIC(63, InsertVal(12, 21, 30), OpDesc_D, OpDesc_B), // frsp
|
||||
|
||||
// A-10
|
||||
MNEMONIC(63, InsertVal(32, 21, 30), _Crfd, _A, _B), // fcmpo
|
||||
MNEMONIC(63, InsertVal(0, 21, 30), _Crfd, _A, _B), // fcmpu
|
||||
MNEMONIC(63, InsertVal(32, 21, 30), OpDesc_Crfd, OpDesc_A, OpDesc_B), // fcmpo
|
||||
MNEMONIC(63, InsertVal(0, 21, 30), OpDesc_Crfd, OpDesc_A, OpDesc_B), // fcmpu
|
||||
|
||||
// A-11
|
||||
MNEMONIC(63, InsertVal(64, 21, 30), _Crfd, _Crfs), // mcrfs
|
||||
RC_MNEMONIC(63, InsertVal(583, 21, 30), _D), // mffs
|
||||
RC_MNEMONIC(63, InsertVal(70, 21, 30), _Crbd), // mtfsb0
|
||||
RC_MNEMONIC(63, InsertVal(38, 21, 30), _Crbd), // mtfsb1
|
||||
RC_MNEMONIC(63, InsertVal(711, 21, 30), _FM, _B), // mtfsf
|
||||
RC_MNEMONIC(63, InsertVal(134, 21, 30), _Crfd, _IMM), // mtfsfi
|
||||
MNEMONIC(63, InsertVal(64, 21, 30), OpDesc_Crfd, OpDesc_Crfs), // mcrfs
|
||||
RC_MNEMONIC(63, InsertVal(583, 21, 30), OpDesc_D), // mffs
|
||||
RC_MNEMONIC(63, InsertVal(70, 21, 30), OpDesc_Crbd), // mtfsb0
|
||||
RC_MNEMONIC(63, InsertVal(38, 21, 30), OpDesc_Crbd), // mtfsb1
|
||||
RC_MNEMONIC(63, InsertVal(711, 21, 30), OpDesc_FM, OpDesc_B), // mtfsf
|
||||
RC_MNEMONIC(63, InsertVal(134, 21, 30), OpDesc_Crfd, OpDesc_IMM), // mtfsfi
|
||||
|
||||
// A-12
|
||||
BASIC_MNEMONIC(34, _D, _Offd, _A), // lbz
|
||||
BASIC_MNEMONIC(35, _D, _Offd, _A), // lbzu
|
||||
MNEMONIC(31, InsertVal(119, 21, 30), _D, _A, _B), // lbzux
|
||||
MNEMONIC(31, InsertVal(87, 21, 30), _D, _A, _B), // lbzx
|
||||
BASIC_MNEMONIC(42, _D, _Offd, _A), // lha
|
||||
BASIC_MNEMONIC(43, _D, _Offd, _A), // lhau
|
||||
MNEMONIC(31, InsertVal(375, 21, 30), _D, _A, _B), // lhaux
|
||||
MNEMONIC(31, InsertVal(343, 21, 30), _D, _A, _B), // lhax
|
||||
BASIC_MNEMONIC(40, _D, _Offd, _A), // lhz
|
||||
BASIC_MNEMONIC(41, _D, _Offd, _A), // lhzu
|
||||
MNEMONIC(31, InsertVal(311, 21, 30), _D, _A, _B), // lhzux
|
||||
MNEMONIC(31, InsertVal(279, 21, 30), _D, _A, _B), // lhzx
|
||||
BASIC_MNEMONIC(32, _D, _Offd, _A), // lwz
|
||||
BASIC_MNEMONIC(33, _D, _Offd, _A), // lwzu
|
||||
MNEMONIC(31, InsertVal(55, 21, 30), _D, _A, _B), // lwzux
|
||||
MNEMONIC(31, InsertVal(23, 21, 30), _D, _A, _B), // lwzx
|
||||
BASIC_MNEMONIC(34, OpDesc_D, OpDesc_Offd, OpDesc_A), // lbz
|
||||
BASIC_MNEMONIC(35, OpDesc_D, OpDesc_Offd, OpDesc_A), // lbzu
|
||||
MNEMONIC(31, InsertVal(119, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lbzux
|
||||
MNEMONIC(31, InsertVal(87, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lbzx
|
||||
BASIC_MNEMONIC(42, OpDesc_D, OpDesc_Offd, OpDesc_A), // lha
|
||||
BASIC_MNEMONIC(43, OpDesc_D, OpDesc_Offd, OpDesc_A), // lhau
|
||||
MNEMONIC(31, InsertVal(375, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lhaux
|
||||
MNEMONIC(31, InsertVal(343, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lhax
|
||||
BASIC_MNEMONIC(40, OpDesc_D, OpDesc_Offd, OpDesc_A), // lhz
|
||||
BASIC_MNEMONIC(41, OpDesc_D, OpDesc_Offd, OpDesc_A), // lhzu
|
||||
MNEMONIC(31, InsertVal(311, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lhzux
|
||||
MNEMONIC(31, InsertVal(279, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lhzx
|
||||
BASIC_MNEMONIC(32, OpDesc_D, OpDesc_Offd, OpDesc_A), // lwz
|
||||
BASIC_MNEMONIC(33, OpDesc_D, OpDesc_Offd, OpDesc_A), // lwzu
|
||||
MNEMONIC(31, InsertVal(55, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lwzux
|
||||
MNEMONIC(31, InsertVal(23, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lwzx
|
||||
|
||||
// A-13
|
||||
BASIC_MNEMONIC(38, _S, _Offd, _A), // stb
|
||||
BASIC_MNEMONIC(39, _S, _Offd, _A), // stbu
|
||||
MNEMONIC(31, InsertVal(247, 21, 30), _S, _A, _B), // stbux
|
||||
MNEMONIC(31, InsertVal(215, 21, 30), _S, _A, _B), // stbx
|
||||
BASIC_MNEMONIC(44, _S, _Offd, _A), // sth
|
||||
BASIC_MNEMONIC(45, _S, _Offd, _A), // sthu
|
||||
MNEMONIC(31, InsertVal(439, 21, 30), _S, _A, _B), // sthux
|
||||
MNEMONIC(31, InsertVal(407, 21, 30), _S, _A, _B), // sthx
|
||||
BASIC_MNEMONIC(36, _S, _Offd, _A), // stw
|
||||
BASIC_MNEMONIC(37, _S, _Offd, _A), // stwu
|
||||
MNEMONIC(31, InsertVal(183, 21, 30), _S, _A, _B), // stwux
|
||||
MNEMONIC(31, InsertVal(151, 21, 30), _S, _A, _B), // stwx
|
||||
BASIC_MNEMONIC(38, OpDesc_S, OpDesc_Offd, OpDesc_A), // stb
|
||||
BASIC_MNEMONIC(39, OpDesc_S, OpDesc_Offd, OpDesc_A), // stbu
|
||||
MNEMONIC(31, InsertVal(247, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stbux
|
||||
MNEMONIC(31, InsertVal(215, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stbx
|
||||
BASIC_MNEMONIC(44, OpDesc_S, OpDesc_Offd, OpDesc_A), // sth
|
||||
BASIC_MNEMONIC(45, OpDesc_S, OpDesc_Offd, OpDesc_A), // sthu
|
||||
MNEMONIC(31, InsertVal(439, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // sthux
|
||||
MNEMONIC(31, InsertVal(407, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // sthx
|
||||
BASIC_MNEMONIC(36, OpDesc_S, OpDesc_Offd, OpDesc_A), // stw
|
||||
BASIC_MNEMONIC(37, OpDesc_S, OpDesc_Offd, OpDesc_A), // stwu
|
||||
MNEMONIC(31, InsertVal(183, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stwux
|
||||
MNEMONIC(31, InsertVal(151, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stwx
|
||||
|
||||
// A-14
|
||||
MNEMONIC(31, InsertVal(790, 21, 30), _D, _A, _B), // lhbrx
|
||||
MNEMONIC(31, InsertVal(534, 21, 30), _D, _A, _B), // lwbrx
|
||||
MNEMONIC(31, InsertVal(918, 21, 30), _S, _A, _B), // sthbrx
|
||||
MNEMONIC(31, InsertVal(662, 21, 30), _S, _A, _B), // stwbrx
|
||||
MNEMONIC(31, InsertVal(790, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lhbrx
|
||||
MNEMONIC(31, InsertVal(534, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lwbrx
|
||||
MNEMONIC(31, InsertVal(918, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // sthbrx
|
||||
MNEMONIC(31, InsertVal(662, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stwbrx
|
||||
|
||||
// A-15
|
||||
BASIC_MNEMONIC(46, _D, _Offd, _A), // lmw
|
||||
BASIC_MNEMONIC(47, _S, _Offd, _A), // stmw
|
||||
BASIC_MNEMONIC(46, OpDesc_D, OpDesc_Offd, OpDesc_A), // lmw
|
||||
BASIC_MNEMONIC(47, OpDesc_S, OpDesc_Offd, OpDesc_A), // stmw
|
||||
|
||||
// A-16
|
||||
MNEMONIC(31, InsertVal(597, 21, 30), _D, _A, _NB), // lswi
|
||||
MNEMONIC(31, InsertVal(533, 21, 30), _D, _A, _B), // lswx
|
||||
MNEMONIC(31, InsertVal(725, 21, 30), _S, _A, _NB), // stswi
|
||||
MNEMONIC(31, InsertVal(661, 21, 30), _S, _A, _B), // stswx
|
||||
MNEMONIC(31, InsertVal(597, 21, 30), OpDesc_D, OpDesc_A, OpDesc_NB), // lswi
|
||||
MNEMONIC(31, InsertVal(533, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lswx
|
||||
MNEMONIC(31, InsertVal(725, 21, 30), OpDesc_S, OpDesc_A, OpDesc_NB), // stswi
|
||||
MNEMONIC(31, InsertVal(661, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stswx
|
||||
|
||||
// A-17
|
||||
MNEMONIC(31, InsertVal(854, 21, 30)), // eieio
|
||||
MNEMONIC(19, InsertVal(150, 21, 30)), // isync
|
||||
MNEMONIC(31, InsertVal(20, 21, 30), _D, _A, _B), // lwarx
|
||||
MNEMONIC(31, InsertVal(150, 21, 30) | InsertVal(1, 31, 31), _S, _A, _B), // stwcx.
|
||||
MNEMONIC(31, InsertVal(598, 21, 30)), // sync
|
||||
MNEMONIC(31, InsertVal(854, 21, 30)), // eieio
|
||||
MNEMONIC(19, InsertVal(150, 21, 30)), // isync
|
||||
MNEMONIC(31, InsertVal(20, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lwarx
|
||||
MNEMONIC(31, InsertVal(150, 21, 30) | InsertVal(1, 31, 31), OpDesc_S, OpDesc_A,
|
||||
OpDesc_B), // stwcx.
|
||||
MNEMONIC(31, InsertVal(598, 21, 30)), // sync
|
||||
|
||||
// A-18
|
||||
BASIC_MNEMONIC(50, _D, _Offd, _A), // lfd
|
||||
BASIC_MNEMONIC(51, _D, _Offd, _A), // lfdu
|
||||
MNEMONIC(31, InsertVal(631, 21, 30), _D, _A, _B), // lfdux
|
||||
MNEMONIC(31, InsertVal(599, 21, 30), _D, _A, _B), // lfdx
|
||||
BASIC_MNEMONIC(48, _D, _Offd, _A), // lfs
|
||||
BASIC_MNEMONIC(49, _D, _Offd, _A), // lfsu
|
||||
MNEMONIC(31, InsertVal(567, 21, 30), _D, _A, _B), // lfsux
|
||||
MNEMONIC(31, InsertVal(535, 21, 30), _D, _A, _B), // lfsx
|
||||
BASIC_MNEMONIC(50, OpDesc_D, OpDesc_Offd, OpDesc_A), // lfd
|
||||
BASIC_MNEMONIC(51, OpDesc_D, OpDesc_Offd, OpDesc_A), // lfdu
|
||||
MNEMONIC(31, InsertVal(631, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lfdux
|
||||
MNEMONIC(31, InsertVal(599, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lfdx
|
||||
BASIC_MNEMONIC(48, OpDesc_D, OpDesc_Offd, OpDesc_A), // lfs
|
||||
BASIC_MNEMONIC(49, OpDesc_D, OpDesc_Offd, OpDesc_A), // lfsu
|
||||
MNEMONIC(31, InsertVal(567, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lfsux
|
||||
MNEMONIC(31, InsertVal(535, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lfsx
|
||||
|
||||
// A-19
|
||||
BASIC_MNEMONIC(54, _S, _Offd, _A), // stfd
|
||||
BASIC_MNEMONIC(55, _S, _Offd, _A), // stfdu
|
||||
MNEMONIC(31, InsertVal(759, 21, 30), _S, _A, _B), // stfdux
|
||||
MNEMONIC(31, InsertVal(727, 21, 30), _S, _A, _B), // stfdx
|
||||
MNEMONIC(31, InsertVal(983, 21, 30), _S, _A, _B), // stfiwx
|
||||
BASIC_MNEMONIC(52, _S, _Offd, _A), // stfs
|
||||
BASIC_MNEMONIC(53, _S, _Offd, _A), // stfsu
|
||||
MNEMONIC(31, InsertVal(695, 21, 30), _S, _A, _B), // stfsux
|
||||
MNEMONIC(31, InsertVal(663, 21, 30), _S, _A, _B), // stfsx
|
||||
BASIC_MNEMONIC(54, OpDesc_S, OpDesc_Offd, OpDesc_A), // stfd
|
||||
BASIC_MNEMONIC(55, OpDesc_S, OpDesc_Offd, OpDesc_A), // stfdu
|
||||
MNEMONIC(31, InsertVal(759, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stfdux
|
||||
MNEMONIC(31, InsertVal(727, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stfdx
|
||||
MNEMONIC(31, InsertVal(983, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stfiwx
|
||||
BASIC_MNEMONIC(52, OpDesc_S, OpDesc_Offd, OpDesc_A), // stfs
|
||||
BASIC_MNEMONIC(53, OpDesc_S, OpDesc_Offd, OpDesc_A), // stfsu
|
||||
MNEMONIC(31, InsertVal(695, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stfsux
|
||||
MNEMONIC(31, InsertVal(663, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stfsx
|
||||
|
||||
// A-20
|
||||
RC_MNEMONIC(63, InsertVal(264, 21, 30), _D, _B), // fabs
|
||||
RC_MNEMONIC(63, InsertVal(72, 21, 30), _D, _B), // fmr
|
||||
RC_MNEMONIC(63, InsertVal(136, 21, 30), _D, _B), // fnabs
|
||||
RC_MNEMONIC(63, InsertVal(40, 21, 30), _D, _B), // fneg
|
||||
RC_MNEMONIC(63, InsertVal(264, 21, 30), OpDesc_D, OpDesc_B), // fabs
|
||||
RC_MNEMONIC(63, InsertVal(72, 21, 30), OpDesc_D, OpDesc_B), // fmr
|
||||
RC_MNEMONIC(63, InsertVal(136, 21, 30), OpDesc_D, OpDesc_B), // fnabs
|
||||
RC_MNEMONIC(63, InsertVal(40, 21, 30), OpDesc_D, OpDesc_B), // fneg
|
||||
|
||||
// A-21
|
||||
AALK_MNEMONIC(18, 0, _LI), // b
|
||||
AALK_MNEMONIC(16, 0, _BO, _BI, _BD), // bc
|
||||
LK_MNEMONIC(19, InsertVal(528, 21, 30), _BO, _BI), // bcctr
|
||||
LK_MNEMONIC(19, InsertVal(16, 21, 30), _BO, _BI), // bclr
|
||||
AALK_MNEMONIC(18, 0, OpDesc_LI), // b
|
||||
AALK_MNEMONIC(16, 0, OpDesc_BO, OpDesc_BI, OpDesc_BD), // bc
|
||||
LK_MNEMONIC(19, InsertVal(528, 21, 30), OpDesc_BO, OpDesc_BI), // bcctr
|
||||
LK_MNEMONIC(19, InsertVal(16, 21, 30), OpDesc_BO, OpDesc_BI), // bclr
|
||||
|
||||
// A-22
|
||||
MNEMONIC(19, InsertVal(257, 21, 30), _Crbd, _Crba, _Crbb), // crand
|
||||
MNEMONIC(19, InsertVal(129, 21, 30), _Crbd, _Crba, _Crbb), // crandc
|
||||
MNEMONIC(19, InsertVal(289, 21, 30), _Crbd, _Crba, _Crbb), // creqv
|
||||
MNEMONIC(19, InsertVal(225, 21, 30), _Crbd, _Crba, _Crbb), // crnand
|
||||
MNEMONIC(19, InsertVal(33, 21, 30), _Crbd, _Crba, _Crbb), // crnor
|
||||
MNEMONIC(19, InsertVal(449, 21, 30), _Crbd, _Crba, _Crbb), // cror
|
||||
MNEMONIC(19, InsertVal(417, 21, 30), _Crbd, _Crba, _Crbb), // crorc
|
||||
MNEMONIC(19, InsertVal(193, 21, 30), _Crbd, _Crba, _Crbb), // crxor
|
||||
MNEMONIC(19, InsertVal(0, 21, 30), _Crfd, _Crfs), // mcrf
|
||||
MNEMONIC(19, InsertVal(257, 21, 30), OpDesc_Crbd, OpDesc_Crba, OpDesc_Crbb), // crand
|
||||
MNEMONIC(19, InsertVal(129, 21, 30), OpDesc_Crbd, OpDesc_Crba, OpDesc_Crbb), // crandc
|
||||
MNEMONIC(19, InsertVal(289, 21, 30), OpDesc_Crbd, OpDesc_Crba, OpDesc_Crbb), // creqv
|
||||
MNEMONIC(19, InsertVal(225, 21, 30), OpDesc_Crbd, OpDesc_Crba, OpDesc_Crbb), // crnand
|
||||
MNEMONIC(19, InsertVal(33, 21, 30), OpDesc_Crbd, OpDesc_Crba, OpDesc_Crbb), // crnor
|
||||
MNEMONIC(19, InsertVal(449, 21, 30), OpDesc_Crbd, OpDesc_Crba, OpDesc_Crbb), // cror
|
||||
MNEMONIC(19, InsertVal(417, 21, 30), OpDesc_Crbd, OpDesc_Crba, OpDesc_Crbb), // crorc
|
||||
MNEMONIC(19, InsertVal(193, 21, 30), OpDesc_Crbd, OpDesc_Crba, OpDesc_Crbb), // crxor
|
||||
MNEMONIC(19, InsertVal(0, 21, 30), OpDesc_Crfd, OpDesc_Crfs), // mcrf
|
||||
|
||||
// A-23
|
||||
MNEMONIC(19, InsertVal(50, 21, 30)), // rfi
|
||||
MNEMONIC(17, InsertVal(1, 30, 30)), // sc
|
||||
|
||||
// A-24
|
||||
MNEMONIC(31, InsertVal(4, 21, 30), _TO, _A, _B), // tw
|
||||
BASIC_MNEMONIC(3, _TO, _A, _SIMM), // twi
|
||||
MNEMONIC(31, InsertVal(4, 21, 30), OpDesc_TO, OpDesc_A, OpDesc_B), // tw
|
||||
BASIC_MNEMONIC(3, OpDesc_TO, OpDesc_A, OpDesc_SIMM), // twi
|
||||
|
||||
// A-25
|
||||
MNEMONIC(31, InsertVal(512, 21, 30), _Crfd), // mcrxr
|
||||
MNEMONIC(31, InsertVal(19, 21, 30), _D), // mfcr
|
||||
MNEMONIC(31, InsertVal(83, 21, 30), _D), // mfmsr
|
||||
MNEMONIC(31, InsertVal(339, 21, 30), _D, _SPR), // mfspr
|
||||
MNEMONIC(31, InsertVal(371, 21, 30), _D, _TPR), // mftb
|
||||
MNEMONIC(31, InsertVal(144, 21, 30), _CRM, _S), // mtcrf
|
||||
MNEMONIC(31, InsertVal(146, 21, 30), _S), // mtmsr
|
||||
MNEMONIC(31, InsertVal(467, 21, 30), _SPR, _D), // mtspr
|
||||
MNEMONIC(31, InsertVal(512, 21, 30), OpDesc_Crfd), // mcrxr
|
||||
MNEMONIC(31, InsertVal(19, 21, 30), OpDesc_D), // mfcr
|
||||
MNEMONIC(31, InsertVal(83, 21, 30), OpDesc_D), // mfmsr
|
||||
MNEMONIC(31, InsertVal(339, 21, 30), OpDesc_D, OpDesc_SPR), // mfspr
|
||||
MNEMONIC(31, InsertVal(371, 21, 30), OpDesc_D, OpDesc_TPR), // mftb
|
||||
MNEMONIC(31, InsertVal(144, 21, 30), OpDesc_CRM, OpDesc_S), // mtcrf
|
||||
MNEMONIC(31, InsertVal(146, 21, 30), OpDesc_S), // mtmsr
|
||||
MNEMONIC(31, InsertVal(467, 21, 30), OpDesc_SPR, OpDesc_D), // mtspr
|
||||
|
||||
// A-26
|
||||
MNEMONIC(31, InsertVal(86, 21, 30), _A, _B), // dcbf
|
||||
MNEMONIC(31, InsertVal(470, 21, 30), _A, _B), // dcbi
|
||||
MNEMONIC(31, InsertVal(54, 21, 30), _A, _B), // dcbst
|
||||
MNEMONIC(31, InsertVal(278, 21, 30), _A, _B), // dcbt
|
||||
MNEMONIC(31, InsertVal(246, 21, 30), _A, _B), // dcbtst
|
||||
MNEMONIC(31, InsertVal(1014, 21, 30), _A, _B), // dcbz
|
||||
MNEMONIC(31, InsertVal(982, 21, 30), _A, _B), // icbi
|
||||
MNEMONIC(31, InsertVal(86, 21, 30), OpDesc_A, OpDesc_B), // dcbf
|
||||
MNEMONIC(31, InsertVal(470, 21, 30), OpDesc_A, OpDesc_B), // dcbi
|
||||
MNEMONIC(31, InsertVal(54, 21, 30), OpDesc_A, OpDesc_B), // dcbst
|
||||
MNEMONIC(31, InsertVal(278, 21, 30), OpDesc_A, OpDesc_B), // dcbt
|
||||
MNEMONIC(31, InsertVal(246, 21, 30), OpDesc_A, OpDesc_B), // dcbtst
|
||||
MNEMONIC(31, InsertVal(1014, 21, 30), OpDesc_A, OpDesc_B), // dcbz
|
||||
MNEMONIC(31, InsertVal(982, 21, 30), OpDesc_A, OpDesc_B), // icbi
|
||||
|
||||
// A-27
|
||||
MNEMONIC(31, InsertVal(595, 21, 30), _D, _SR), // mfsr
|
||||
MNEMONIC(31, InsertVal(659, 21, 30), _D, _B), // mfsrin
|
||||
MNEMONIC(31, InsertVal(210, 21, 30), _SR, _S), // mtsr
|
||||
MNEMONIC(31, InsertVal(242, 21, 30), _S, _B), // mtsrin
|
||||
MNEMONIC(31, InsertVal(595, 21, 30), OpDesc_D, OpDesc_SR), // mfsr
|
||||
MNEMONIC(31, InsertVal(659, 21, 30), OpDesc_D, OpDesc_B), // mfsrin
|
||||
MNEMONIC(31, InsertVal(210, 21, 30), OpDesc_SR, OpDesc_S), // mtsr
|
||||
MNEMONIC(31, InsertVal(242, 21, 30), OpDesc_S, OpDesc_B), // mtsrin
|
||||
|
||||
// A-28
|
||||
MNEMONIC(31, InsertVal(306, 21, 30), _B), // tlbie
|
||||
MNEMONIC(31, InsertVal(566, 21, 30)), // tlbsync
|
||||
MNEMONIC(31, InsertVal(306, 21, 30), OpDesc_B), // tlbie
|
||||
MNEMONIC(31, InsertVal(566, 21, 30)), // tlbsync
|
||||
|
||||
// A-29
|
||||
MNEMONIC(31, InsertVal(310, 21, 30), _D, _A, _B), // eciwx
|
||||
MNEMONIC(31, InsertVal(438, 21, 30), _S, _A, _B), // ecowx
|
||||
MNEMONIC(31, InsertVal(310, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // eciwx
|
||||
MNEMONIC(31, InsertVal(438, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // ecowx
|
||||
|
||||
// A-30
|
||||
MNEMONIC(4, InsertVal(6, 25, 30), _D, _A, _B, _W2, _I2), // psq_lx
|
||||
MNEMONIC(4, InsertVal(7, 25, 30), _S, _A, _B, _W2, _I2), // psq_stx
|
||||
MNEMONIC(4, InsertVal(38, 25, 30), _D, _A, _B, _W2, _I2), // psq_lux
|
||||
MNEMONIC(4, InsertVal(39, 25, 30), _S, _A, _B, _W2, _I2), // psq_stux
|
||||
BASIC_MNEMONIC(56, _D, _OffdPs, _A, _W1, _I1), // psq_l
|
||||
BASIC_MNEMONIC(57, _D, _OffdPs, _A, _W1, _I1), // psq_lu
|
||||
BASIC_MNEMONIC(60, _S, _OffdPs, _A, _W1, _I1), // psq_st
|
||||
BASIC_MNEMONIC(61, _S, _OffdPs, _A, _W1, _I1), // psq_stu
|
||||
MNEMONIC(4, InsertVal(6, 25, 30), OpDesc_D, OpDesc_A, OpDesc_B, OpDesc_W2,
|
||||
OpDesc_I2), // psq_lx
|
||||
MNEMONIC(4, InsertVal(7, 25, 30), OpDesc_S, OpDesc_A, OpDesc_B, OpDesc_W2,
|
||||
OpDesc_I2), // psq_stx
|
||||
MNEMONIC(4, InsertVal(38, 25, 30), OpDesc_D, OpDesc_A, OpDesc_B, OpDesc_W2,
|
||||
OpDesc_I2), // psq_lux
|
||||
MNEMONIC(4, InsertVal(39, 25, 30), OpDesc_S, OpDesc_A, OpDesc_B, OpDesc_W2,
|
||||
OpDesc_I2), // psq_stux
|
||||
BASIC_MNEMONIC(56, OpDesc_D, OpDesc_OffdPs, OpDesc_A, OpDesc_W1, OpDesc_I1), // psq_l
|
||||
BASIC_MNEMONIC(57, OpDesc_D, OpDesc_OffdPs, OpDesc_A, OpDesc_W1, OpDesc_I1), // psq_lu
|
||||
BASIC_MNEMONIC(60, OpDesc_S, OpDesc_OffdPs, OpDesc_A, OpDesc_W1, OpDesc_I1), // psq_st
|
||||
BASIC_MNEMONIC(61, OpDesc_S, OpDesc_OffdPs, OpDesc_A, OpDesc_W1, OpDesc_I1), // psq_stu
|
||||
|
||||
// A-31
|
||||
RC_MNEMONIC(4, InsertVal(18, 26, 30), _D, _A, _B), // ps_div
|
||||
RC_MNEMONIC(4, InsertVal(20, 26, 30), _D, _A, _B), // ps_sub
|
||||
RC_MNEMONIC(4, InsertVal(21, 26, 30), _D, _A, _B), // ps_add
|
||||
RC_MNEMONIC(4, InsertVal(23, 26, 30), _D, _A, _C, _B), // ps_sel
|
||||
RC_MNEMONIC(4, InsertVal(24, 26, 30), _D, _B), // ps_res
|
||||
RC_MNEMONIC(4, InsertVal(25, 26, 30), _D, _A, _C), // ps_mul
|
||||
RC_MNEMONIC(4, InsertVal(26, 26, 30), _D, _B), // ps_rsqrte
|
||||
RC_MNEMONIC(4, InsertVal(28, 26, 30), _D, _A, _C, _B), // ps_msub
|
||||
RC_MNEMONIC(4, InsertVal(29, 26, 30), _D, _A, _C, _B), // ps_madd
|
||||
RC_MNEMONIC(4, InsertVal(30, 26, 30), _D, _A, _C, _B), // ps_nmsub
|
||||
RC_MNEMONIC(4, InsertVal(31, 26, 30), _D, _A, _C, _B), // ps_nmadd
|
||||
RC_MNEMONIC(4, InsertVal(40, 21, 30), _D, _B), // ps_neg
|
||||
RC_MNEMONIC(4, InsertVal(72, 21, 30), _D, _B), // ps_mr
|
||||
RC_MNEMONIC(4, InsertVal(136, 21, 30), _D, _B), // ps_nabs
|
||||
RC_MNEMONIC(4, InsertVal(264, 21, 30), _D, _B), // ps_abs
|
||||
RC_MNEMONIC(4, InsertVal(18, 26, 30), OpDesc_D, OpDesc_A, OpDesc_B), // ps_div
|
||||
RC_MNEMONIC(4, InsertVal(20, 26, 30), OpDesc_D, OpDesc_A, OpDesc_B), // ps_sub
|
||||
RC_MNEMONIC(4, InsertVal(21, 26, 30), OpDesc_D, OpDesc_A, OpDesc_B), // ps_add
|
||||
RC_MNEMONIC(4, InsertVal(23, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // ps_sel
|
||||
RC_MNEMONIC(4, InsertVal(24, 26, 30), OpDesc_D, OpDesc_B), // ps_res
|
||||
RC_MNEMONIC(4, InsertVal(25, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C), // ps_mul
|
||||
RC_MNEMONIC(4, InsertVal(26, 26, 30), OpDesc_D, OpDesc_B), // ps_rsqrte
|
||||
RC_MNEMONIC(4, InsertVal(28, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // ps_msub
|
||||
RC_MNEMONIC(4, InsertVal(29, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // ps_madd
|
||||
RC_MNEMONIC(4, InsertVal(30, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // ps_nmsub
|
||||
RC_MNEMONIC(4, InsertVal(31, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // ps_nmadd
|
||||
RC_MNEMONIC(4, InsertVal(40, 21, 30), OpDesc_D, OpDesc_B), // ps_neg
|
||||
RC_MNEMONIC(4, InsertVal(72, 21, 30), OpDesc_D, OpDesc_B), // ps_mr
|
||||
RC_MNEMONIC(4, InsertVal(136, 21, 30), OpDesc_D, OpDesc_B), // ps_nabs
|
||||
RC_MNEMONIC(4, InsertVal(264, 21, 30), OpDesc_D, OpDesc_B), // ps_abs
|
||||
|
||||
// A-32
|
||||
RC_MNEMONIC(4, InsertVal(10, 26, 30), _D, _A, _C, _B), // ps_sum0
|
||||
RC_MNEMONIC(4, InsertVal(11, 26, 30), _D, _A, _C, _B), // ps_sum1
|
||||
RC_MNEMONIC(4, InsertVal(12, 26, 30), _D, _A, _C), // ps_muls0
|
||||
RC_MNEMONIC(4, InsertVal(13, 26, 30), _D, _A, _C), // ps_muls1
|
||||
RC_MNEMONIC(4, InsertVal(14, 26, 30), _D, _A, _C, _B), // ps_madds0
|
||||
RC_MNEMONIC(4, InsertVal(15, 26, 30), _D, _A, _C, _B), // ps_madds1
|
||||
MNEMONIC(4, InsertVal(0, 21, 30), _Crfd, _A, _B), // ps_cmpu0
|
||||
MNEMONIC(4, InsertVal(32, 21, 30), _Crfd, _A, _B), // ps_cmpo0
|
||||
MNEMONIC(4, InsertVal(64, 21, 30), _Crfd, _A, _B), // ps_cmpu1
|
||||
MNEMONIC(4, InsertVal(96, 21, 30), _Crfd, _A, _B), // ps_cmpo1
|
||||
RC_MNEMONIC(4, InsertVal(528, 21, 30), _D, _A, _B), // ps_merge00
|
||||
RC_MNEMONIC(4, InsertVal(560, 21, 30), _D, _A, _B), // ps_merge01
|
||||
RC_MNEMONIC(4, InsertVal(592, 21, 30), _D, _A, _B), // ps_merge10
|
||||
RC_MNEMONIC(4, InsertVal(624, 21, 30), _D, _A, _B), // ps_merge11
|
||||
MNEMONIC(4, InsertVal(1014, 21, 30), _A, _B), // dcbz_l
|
||||
RC_MNEMONIC(4, InsertVal(10, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // ps_sum0
|
||||
RC_MNEMONIC(4, InsertVal(11, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // ps_sum1
|
||||
RC_MNEMONIC(4, InsertVal(12, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C), // ps_muls0
|
||||
RC_MNEMONIC(4, InsertVal(13, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C), // ps_muls1
|
||||
RC_MNEMONIC(4, InsertVal(14, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // ps_madds0
|
||||
RC_MNEMONIC(4, InsertVal(15, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // ps_madds1
|
||||
MNEMONIC(4, InsertVal(0, 21, 30), OpDesc_Crfd, OpDesc_A, OpDesc_B), // ps_cmpu0
|
||||
MNEMONIC(4, InsertVal(32, 21, 30), OpDesc_Crfd, OpDesc_A, OpDesc_B), // ps_cmpo0
|
||||
MNEMONIC(4, InsertVal(64, 21, 30), OpDesc_Crfd, OpDesc_A, OpDesc_B), // ps_cmpu1
|
||||
MNEMONIC(4, InsertVal(96, 21, 30), OpDesc_Crfd, OpDesc_A, OpDesc_B), // ps_cmpo1
|
||||
RC_MNEMONIC(4, InsertVal(528, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // ps_merge00
|
||||
RC_MNEMONIC(4, InsertVal(560, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // ps_merge01
|
||||
RC_MNEMONIC(4, InsertVal(592, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // ps_merge10
|
||||
RC_MNEMONIC(4, InsertVal(624, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // ps_merge11
|
||||
MNEMONIC(4, InsertVal(1014, 21, 30), OpDesc_A, OpDesc_B), // dcbz_l
|
||||
};
|
||||
|
||||
namespace
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "Common/Assembler/GekkoLexer.h"
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
|
@ -181,6 +182,11 @@ std::optional<T> EvalIntegral(TokenType tp, std::string_view val)
|
|||
case TokenType::BinaryLit:
|
||||
return std::accumulate(val.begin() + 2, val.end(), T{0}, bin_step);
|
||||
case TokenType::GPR:
|
||||
if (CaseInsensitiveEquals(val, "sp"))
|
||||
return T{1};
|
||||
if (CaseInsensitiveEquals(val, "rtoc"))
|
||||
return T{2};
|
||||
[[fallthrough]];
|
||||
case TokenType::FPR:
|
||||
return std::accumulate(val.begin() + 1, val.end(), T{0}, dec_step);
|
||||
case TokenType::CRField:
|
||||
|
@ -643,50 +649,43 @@ TokenType Lexer::ClassifyAlnum() const
|
|||
|
||||
if (rn[0] == '3')
|
||||
{
|
||||
return rn[1] <= '2';
|
||||
return rn[1] < '2';
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
constexpr auto eq_nocase = [](std::string_view str, std::string_view lwr) {
|
||||
auto it_l = str.cbegin(), it_r = lwr.cbegin();
|
||||
for (; it_l != str.cend() && it_r != lwr.cend(); it_l++, it_r++)
|
||||
{
|
||||
if (std::tolower(*it_l) != *it_r)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return it_l == str.end() && it_r == lwr.end();
|
||||
};
|
||||
|
||||
if (std::tolower(alnum[0]) == 'r' && valid_regnum(alnum.substr(1)))
|
||||
{
|
||||
return TokenType::GPR;
|
||||
}
|
||||
else if ((CaseInsensitiveEquals(alnum, "sp")) || (CaseInsensitiveEquals(alnum, "rtoc")))
|
||||
{
|
||||
return TokenType::GPR;
|
||||
}
|
||||
else if (std::tolower(alnum[0]) == 'f' && valid_regnum(alnum.substr(1)))
|
||||
{
|
||||
return TokenType::FPR;
|
||||
}
|
||||
else if (alnum.length() == 3 && eq_nocase(alnum.substr(0, 2), "cr") && alnum[2] >= '0' &&
|
||||
alnum[2] <= '7')
|
||||
else if (alnum.length() == 3 && CaseInsensitiveEquals(alnum.substr(0, 2), "cr") &&
|
||||
alnum[2] >= '0' && alnum[2] <= '7')
|
||||
{
|
||||
return TokenType::CRField;
|
||||
}
|
||||
else if (eq_nocase(alnum, "lt"))
|
||||
else if (CaseInsensitiveEquals(alnum, "lt"))
|
||||
{
|
||||
return TokenType::Lt;
|
||||
}
|
||||
else if (eq_nocase(alnum, "gt"))
|
||||
else if (CaseInsensitiveEquals(alnum, "gt"))
|
||||
{
|
||||
return TokenType::Gt;
|
||||
}
|
||||
else if (eq_nocase(alnum, "eq"))
|
||||
else if (CaseInsensitiveEquals(alnum, "eq"))
|
||||
{
|
||||
return TokenType::Eq;
|
||||
}
|
||||
else if (eq_nocase(alnum, "so"))
|
||||
else if (CaseInsensitiveEquals(alnum, "so"))
|
||||
{
|
||||
return TokenType::So;
|
||||
}
|
||||
|
|
|
@ -312,7 +312,7 @@ class BitFieldArrayConstRef
|
|||
friend class BitFieldArrayConstIterator<position, bits, size, T, S>;
|
||||
|
||||
public:
|
||||
constexpr T Value() const { return m_array->Value(m_index); };
|
||||
constexpr T Value() const { return m_array->Value(m_index); }
|
||||
constexpr operator T() const { return Value(); }
|
||||
|
||||
private:
|
||||
|
@ -333,7 +333,7 @@ class BitFieldArrayRef
|
|||
friend class BitFieldArrayIterator<position, bits, size, T, S>;
|
||||
|
||||
public:
|
||||
constexpr T Value() const { return m_array->Value(m_index); };
|
||||
constexpr T Value() const { return m_array->Value(m_index); }
|
||||
constexpr operator T() const { return Value(); }
|
||||
T operator=(const BitFieldArrayRef<position, bits, size, T, S>& value) const
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
@ -166,50 +167,10 @@ inline auto BitCastPtr(PtrType* ptr) noexcept -> BitCastPtrType<T, PtrType>
|
|||
}
|
||||
|
||||
// Similar to BitCastPtr, but specifically for aliasing structs to arrays.
|
||||
template <typename ArrayType, typename T,
|
||||
typename Container = std::array<ArrayType, sizeof(T) / sizeof(ArrayType)>>
|
||||
inline auto BitCastToArray(const T& obj) noexcept -> Container
|
||||
template <typename ValueType, typename From>
|
||||
[[nodiscard]] constexpr auto BitCastToArray(const From& obj) noexcept
|
||||
{
|
||||
static_assert(sizeof(T) % sizeof(ArrayType) == 0,
|
||||
"Size of array type must be a factor of size of source type.");
|
||||
static_assert(std::is_trivially_copyable<T>(),
|
||||
"BitCastToArray source type must be trivially copyable.");
|
||||
static_assert(std::is_trivially_copyable<Container>(),
|
||||
"BitCastToArray array type must be trivially copyable.");
|
||||
|
||||
Container result;
|
||||
std::memcpy(result.data(), &obj, sizeof(T));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename ArrayType, typename T,
|
||||
typename Container = std::array<ArrayType, sizeof(T) / sizeof(ArrayType)>>
|
||||
inline void BitCastFromArray(const Container& array, T& obj) noexcept
|
||||
{
|
||||
static_assert(sizeof(T) % sizeof(ArrayType) == 0,
|
||||
"Size of array type must be a factor of size of destination type.");
|
||||
static_assert(std::is_trivially_copyable<Container>(),
|
||||
"BitCastFromArray array type must be trivially copyable.");
|
||||
static_assert(std::is_trivially_copyable<T>(),
|
||||
"BitCastFromArray destination type must be trivially copyable.");
|
||||
|
||||
std::memcpy(&obj, array.data(), sizeof(T));
|
||||
}
|
||||
|
||||
template <typename ArrayType, typename T,
|
||||
typename Container = std::array<ArrayType, sizeof(T) / sizeof(ArrayType)>>
|
||||
inline auto BitCastFromArray(const Container& array) noexcept -> T
|
||||
{
|
||||
static_assert(sizeof(T) % sizeof(ArrayType) == 0,
|
||||
"Size of array type must be a factor of size of destination type.");
|
||||
static_assert(std::is_trivially_copyable<Container>(),
|
||||
"BitCastFromArray array type must be trivially copyable.");
|
||||
static_assert(std::is_trivially_copyable<T>(),
|
||||
"BitCastFromArray destination type must be trivially copyable.");
|
||||
|
||||
T obj;
|
||||
std::memcpy(&obj, array.data(), sizeof(T));
|
||||
return obj;
|
||||
return std::bit_cast<std::array<ValueType, sizeof(From) / sizeof(ValueType)>>(obj);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
|
|
@ -87,6 +87,7 @@ add_library(common
|
|||
JitRegister.cpp
|
||||
JitRegister.h
|
||||
JsonUtil.h
|
||||
JsonUtil.cpp
|
||||
Lazy.h
|
||||
LinearDiskCache.h
|
||||
Logging/ConsoleListener.h
|
||||
|
@ -143,6 +144,7 @@ add_library(common
|
|||
TraversalClient.h
|
||||
TraversalProto.h
|
||||
TypeUtils.h
|
||||
Unreachable.h
|
||||
UPnP.cpp
|
||||
UPnP.h
|
||||
VariantUtil.h
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Common
|
|||
// having to prefix them with gen-> or something similar.
|
||||
// Example implementation:
|
||||
// class JIT : public CodeBlock<ARMXEmitter> {}
|
||||
template <class T>
|
||||
template <class T, bool executable = true>
|
||||
class CodeBlock : public T
|
||||
{
|
||||
private:
|
||||
|
@ -53,7 +53,10 @@ public:
|
|||
{
|
||||
region_size = size;
|
||||
total_region_size = size;
|
||||
region = static_cast<u8*>(Common::AllocateExecutableMemory(total_region_size));
|
||||
if constexpr (executable)
|
||||
region = static_cast<u8*>(Common::AllocateExecutableMemory(total_region_size));
|
||||
else
|
||||
region = static_cast<u8*>(Common::AllocateMemoryPages(total_region_size));
|
||||
T::SetCodePtr(region, region + size);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Common/ColorUtil.h"
|
||||
|
||||
#include "Common/Swap.h"
|
||||
|
||||
namespace Common
|
||||
|
|
|
@ -37,7 +37,7 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
|
|||
{ \
|
||||
DebugBreak(); \
|
||||
}
|
||||
#endif // WIN32 ndef
|
||||
#endif // _WIN32
|
||||
|
||||
namespace Common
|
||||
{
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#define ROOT_DIR "."
|
||||
|
||||
// The normal user directory
|
||||
#ifndef STEAM
|
||||
#ifdef _WIN32
|
||||
#define NORMAL_USER_DIR "Dolphin Emulator"
|
||||
#elif defined(__APPLE__)
|
||||
|
@ -21,15 +20,6 @@
|
|||
#else
|
||||
#define NORMAL_USER_DIR "dolphin-emu"
|
||||
#endif
|
||||
#else // ifndef STEAM
|
||||
#ifdef _WIN32
|
||||
#define NORMAL_USER_DIR "Dolphin Emulator (Steam)"
|
||||
#elif defined(__APPLE__)
|
||||
#define NORMAL_USER_DIR "Library/Application Support/Dolphin (Steam)"
|
||||
#else
|
||||
#define NORMAL_USER_DIR "dolphin-emu-steam"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// The portable user directory
|
||||
#ifdef _WIN32
|
||||
|
@ -63,6 +53,7 @@
|
|||
#define COVERCACHE_DIR "GameCovers"
|
||||
#define REDUMPCACHE_DIR "Redump"
|
||||
#define SHADERCACHE_DIR "Shaders"
|
||||
#define RETROACHIEVEMENTSCACHE_DIR "RetroAchievements"
|
||||
#define STATESAVES_DIR "StateSaves"
|
||||
#define SCREENSHOTS_DIR "ScreenShots"
|
||||
#define LOAD_DIR "Load"
|
||||
|
@ -160,6 +151,10 @@
|
|||
|
||||
#define GECKO_CODE_HANDLER "codehandler.bin"
|
||||
|
||||
#define GECKO_CODE_HANDLER_MPN "codehandler-mpn.bin"
|
||||
|
||||
#define GECKO_CODE_HANDLER_MPN_SUPER "codehandler-mpn.bin"
|
||||
|
||||
// Subdirs in Sys
|
||||
#define GC_SYS_DIR "GC"
|
||||
#define WII_SYS_DIR "Wii"
|
||||
|
|
|
@ -261,4 +261,4 @@ int __cdecl EnableCompatPatches()
|
|||
extern "C" {
|
||||
__declspec(allocate(".CRT$XCZ")) decltype(&EnableCompatPatches)
|
||||
enableCompatPatches = EnableCompatPatches;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright 2017 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Common/Crypto/AES.h"
|
||||
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <memory>
|
||||
|
@ -9,7 +11,6 @@
|
|||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/CPUDetect.h"
|
||||
#include "Common/Crypto/AES.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
|
|
|
@ -385,4 +385,20 @@ Digest CalculateDigest(const u8* msg, size_t len)
|
|||
ctx->Update(msg, len);
|
||||
return ctx->Finish();
|
||||
}
|
||||
|
||||
std::string DigestToString(const Digest& digest)
|
||||
{
|
||||
static constexpr std::array<char, 16> lookup = {'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
std::string hash;
|
||||
hash.reserve(digest.size() * 2);
|
||||
for (size_t i = 0; i < digest.size(); ++i)
|
||||
{
|
||||
const u8 upper = static_cast<u8>((digest[i] >> 4) & 0xf);
|
||||
const u8 lower = static_cast<u8>(digest[i] & 0xf);
|
||||
hash.push_back(lookup[upper]);
|
||||
hash.push_back(lookup[lower]);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
} // namespace Common::SHA1
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <array>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
@ -23,7 +24,11 @@ class Context
|
|||
public:
|
||||
virtual ~Context() = default;
|
||||
virtual void Update(const u8* msg, size_t len) = 0;
|
||||
void Update(const std::vector<u8>& msg) { return Update(msg.data(), msg.size()); }
|
||||
void Update(std::span<const u8> msg) { return Update(msg.data(), msg.size()); }
|
||||
void Update(std::string_view msg)
|
||||
{
|
||||
return Update(reinterpret_cast<const u8*>(msg.data()), msg.size());
|
||||
}
|
||||
virtual Digest Finish() = 0;
|
||||
virtual bool HwAccelerated() const = 0;
|
||||
};
|
||||
|
@ -51,4 +56,6 @@ inline Digest CalculateDigest(const std::array<T, Size>& msg)
|
|||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
return CalculateDigest(reinterpret_cast<const u8*>(msg.data()), sizeof(msg));
|
||||
}
|
||||
|
||||
std::string DigestToString(const Digest& digest);
|
||||
} // namespace Common::SHA1
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
namespace Core
|
||||
{
|
||||
class CPUThreadGuard;
|
||||
};
|
||||
}
|
||||
|
||||
namespace Common::Debug
|
||||
{
|
||||
|
|
|
@ -553,7 +553,7 @@ bool SyncSDFolderToSDImage(const std::function<bool()>& cancelled, bool determin
|
|||
}
|
||||
|
||||
MKFS_PARM options = {};
|
||||
options.fmt = FM_FAT32;
|
||||
options.fmt = FM_FAT32 | FM_SFD;
|
||||
options.n_fat = 0; // Number of FATs: automatic
|
||||
options.align = 1; // Alignment of the data region (in sectors)
|
||||
options.n_root = 0; // Number of root directory entries: automatic (and unused for FAT32)
|
||||
|
|
|
@ -60,6 +60,10 @@
|
|||
#include "jni/AndroidCommon/AndroidCommon.h"
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace File
|
||||
|
@ -738,6 +742,15 @@ std::string GetExePath()
|
|||
return PathToString(exe_path_absolute);
|
||||
#elif defined(__APPLE__)
|
||||
return GetBundleDirectory();
|
||||
#elif defined(__FreeBSD__)
|
||||
int name[4]{CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
|
||||
size_t length = 0;
|
||||
if (sysctl(name, 4, nullptr, &length, nullptr, 0) != 0 || length == 0)
|
||||
return {};
|
||||
std::string dolphin_exe_path(length, '\0');
|
||||
if (sysctl(name, 4, dolphin_exe_path.data(), &length, nullptr, 0) != 0)
|
||||
return {};
|
||||
return dolphin_exe_path;
|
||||
#else
|
||||
char dolphin_exe_path[PATH_MAX];
|
||||
ssize_t len = ::readlink("/proc/self/exe", dolphin_exe_path, sizeof(dolphin_exe_path));
|
||||
|
@ -843,6 +856,8 @@ static void RebuildUserDirectories(unsigned int dir_index)
|
|||
s_user_paths[D_COVERCACHE_IDX] = s_user_paths[D_CACHE_IDX] + COVERCACHE_DIR DIR_SEP;
|
||||
s_user_paths[D_REDUMPCACHE_IDX] = s_user_paths[D_CACHE_IDX] + REDUMPCACHE_DIR DIR_SEP;
|
||||
s_user_paths[D_SHADERCACHE_IDX] = s_user_paths[D_CACHE_IDX] + SHADERCACHE_DIR DIR_SEP;
|
||||
s_user_paths[D_RETROACHIEVEMENTSCACHE_IDX] =
|
||||
s_user_paths[D_CACHE_IDX] + RETROACHIEVEMENTSCACHE_DIR DIR_SEP;
|
||||
s_user_paths[D_SHADERS_IDX] = s_user_paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
|
||||
s_user_paths[D_STATESAVES_IDX] = s_user_paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
|
||||
s_user_paths[D_SCREENSHOTS_IDX] = s_user_paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP;
|
||||
|
@ -926,6 +941,8 @@ static void RebuildUserDirectories(unsigned int dir_index)
|
|||
s_user_paths[D_COVERCACHE_IDX] = s_user_paths[D_CACHE_IDX] + COVERCACHE_DIR DIR_SEP;
|
||||
s_user_paths[D_REDUMPCACHE_IDX] = s_user_paths[D_CACHE_IDX] + REDUMPCACHE_DIR DIR_SEP;
|
||||
s_user_paths[D_SHADERCACHE_IDX] = s_user_paths[D_CACHE_IDX] + SHADERCACHE_DIR DIR_SEP;
|
||||
s_user_paths[D_RETROACHIEVEMENTSCACHE_IDX] =
|
||||
s_user_paths[D_CACHE_IDX] + RETROACHIEVEMENTSCACHE_DIR DIR_SEP;
|
||||
break;
|
||||
|
||||
case D_GCUSER_IDX:
|
||||
|
|
|
@ -40,6 +40,7 @@ enum
|
|||
D_COVERCACHE_IDX,
|
||||
D_REDUMPCACHE_IDX,
|
||||
D_SHADERCACHE_IDX,
|
||||
D_RETROACHIEVEMENTSCACHE_IDX,
|
||||
D_SHADERS_IDX,
|
||||
D_STATESAVES_IDX,
|
||||
D_SCREENSHOTS_IDX,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Common/GL/GLInterface/AGL.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
// UpdateCachedDimensions and AttachContextToView contain calls to UI APIs, so they must only be
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Common/GL/GLX11Window.h"
|
||||
|
||||
#include "Common/GL/GLContext.h"
|
||||
|
||||
GLX11Window::GLX11Window(Display* display, Window parent_window, Colormap color_map, Window window,
|
||||
|
|
|
@ -75,7 +75,7 @@ bool IniFile::Section::Get(std::string_view key, std::string* value,
|
|||
|
||||
bool IniFile::Section::Exists(std::string_view key) const
|
||||
{
|
||||
return values.find(key) != values.end();
|
||||
return values.contains(key);
|
||||
}
|
||||
|
||||
bool IniFile::Section::Delete(std::string_view key)
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
|
||||
#include "Common/JsonUtil.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "Common/FileUtil.h"
|
||||
|
||||
picojson::object ToJsonObject(const Common::Vec3& vec)
|
||||
{
|
||||
picojson::object obj;
|
||||
|
@ -38,3 +42,27 @@ std::optional<bool> ReadBoolFromJson(const picojson::object& obj, const std::str
|
|||
return std::nullopt;
|
||||
return it->second.get<bool>();
|
||||
}
|
||||
|
||||
bool JsonToFile(const std::string& filename, const picojson::value& root, bool prettify)
|
||||
{
|
||||
std::ofstream json_stream;
|
||||
File::OpenFStream(json_stream, filename, std::ios_base::out);
|
||||
if (!json_stream.is_open())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
json_stream << root.serialize(prettify);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonFromFile(const std::string& filename, picojson::value* root, std::string* error)
|
||||
{
|
||||
std::string json_data;
|
||||
if (!File::ReadFileToString(filename, json_data))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*error = picojson::parse(*root, json_data);
|
||||
return error->empty();
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include <picojson.h>
|
||||
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Common/Matrix.h"
|
||||
|
||||
// Ideally this would use a concept like, 'template <std::ranges::range Range>' to constrain it,
|
||||
|
@ -47,7 +46,7 @@ std::optional<Type> ReadNumericFromJson(const picojson::object& obj, const std::
|
|||
return std::nullopt;
|
||||
if (!it->second.is<double>())
|
||||
return std::nullopt;
|
||||
return MathUtil::SaturatingCast<Type>(it->second.get<double>());
|
||||
return static_cast<Type>(it->second.get<double>());
|
||||
}
|
||||
|
||||
std::optional<std::string> ReadStringFromJson(const picojson::object& obj, const std::string& key);
|
||||
|
@ -56,3 +55,6 @@ std::optional<bool> ReadBoolFromJson(const picojson::object& obj, const std::str
|
|||
|
||||
picojson::object ToJsonObject(const Common::Vec3& vec);
|
||||
void FromJson(const picojson::object& obj, Common::Vec3& vec);
|
||||
|
||||
bool JsonToFile(const std::string& filename, const picojson::value& root, bool prettify = false);
|
||||
bool JsonFromFile(const std::string& filename, picojson::value* root, std::string* error);
|
||||
|
|
|
@ -78,7 +78,7 @@ public:
|
|||
{
|
||||
static LdrDllNotifier notifier;
|
||||
return notifier;
|
||||
};
|
||||
}
|
||||
void Install(LdrObserver* observer);
|
||||
void Uninstall(LdrObserver* observer);
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ public:
|
|||
private:
|
||||
Profiler* m_p;
|
||||
};
|
||||
}; // namespace Common
|
||||
} // namespace Common
|
||||
|
||||
// Warning: This profiler isn't thread safe. Only profile functions which doesn't run simultaneously
|
||||
#define PROFILE(name) \
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
|
||||
#include "Common/SocketContext.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/Network.h"
|
||||
|
||||
namespace Common
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
@ -11,7 +14,23 @@ SocketContext::SocketContext()
|
|||
std::lock_guard<std::mutex> g(s_lock);
|
||||
if (s_num_objects == 0)
|
||||
{
|
||||
static_cast<void>(WSAStartup(MAKEWORD(2, 2), &s_data));
|
||||
const int ret = WSAStartup(MAKEWORD(2, 2), &s_data);
|
||||
if (ret == 0)
|
||||
{
|
||||
INFO_LOG_FMT(COMMON, "WSAStartup succeeded, wVersion={}.{}, wHighVersion={}.{}",
|
||||
int(LOBYTE(s_data.wVersion)), int(HIBYTE(s_data.wVersion)),
|
||||
int(LOBYTE(s_data.wHighVersion)), int(HIBYTE(s_data.wHighVersion)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// The WSAStartup function directly returns the extended error code in the return value.
|
||||
// A call to the WSAGetLastError function is not needed and should not be used.
|
||||
//
|
||||
// Source:
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsastartup
|
||||
ERROR_LOG_FMT(COMMON, "WSAStartup failed with error {}: {}", ret,
|
||||
Common::DecodeNetworkError(ret));
|
||||
}
|
||||
}
|
||||
s_num_objects++;
|
||||
}
|
||||
|
|
|
@ -195,7 +195,7 @@ std::from_chars_result FromChars(std::string_view sv, T& value,
|
|||
const char* const last = first + sv.size();
|
||||
return std::from_chars(first, last, value, fmt);
|
||||
}
|
||||
}; // namespace Common
|
||||
} // namespace Common
|
||||
|
||||
std::string TabsToSpaces(int tab_size, std::string str);
|
||||
|
||||
|
|
|
@ -82,17 +82,4 @@ static_assert(!IsNOf<int, 1, int, int>::value);
|
|||
static_assert(IsNOf<int, 2, int, int>::value);
|
||||
static_assert(IsNOf<int, 2, int, short>::value); // Type conversions ARE allowed
|
||||
static_assert(!IsNOf<int, 2, int, char*>::value);
|
||||
|
||||
// TODO: This can be replaced with std::array's fill() once C++20 is fully supported.
|
||||
// Prior to C++20, std::array's fill() function is, unfortunately, not constexpr.
|
||||
// Ditto for <algorithm>'s std::fill. Although Dolphin targets C++20, Android doesn't
|
||||
// seem to properly support constexpr fill(), so we need this for now.
|
||||
template <typename T1, size_t N, typename T2>
|
||||
constexpr void Fill(std::array<T1, N>& array, const T2& value)
|
||||
{
|
||||
for (auto& entry : array)
|
||||
{
|
||||
entry = value;
|
||||
}
|
||||
}
|
||||
} // namespace Common
|
||||
|
|
21
Source/Core/Common/Unreachable.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2024 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common/CommonFuncs.h"
|
||||
|
||||
namespace Common
|
||||
{
|
||||
// TODO C++23: Replace with std::unreachable.
|
||||
[[noreturn]] inline void Unreachable()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
Crash();
|
||||
#elif defined(_MSC_VER) && !defined(__clang__)
|
||||
__assume(false);
|
||||
#else
|
||||
__builtin_unreachable();
|
||||
#endif
|
||||
}
|
||||
} // namespace Common
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
namespace Common
|
||||
{
|
||||
#define EMULATOR_NAME "Dolphin"
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define BUILD_TYPE_STR "Debug "
|
||||
#elif defined DEBUGFAST
|
||||
|
@ -17,13 +19,6 @@ namespace Common
|
|||
#define BUILD_TYPE_STR ""
|
||||
#endif
|
||||
|
||||
const std::string& GetScmRevStr()
|
||||
{
|
||||
#define MPN_REV_STR "02/27/2022"
|
||||
static const std::string scm_rev_str = "Dolphin MPN";
|
||||
return scm_rev_str;
|
||||
}
|
||||
|
||||
const std::string& GetScmRevGitStr()
|
||||
{
|
||||
static const std::string scm_rev_git_str = SCM_REV_STR;
|
||||
|
@ -34,6 +29,12 @@ const std::string& GetScmDescStr()
|
|||
{
|
||||
static const std::string scm_desc_str = SCM_DESC_STR;
|
||||
return scm_desc_str;
|
||||
|
||||
}
|
||||
const std::string& GetScmRevStr()
|
||||
{
|
||||
static const std::string scm_rev_str = std::string("Dolphin MPN (") + SCM_DESC_STR + ")";
|
||||
return scm_rev_str;
|
||||
}
|
||||
|
||||
const std::string& GetScmBranchStr()
|
||||
|
@ -42,6 +43,12 @@ const std::string& GetScmBranchStr()
|
|||
return scm_branch_str;
|
||||
}
|
||||
|
||||
const std::string& GetUserAgentStr()
|
||||
{
|
||||
static const std::string user_agent_str = EMULATOR_NAME "/" SCM_DESC_STR;
|
||||
return user_agent_str;
|
||||
}
|
||||
|
||||
const std::string& GetScmDistributorStr()
|
||||
{
|
||||
static const std::string scm_distributor_str = SCM_DISTRIBUTOR_STR;
|
||||
|
|
|
@ -11,6 +11,7 @@ const std::string& GetScmDescStr();
|
|||
const std::string& GetScmBranchStr();
|
||||
const std::string& GetScmRevStr();
|
||||
const std::string& GetScmRevGitStr();
|
||||
const std::string& GetUserAgentStr();
|
||||
const std::string& GetScmDistributorStr();
|
||||
const std::string& GetScmUpdateTrackStr();
|
||||
const std::string& GetNetplayDolphinVer();
|
||||
|
|
|
@ -69,4 +69,4 @@ OSVERSIONINFOW GetOSVersion()
|
|||
}
|
||||
return info;
|
||||
}
|
||||
}; // namespace WindowsRegistry
|
||||
} // namespace WindowsRegistry
|
||||
|
|
|
@ -15,4 +15,4 @@ template <>
|
|||
bool ReadValue(std::string* value, const std::string& subkey, const std::string& name);
|
||||
|
||||
OSVERSIONINFOW GetOSVersion();
|
||||
}; // namespace WindowsRegistry
|
||||
} // namespace WindowsRegistry
|
||||
|
|
|
@ -3,9 +3,10 @@ var oFS = new ActiveXObject("Scripting.FileSystemObject");
|
|||
|
||||
var outfile = "./scmrev.h";
|
||||
var cmd_revision = " rev-parse HEAD";
|
||||
var cmd_describe = " describe --always --long --dirty";
|
||||
var cmd_describe = " rev-parse --short HEAD";
|
||||
var cmd_branch = " rev-parse --abbrev-ref HEAD";
|
||||
var cmd_commits_ahead = " rev-list --count HEAD ^master";
|
||||
var cmd_get_tag = " describe --exact-match HEAD";
|
||||
|
||||
function GetGitExe()
|
||||
{
|
||||
|
@ -59,6 +60,25 @@ function GetFirstStdOutLine(cmd)
|
|||
}
|
||||
}
|
||||
|
||||
function AttemptToExecuteCommand(cmd)
|
||||
{
|
||||
try
|
||||
{
|
||||
var exec = wshShell.Exec(cmd)
|
||||
|
||||
// wait until the command has finished
|
||||
while (exec.Status == 0) {}
|
||||
|
||||
return exec.ExitCode;
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
// catch "the system cannot find the file specified" error
|
||||
WScript.Echo("Failed to exec " + cmd + " this should never happen");
|
||||
WScript.Quit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function GetFileContents(f)
|
||||
{
|
||||
try
|
||||
|
@ -88,6 +108,12 @@ if (default_update_track == "%DOLPHIN_DEFAULT_UPDATE_TRACK%") default_update_tra
|
|||
// remove hash (and trailing "-0" if needed) from description
|
||||
describe = describe.replace(/(-0)?-[^-]+(-dirty)?$/, '$2');
|
||||
|
||||
// set commits ahead to zero if on a tag
|
||||
if (AttemptToExecuteCommand(gitexe + cmd_get_tag) == 0)
|
||||
{
|
||||
commits_ahead = "0";
|
||||
}
|
||||
|
||||
var out_contents =
|
||||
"#define SCM_REV_STR \"" + revision + "\"\n" +
|
||||
"#define SCM_DESC_STR \"" + describe + "\"\n" +
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
// FIXME: avoid pushing all 16 XMM registers when possible? most functions we call probably
|
||||
// don't actually clobber them.
|
||||
#define ABI_ALL_CALLER_SAVED (BitSet32{RAX, RCX, RDX, RDI, RSI, R8, R9, R10, R11} | ABI_ALL_FPRS)
|
||||
#endif // WIN32
|
||||
#endif // _WIN32
|
||||
|
||||
#define ABI_ALL_CALLEE_SAVED (~ABI_ALL_CALLER_SAVED)
|
||||
|
||||
|
|
|
@ -107,26 +107,6 @@ void XEmitter::SetCodePtr(u8* ptr, u8* end, bool write_failed)
|
|||
m_write_failed = write_failed;
|
||||
}
|
||||
|
||||
const u8* XEmitter::GetCodePtr() const
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
u8* XEmitter::GetWritableCodePtr()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
const u8* XEmitter::GetCodeEnd() const
|
||||
{
|
||||
return m_code_end;
|
||||
}
|
||||
|
||||
u8* XEmitter::GetWritableCodeEnd()
|
||||
{
|
||||
return m_code_end;
|
||||
}
|
||||
|
||||
void XEmitter::Write8(u8 value)
|
||||
{
|
||||
if (code >= m_code_end)
|
||||
|
|
|
@ -394,10 +394,10 @@ public:
|
|||
u8* AlignCode4();
|
||||
u8* AlignCode16();
|
||||
u8* AlignCodePage();
|
||||
const u8* GetCodePtr() const;
|
||||
u8* GetWritableCodePtr();
|
||||
const u8* GetCodeEnd() const;
|
||||
u8* GetWritableCodeEnd();
|
||||
const u8* GetCodePtr() const { return code; }
|
||||
u8* GetWritableCodePtr() { return code; }
|
||||
const u8* GetCodeEnd() const { return m_code_end; }
|
||||
u8* GetWritableCodeEnd() { return m_code_end; }
|
||||
|
||||
void LockFlags() { flags_locked = true; }
|
||||
void UnlockFlags() { flags_locked = false; }
|
||||
|
@ -1082,14 +1082,6 @@ public:
|
|||
ABI_CallFunction(func);
|
||||
}
|
||||
|
||||
template <typename FunctionPointer>
|
||||
void ABI_CallFunctionPP(FunctionPointer func, const void* param1, const void* param2)
|
||||
{
|
||||
MOV(64, R(ABI_PARAM1), Imm64(reinterpret_cast<u64>(param1)));
|
||||
MOV(64, R(ABI_PARAM2), Imm64(reinterpret_cast<u64>(param2)));
|
||||
ABI_CallFunction(func);
|
||||
}
|
||||
|
||||
template <typename FunctionPointer>
|
||||
void ABI_CallFunctionPC(FunctionPointer func, const void* param1, u32 param2)
|
||||
{
|
||||
|
|
|
@ -13,19 +13,31 @@
|
|||
#include <rcheevos/include/rc_api_info.h>
|
||||
#include <rcheevos/include/rc_hash.h>
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/CommonPaths.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/IOFile.h"
|
||||
#include "Common/Image.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/ScopeGuard.h"
|
||||
#include "Common/Version.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
#include "Core/Config/AchievementSettings.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/HW/VideoInterface.h"
|
||||
#include "Core/PatchEngine.h"
|
||||
#include "Core/PowerPC/MMU.h"
|
||||
#include "Core/System.h"
|
||||
#include "DiscIO/Blob.h"
|
||||
#include "UICommon/DiscordPresence.h"
|
||||
#include "VideoCommon/Assets/CustomTextureData.h"
|
||||
#include "VideoCommon/OnScreenDisplay.h"
|
||||
#include "VideoCommon/VideoEvents.h"
|
||||
|
||||
static std::unique_ptr<OSD::Icon> DecodeBadgeToOSDIcon(const AchievementManager::Badge& badge);
|
||||
static const Common::HttpRequest::Headers USER_AGENT_HEADER = {
|
||||
{"User-Agent", Common::GetUserAgentStr()}};
|
||||
|
||||
AchievementManager& AchievementManager::GetInstance()
|
||||
{
|
||||
|
@ -35,9 +47,13 @@ AchievementManager& AchievementManager::GetInstance()
|
|||
|
||||
void AchievementManager::Init()
|
||||
{
|
||||
LoadDefaultBadges();
|
||||
if (!m_client && Config::Get(Config::RA_ENABLED))
|
||||
{
|
||||
m_client = rc_client_create(MemoryPeeker, Request);
|
||||
{
|
||||
std::lock_guard lg{m_lock};
|
||||
m_client = rc_client_create(MemoryVerifier, Request);
|
||||
}
|
||||
std::string host_url = Config::Get(Config::RA_HOST_URL);
|
||||
if (!host_url.empty())
|
||||
rc_client_set_host(m_client, host_url.c_str());
|
||||
|
@ -56,6 +72,33 @@ void AchievementManager::Init()
|
|||
}
|
||||
}
|
||||
|
||||
picojson::value AchievementManager::LoadApprovedList()
|
||||
{
|
||||
picojson::value temp;
|
||||
std::string error;
|
||||
if (!JsonFromFile(fmt::format("{}{}{}", File::GetSysDirectory(), DIR_SEP, APPROVED_LIST_FILENAME),
|
||||
&temp, &error))
|
||||
{
|
||||
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to load approved game settings list {}",
|
||||
APPROVED_LIST_FILENAME);
|
||||
WARN_LOG_FMT(ACHIEVEMENTS, "Error: {}", error);
|
||||
return {};
|
||||
}
|
||||
auto context = Common::SHA1::CreateContext();
|
||||
context->Update(temp.serialize());
|
||||
auto digest = context->Finish();
|
||||
if (digest != APPROVED_LIST_HASH)
|
||||
{
|
||||
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to verify approved game settings list {}",
|
||||
APPROVED_LIST_FILENAME);
|
||||
WARN_LOG_FMT(ACHIEVEMENTS, "Expected hash {}, found hash {}",
|
||||
Common::SHA1::DigestToString(APPROVED_LIST_HASH),
|
||||
Common::SHA1::DigestToString(digest));
|
||||
return {};
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
void AchievementManager::SetUpdateCallback(UpdateCallback callback)
|
||||
{
|
||||
m_update_callback = std::move(callback);
|
||||
|
@ -137,6 +180,7 @@ void AchievementManager::LoadGame(const std::string& file_path, const DiscIO::Vo
|
|||
}
|
||||
else
|
||||
{
|
||||
rc_client_set_read_memory_function(m_client, MemoryVerifier);
|
||||
rc_client_begin_identify_and_load_game(m_client, RC_CONSOLE_GAMECUBE, file_path.c_str(), NULL,
|
||||
0, LoadGameCallback, NULL);
|
||||
}
|
||||
|
@ -148,6 +192,18 @@ bool AchievementManager::IsGameLoaded() const
|
|||
return game_info && game_info->id != 0;
|
||||
}
|
||||
|
||||
void AchievementManager::SetBackgroundExecutionAllowed(bool allowed)
|
||||
{
|
||||
m_background_execution_allowed = allowed;
|
||||
|
||||
Core::System* system = m_system.load(std::memory_order_acquire);
|
||||
if (!system)
|
||||
return;
|
||||
|
||||
if (allowed && Core::GetState(*system) == Core::State::Paused)
|
||||
DoIdle();
|
||||
}
|
||||
|
||||
void AchievementManager::FetchPlayerBadge()
|
||||
{
|
||||
FetchBadge(&m_player_badge, RC_IMAGE_TYPE_USER,
|
||||
|
@ -219,7 +275,8 @@ void AchievementManager::DoFrame()
|
|||
std::lock_guard lg{m_lock};
|
||||
rc_client_do_frame(m_client);
|
||||
}
|
||||
if (!m_system)
|
||||
Core::System* system = m_system.load(std::memory_order_acquire);
|
||||
if (!system)
|
||||
return;
|
||||
auto current_time = std::chrono::steady_clock::now();
|
||||
if (current_time - m_last_rp_time > std::chrono::seconds{10})
|
||||
|
@ -227,9 +284,59 @@ void AchievementManager::DoFrame()
|
|||
m_last_rp_time = current_time;
|
||||
rc_client_get_rich_presence_message(m_client, m_rich_presence.data(), RP_SIZE);
|
||||
m_update_callback(UpdatedItems{.rich_presence = true});
|
||||
if (Config::Get(Config::RA_DISCORD_PRESENCE_ENABLED))
|
||||
Discord::UpdateDiscordPresence();
|
||||
}
|
||||
}
|
||||
|
||||
bool AchievementManager::CanPause()
|
||||
{
|
||||
u32 frames_to_next_pause = 0;
|
||||
bool can_pause = rc_client_can_pause(m_client, &frames_to_next_pause);
|
||||
if (!can_pause)
|
||||
{
|
||||
OSD::AddMessage(
|
||||
fmt::format("RetroAchievements Hardcore Mode:\n"
|
||||
"Cannot pause until another {:.2f} seconds have passed.",
|
||||
static_cast<float>(frames_to_next_pause) /
|
||||
Core::System::GetInstance().GetVideoInterface().GetTargetRefreshRate()),
|
||||
OSD::Duration::VERY_LONG, OSD::Color::RED);
|
||||
}
|
||||
return can_pause;
|
||||
}
|
||||
|
||||
void AchievementManager::DoIdle()
|
||||
{
|
||||
std::thread([this]() {
|
||||
while (true)
|
||||
{
|
||||
Common::SleepCurrentThread(1000);
|
||||
{
|
||||
std::lock_guard lg{m_lock};
|
||||
Core::System* system = m_system.load(std::memory_order_acquire);
|
||||
if (!system || Core::GetState(*system) != Core::State::Paused)
|
||||
return;
|
||||
if (!m_background_execution_allowed)
|
||||
return;
|
||||
if (!m_client || !IsGameLoaded())
|
||||
return;
|
||||
}
|
||||
// rc_client_idle peeks at memory to recalculate rich presence and therefore
|
||||
// needs to be on host or CPU thread to access memory.
|
||||
Core::QueueHostJob([this](Core::System& system) {
|
||||
std::lock_guard lg{m_lock};
|
||||
if (Core::GetState(system) != Core::State::Paused)
|
||||
return;
|
||||
if (!m_background_execution_allowed)
|
||||
return;
|
||||
if (!m_client || !IsGameLoaded())
|
||||
return;
|
||||
rc_client_idle(m_client);
|
||||
});
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
|
||||
std::recursive_mutex& AchievementManager::GetLock()
|
||||
{
|
||||
return m_lock;
|
||||
|
@ -250,6 +357,62 @@ bool AchievementManager::IsHardcoreModeActive() const
|
|||
return rc_client_is_processing_required(m_client);
|
||||
}
|
||||
|
||||
void AchievementManager::FilterApprovedPatches(std::vector<PatchEngine::Patch>& patches,
|
||||
const std::string& game_ini_id) const
|
||||
{
|
||||
if (patches.empty())
|
||||
{
|
||||
// There's nothing to verify, so let's save ourselves some work
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lg{m_lock};
|
||||
|
||||
if (!IsHardcoreModeActive())
|
||||
return;
|
||||
|
||||
const bool known_id = m_ini_root->contains(game_ini_id);
|
||||
|
||||
auto patch_itr = patches.begin();
|
||||
while (patch_itr != patches.end())
|
||||
{
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Verifying patch {}", patch_itr->name);
|
||||
|
||||
bool verified = false;
|
||||
|
||||
if (known_id)
|
||||
{
|
||||
auto context = Common::SHA1::CreateContext();
|
||||
context->Update(Common::BitCastToArray<u8>(static_cast<u64>(patch_itr->entries.size())));
|
||||
for (const auto& entry : patch_itr->entries)
|
||||
{
|
||||
context->Update(Common::BitCastToArray<u8>(entry.type));
|
||||
context->Update(Common::BitCastToArray<u8>(entry.address));
|
||||
context->Update(Common::BitCastToArray<u8>(entry.value));
|
||||
context->Update(Common::BitCastToArray<u8>(entry.comparand));
|
||||
context->Update(Common::BitCastToArray<u8>(entry.conditional));
|
||||
}
|
||||
auto digest = context->Finish();
|
||||
|
||||
verified = m_ini_root->get(game_ini_id).contains(Common::SHA1::DigestToString(digest));
|
||||
}
|
||||
|
||||
if (!verified)
|
||||
{
|
||||
patch_itr = patches.erase(patch_itr);
|
||||
OSD::AddMessage(
|
||||
fmt::format("Failed to verify patch {} from file {}.", patch_itr->name, game_ini_id),
|
||||
OSD::Duration::VERY_LONG, OSD::Color::RED);
|
||||
OSD::AddMessage("Disable hardcore mode to enable this patch.", OSD::Duration::VERY_LONG,
|
||||
OSD::Color::RED);
|
||||
}
|
||||
else
|
||||
{
|
||||
patch_itr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AchievementManager::SetSpectatorMode()
|
||||
{
|
||||
rc_client_set_spectator_mode_enabled(m_client, Config::Get(Config::RA_SPECTATOR_ENABLED));
|
||||
|
@ -275,9 +438,9 @@ u32 AchievementManager::GetPlayerScore() const
|
|||
return user->score;
|
||||
}
|
||||
|
||||
const AchievementManager::BadgeStatus& AchievementManager::GetPlayerBadge() const
|
||||
const AchievementManager::Badge& AchievementManager::GetPlayerBadge() const
|
||||
{
|
||||
return m_player_badge;
|
||||
return m_player_badge.data.empty() ? m_default_player_badge : m_player_badge;
|
||||
}
|
||||
|
||||
std::string_view AchievementManager::GetGameDisplayName() const
|
||||
|
@ -295,17 +458,19 @@ rc_api_fetch_game_data_response_t* AchievementManager::GetGameData()
|
|||
return &m_game_data;
|
||||
}
|
||||
|
||||
const AchievementManager::BadgeStatus& AchievementManager::GetGameBadge() const
|
||||
const AchievementManager::Badge& AchievementManager::GetGameBadge() const
|
||||
{
|
||||
return m_game_badge;
|
||||
return m_game_badge.data.empty() ? m_default_game_badge : m_game_badge;
|
||||
}
|
||||
|
||||
const AchievementManager::BadgeStatus& AchievementManager::GetAchievementBadge(AchievementId id,
|
||||
bool locked) const
|
||||
const AchievementManager::Badge& AchievementManager::GetAchievementBadge(AchievementId id,
|
||||
bool locked) const
|
||||
{
|
||||
auto& badge_list = locked ? m_locked_badges : m_locked_badges;
|
||||
auto& badge_list = locked ? m_locked_badges : m_unlocked_badges;
|
||||
auto itr = badge_list.find(id);
|
||||
return (itr == badge_list.end()) ? m_default_badge : itr->second;
|
||||
return (itr != badge_list.end() && itr->second.data.size() > 0) ?
|
||||
itr->second :
|
||||
(locked ? m_default_locked_badge : m_default_unlocked_badge);
|
||||
}
|
||||
|
||||
const AchievementManager::LeaderboardStatus*
|
||||
|
@ -314,8 +479,6 @@ AchievementManager::GetLeaderboardInfo(AchievementManager::AchievementId leaderb
|
|||
if (const auto leaderboard_iter = m_leaderboard_map.find(leaderboard_id);
|
||||
leaderboard_iter != m_leaderboard_map.end())
|
||||
{
|
||||
if (leaderboard_iter->second.entries.size() == 0)
|
||||
FetchBoardInfo(leaderboard_id);
|
||||
return &leaderboard_iter->second;
|
||||
}
|
||||
|
||||
|
@ -327,7 +490,18 @@ AchievementManager::RichPresence AchievementManager::GetRichPresence() const
|
|||
return m_rich_presence;
|
||||
}
|
||||
|
||||
const AchievementManager::NamedIconMap& AchievementManager::GetChallengeIcons() const
|
||||
bool AchievementManager::AreChallengesUpdated() const
|
||||
{
|
||||
return m_challenges_updated;
|
||||
}
|
||||
|
||||
void AchievementManager::ResetChallengesUpdated()
|
||||
{
|
||||
m_challenges_updated = false;
|
||||
}
|
||||
|
||||
const std::unordered_set<AchievementManager::AchievementId>&
|
||||
AchievementManager::GetActiveChallenges() const
|
||||
{
|
||||
return m_active_challenges;
|
||||
}
|
||||
|
@ -390,29 +564,36 @@ void AchievementManager::CloseGame()
|
|||
{
|
||||
m_active_challenges.clear();
|
||||
m_active_leaderboards.clear();
|
||||
m_game_badge.name.clear();
|
||||
m_game_badge.width = 0;
|
||||
m_game_badge.height = 0;
|
||||
m_game_badge.data.clear();
|
||||
m_unlocked_badges.clear();
|
||||
m_locked_badges.clear();
|
||||
m_leaderboard_map.clear();
|
||||
m_rich_presence.fill('\0');
|
||||
rc_api_destroy_fetch_game_data_response(&m_game_data);
|
||||
m_game_data = {};
|
||||
m_queue.Cancel();
|
||||
m_image_queue.Cancel();
|
||||
rc_client_unload_game(m_client);
|
||||
m_system = nullptr;
|
||||
m_system.store(nullptr, std::memory_order_release);
|
||||
if (Config::Get(Config::RA_DISCORD_PRESENCE_ENABLED))
|
||||
Discord::UpdateDiscordPresence();
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Game closed.");
|
||||
}
|
||||
}
|
||||
|
||||
m_update_callback(UpdatedItems{.all = true});
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Game closed.");
|
||||
}
|
||||
|
||||
void AchievementManager::Logout()
|
||||
{
|
||||
{
|
||||
std::lock_guard lg{m_lock};
|
||||
CloseGame();
|
||||
m_player_badge.name.clear();
|
||||
std::lock_guard lg{m_lock};
|
||||
m_player_badge.width = 0;
|
||||
m_player_badge.height = 0;
|
||||
m_player_badge.data.clear();
|
||||
Config::SetBaseOrCurrent(Config::RA_API_TOKEN, "");
|
||||
}
|
||||
|
||||
|
@ -426,6 +607,7 @@ void AchievementManager::Shutdown()
|
|||
{
|
||||
CloseGame();
|
||||
m_queue.Shutdown();
|
||||
std::lock_guard lg{m_lock};
|
||||
// DON'T log out - keep those credentials for next run.
|
||||
rc_client_destroy(m_client);
|
||||
m_client = nullptr;
|
||||
|
@ -497,6 +679,53 @@ void AchievementManager::FilereaderClose(void* file_handle)
|
|||
delete static_cast<FilereaderState*>(file_handle);
|
||||
}
|
||||
|
||||
void AchievementManager::LoadDefaultBadges()
|
||||
{
|
||||
std::lock_guard lg{m_lock};
|
||||
|
||||
std::string directory = File::GetSysDirectory() + DIR_SEP + RESOURCES_DIR + DIR_SEP;
|
||||
|
||||
if (m_default_player_badge.data.empty())
|
||||
{
|
||||
if (!LoadPNGTexture(&m_default_player_badge,
|
||||
fmt::format("{}{}", directory, DEFAULT_PLAYER_BADGE_FILENAME)))
|
||||
{
|
||||
ERROR_LOG_FMT(ACHIEVEMENTS, "Default player badge '{}' failed to load",
|
||||
DEFAULT_PLAYER_BADGE_FILENAME);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_default_game_badge.data.empty())
|
||||
{
|
||||
if (!LoadPNGTexture(&m_default_game_badge,
|
||||
fmt::format("{}{}", directory, DEFAULT_GAME_BADGE_FILENAME)))
|
||||
{
|
||||
ERROR_LOG_FMT(ACHIEVEMENTS, "Default game badge '{}' failed to load",
|
||||
DEFAULT_GAME_BADGE_FILENAME);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_default_unlocked_badge.data.empty())
|
||||
{
|
||||
if (!LoadPNGTexture(&m_default_unlocked_badge,
|
||||
fmt::format("{}{}", directory, DEFAULT_UNLOCKED_BADGE_FILENAME)))
|
||||
{
|
||||
ERROR_LOG_FMT(ACHIEVEMENTS, "Default unlocked achievement badge '{}' failed to load",
|
||||
DEFAULT_UNLOCKED_BADGE_FILENAME);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_default_locked_badge.data.empty())
|
||||
{
|
||||
if (!LoadPNGTexture(&m_default_locked_badge,
|
||||
fmt::format("{}{}", directory, DEFAULT_LOCKED_BADGE_FILENAME)))
|
||||
{
|
||||
ERROR_LOG_FMT(ACHIEVEMENTS, "Default locked achievement badge '{}' failed to load",
|
||||
DEFAULT_LOCKED_BADGE_FILENAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AchievementManager::LoginCallback(int result, const char* error_message, rc_client_t* client,
|
||||
void* userdata)
|
||||
{
|
||||
|
@ -575,6 +804,8 @@ void AchievementManager::LeaderboardEntriesCallback(int result, const char* erro
|
|||
map_entry.username.assign(response_entry.user);
|
||||
memcpy(map_entry.score.data(), response_entry.display, FORMAT_SIZE);
|
||||
map_entry.rank = response_entry.rank;
|
||||
if (ix == list->user_index)
|
||||
leaderboard.player_index = response_entry.rank;
|
||||
}
|
||||
AchievementManager::GetInstance().m_update_callback({.leaderboards = {*leaderboard_id}});
|
||||
}
|
||||
|
@ -582,6 +813,7 @@ void AchievementManager::LeaderboardEntriesCallback(int result, const char* erro
|
|||
void AchievementManager::LoadGameCallback(int result, const char* error_message,
|
||||
rc_client_t* client, void* userdata)
|
||||
{
|
||||
AchievementManager::GetInstance().m_loading_volume.reset(nullptr);
|
||||
if (result != RC_OK)
|
||||
{
|
||||
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to load data for current game.");
|
||||
|
@ -600,20 +832,40 @@ void AchievementManager::LoadGameCallback(int result, const char* error_message,
|
|||
}
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Loaded data for game ID {}.", game->id);
|
||||
|
||||
AchievementManager::GetInstance().m_display_welcome_message = true;
|
||||
AchievementManager::GetInstance().FetchGameBadges();
|
||||
AchievementManager::GetInstance().m_system = &Core::System::GetInstance();
|
||||
AchievementManager::GetInstance().m_update_callback({.all = true});
|
||||
auto& instance = AchievementManager::GetInstance();
|
||||
rc_client_set_read_memory_function(instance.m_client, MemoryPeeker);
|
||||
instance.m_display_welcome_message = true;
|
||||
instance.FetchGameBadges();
|
||||
instance.m_system.store(&Core::System::GetInstance(), std::memory_order_release);
|
||||
instance.m_update_callback({.all = true});
|
||||
// Set this to a value that will immediately trigger RP
|
||||
AchievementManager::GetInstance().m_last_rp_time =
|
||||
std::chrono::steady_clock::now() - std::chrono::minutes{2};
|
||||
instance.m_last_rp_time = std::chrono::steady_clock::now() - std::chrono::minutes{2};
|
||||
|
||||
std::lock_guard lg{instance.GetLock()};
|
||||
auto* leaderboard_list =
|
||||
rc_client_create_leaderboard_list(client, RC_CLIENT_LEADERBOARD_LIST_GROUPING_NONE);
|
||||
for (u32 bucket = 0; bucket < leaderboard_list->num_buckets; bucket++)
|
||||
{
|
||||
const auto& leaderboard_bucket = leaderboard_list->buckets[bucket];
|
||||
for (u32 board = 0; board < leaderboard_bucket.num_leaderboards; board++)
|
||||
{
|
||||
const auto& leaderboard = leaderboard_bucket.leaderboards[board];
|
||||
instance.m_leaderboard_map.insert(
|
||||
std::pair(leaderboard->id, LeaderboardStatus{.name = leaderboard->title,
|
||||
.description = leaderboard->description}));
|
||||
}
|
||||
}
|
||||
rc_client_destroy_leaderboard_list(leaderboard_list);
|
||||
}
|
||||
|
||||
void AchievementManager::ChangeMediaCallback(int result, const char* error_message,
|
||||
rc_client_t* client, void* userdata)
|
||||
{
|
||||
AchievementManager::GetInstance().m_loading_volume.reset(nullptr);
|
||||
if (result == RC_OK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == RC_HARDCORE_DISABLED)
|
||||
{
|
||||
|
@ -634,11 +886,8 @@ void AchievementManager::DisplayWelcomeMessage()
|
|||
m_display_welcome_message = false;
|
||||
const u32 color =
|
||||
rc_client_get_hardcore_enabled(m_client) ? OSD::Color::YELLOW : OSD::Color::CYAN;
|
||||
if (Config::Get(Config::RA_BADGES_ENABLED) && !m_game_badge.name.empty())
|
||||
{
|
||||
OSD::AddMessage("", OSD::Duration::VERY_LONG, OSD::Color::GREEN,
|
||||
DecodeBadgeToOSDIcon(m_game_badge.badge));
|
||||
}
|
||||
|
||||
OSD::AddMessage("", OSD::Duration::VERY_LONG, OSD::Color::GREEN, &GetGameBadge());
|
||||
auto info = rc_client_get_game_info(m_client);
|
||||
if (!info)
|
||||
{
|
||||
|
@ -668,17 +917,15 @@ void AchievementManager::DisplayWelcomeMessage()
|
|||
|
||||
void AchievementManager::HandleAchievementTriggeredEvent(const rc_client_event_t* client_event)
|
||||
{
|
||||
const auto& instance = AchievementManager::GetInstance();
|
||||
OSD::AddMessage(fmt::format("Unlocked: {} ({})", client_event->achievement->title,
|
||||
client_event->achievement->points),
|
||||
OSD::Duration::VERY_LONG,
|
||||
(rc_client_get_hardcore_enabled(AchievementManager::GetInstance().m_client)) ?
|
||||
OSD::Color::YELLOW :
|
||||
OSD::Color::CYAN,
|
||||
(Config::Get(Config::RA_BADGES_ENABLED)) ?
|
||||
DecodeBadgeToOSDIcon(AchievementManager::GetInstance()
|
||||
.m_unlocked_badges[client_event->achievement->id]
|
||||
.badge) :
|
||||
nullptr);
|
||||
(rc_client_get_hardcore_enabled(instance.m_client)) ? OSD::Color::YELLOW :
|
||||
OSD::Color::CYAN,
|
||||
&instance.GetAchievementBadge(client_event->achievement->id, false));
|
||||
AchievementManager::GetInstance().m_update_callback(
|
||||
UpdatedItems{.achievements = {client_event->achievement->id}});
|
||||
}
|
||||
|
||||
void AchievementManager::HandleLeaderboardStartedEvent(const rc_client_event_t* client_event)
|
||||
|
@ -703,6 +950,8 @@ void AchievementManager::HandleLeaderboardSubmittedEvent(const rc_client_event_t
|
|||
client_event->leaderboard->title),
|
||||
OSD::Duration::VERY_LONG, OSD::Color::YELLOW);
|
||||
AchievementManager::GetInstance().FetchBoardInfo(client_event->leaderboard->id);
|
||||
AchievementManager::GetInstance().m_update_callback(
|
||||
UpdatedItems{.leaderboards = {client_event->leaderboard->id}});
|
||||
}
|
||||
|
||||
void AchievementManager::HandleLeaderboardTrackerUpdateEvent(const rc_client_event_t* client_event)
|
||||
|
@ -735,36 +984,38 @@ void AchievementManager::HandleLeaderboardTrackerHideEvent(const rc_client_event
|
|||
void AchievementManager::HandleAchievementChallengeIndicatorShowEvent(
|
||||
const rc_client_event_t* client_event)
|
||||
{
|
||||
if (Config::Get(Config::RA_BADGES_ENABLED))
|
||||
{
|
||||
auto& unlocked_badges = AchievementManager::GetInstance().m_unlocked_badges;
|
||||
if (const auto unlocked_iter = unlocked_badges.find(client_event->achievement->id);
|
||||
unlocked_iter != unlocked_badges.end())
|
||||
{
|
||||
AchievementManager::GetInstance().m_active_challenges[client_event->achievement->badge_name] =
|
||||
DecodeBadgeToOSDIcon(unlocked_iter->second.badge);
|
||||
}
|
||||
}
|
||||
auto& instance = AchievementManager::GetInstance();
|
||||
const auto [iter, inserted] = instance.m_active_challenges.insert(client_event->achievement->id);
|
||||
if (inserted)
|
||||
instance.m_challenges_updated = true;
|
||||
AchievementManager::GetInstance().m_update_callback(UpdatedItems{.rich_presence = true});
|
||||
}
|
||||
|
||||
void AchievementManager::HandleAchievementChallengeIndicatorHideEvent(
|
||||
const rc_client_event_t* client_event)
|
||||
{
|
||||
AchievementManager::GetInstance().m_active_challenges.erase(
|
||||
client_event->achievement->badge_name);
|
||||
auto& instance = AchievementManager::GetInstance();
|
||||
const auto removed = instance.m_active_challenges.erase(client_event->achievement->id);
|
||||
if (removed > 0)
|
||||
instance.m_challenges_updated = true;
|
||||
AchievementManager::GetInstance().m_update_callback(UpdatedItems{.rich_presence = true});
|
||||
}
|
||||
|
||||
void AchievementManager::HandleAchievementProgressIndicatorShowEvent(
|
||||
const rc_client_event_t* client_event)
|
||||
{
|
||||
auto& instance = AchievementManager::GetInstance();
|
||||
auto current_time = std::chrono::steady_clock::now();
|
||||
const auto message_wait_time = std::chrono::milliseconds{OSD::Duration::SHORT};
|
||||
if (current_time - instance.m_last_progress_message < message_wait_time)
|
||||
return;
|
||||
OSD::AddMessage(fmt::format("{} {}", client_event->achievement->title,
|
||||
client_event->achievement->measured_progress),
|
||||
OSD::Duration::SHORT, OSD::Color::GREEN,
|
||||
(Config::Get(Config::RA_BADGES_ENABLED)) ?
|
||||
DecodeBadgeToOSDIcon(AchievementManager::GetInstance()
|
||||
.m_unlocked_badges[client_event->achievement->id]
|
||||
.badge) :
|
||||
nullptr);
|
||||
&instance.GetAchievementBadge(client_event->achievement->id, false));
|
||||
instance.m_last_progress_message = current_time;
|
||||
AchievementManager::GetInstance().m_update_callback(
|
||||
UpdatedItems{.achievements = {client_event->achievement->id}});
|
||||
}
|
||||
|
||||
void AchievementManager::HandleGameCompletedEvent(const rc_client_event_t* client_event,
|
||||
|
@ -781,9 +1032,7 @@ void AchievementManager::HandleGameCompletedEvent(const rc_client_event_t* clien
|
|||
OSD::AddMessage(fmt::format("Congratulations! {} has {} {}", user_info->display_name,
|
||||
hardcore ? "mastered" : "completed", game_info->title),
|
||||
OSD::Duration::VERY_LONG, hardcore ? OSD::Color::YELLOW : OSD::Color::CYAN,
|
||||
(Config::Get(Config::RA_BADGES_ENABLED)) ?
|
||||
DecodeBadgeToOSDIcon(AchievementManager::GetInstance().m_game_badge.badge) :
|
||||
nullptr);
|
||||
&AchievementManager::GetInstance().GetGameBadge());
|
||||
}
|
||||
|
||||
void AchievementManager::HandleResetEvent(const rc_client_event_t* client_event)
|
||||
|
@ -798,62 +1047,65 @@ void AchievementManager::HandleServerErrorEvent(const rc_client_event_t* client_
|
|||
client_event->server_error->api, client_event->server_error->error_message);
|
||||
}
|
||||
|
||||
static std::unique_ptr<OSD::Icon> DecodeBadgeToOSDIcon(const AchievementManager::Badge& badge)
|
||||
{
|
||||
if (badge.empty())
|
||||
return nullptr;
|
||||
|
||||
auto icon = std::make_unique<OSD::Icon>();
|
||||
if (!Common::LoadPNG(badge, &icon->rgba_data, &icon->width, &icon->height))
|
||||
{
|
||||
ERROR_LOG_FMT(ACHIEVEMENTS, "Error decoding badge.");
|
||||
return nullptr;
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
void AchievementManager::Request(const rc_api_request_t* request,
|
||||
rc_client_server_callback_t callback, void* callback_data,
|
||||
rc_client_t* client)
|
||||
{
|
||||
std::string url = request->url;
|
||||
std::string post_data = request->post_data;
|
||||
AchievementManager::GetInstance().m_queue.EmplaceItem([url = std::move(url),
|
||||
post_data = std::move(post_data),
|
||||
callback = std::move(callback),
|
||||
callback_data = std::move(callback_data)] {
|
||||
const Common::HttpRequest::Headers USER_AGENT_HEADER = {{"User-Agent", "Dolphin/Placeholder"}};
|
||||
AchievementManager::GetInstance().m_queue.EmplaceItem(
|
||||
[url = std::move(url), post_data = std::move(post_data), callback = std::move(callback),
|
||||
callback_data = std::move(callback_data)] {
|
||||
Common::HttpRequest http_request;
|
||||
Common::HttpRequest::Response http_response;
|
||||
if (!post_data.empty())
|
||||
{
|
||||
http_response = http_request.Post(url, post_data, USER_AGENT_HEADER,
|
||||
Common::HttpRequest::AllowedReturnCodes::All);
|
||||
}
|
||||
else
|
||||
{
|
||||
http_response = http_request.Get(url, USER_AGENT_HEADER,
|
||||
Common::HttpRequest::AllowedReturnCodes::All);
|
||||
}
|
||||
|
||||
Common::HttpRequest http_request;
|
||||
Common::HttpRequest::Response http_response;
|
||||
if (!post_data.empty())
|
||||
{
|
||||
http_response = http_request.Post(url, post_data, USER_AGENT_HEADER,
|
||||
Common::HttpRequest::AllowedReturnCodes::All);
|
||||
}
|
||||
else
|
||||
{
|
||||
http_response =
|
||||
http_request.Get(url, USER_AGENT_HEADER, Common::HttpRequest::AllowedReturnCodes::All);
|
||||
}
|
||||
rc_api_server_response_t server_response;
|
||||
if (http_response.has_value() && http_response->size() > 0)
|
||||
{
|
||||
server_response.body = reinterpret_cast<const char*>(http_response->data());
|
||||
server_response.body_length = http_response->size();
|
||||
server_response.http_status_code = http_request.GetLastResponseCode();
|
||||
}
|
||||
else
|
||||
{
|
||||
static constexpr char error_message[] = "Failed HTTP request.";
|
||||
server_response.body = error_message;
|
||||
server_response.body_length = sizeof(error_message);
|
||||
server_response.http_status_code = RC_API_SERVER_RESPONSE_RETRYABLE_CLIENT_ERROR;
|
||||
}
|
||||
|
||||
rc_api_server_response_t server_response;
|
||||
if (http_response.has_value() && http_response->size() > 0)
|
||||
{
|
||||
server_response.body = reinterpret_cast<const char*>(http_response->data());
|
||||
server_response.body_length = http_response->size();
|
||||
server_response.http_status_code = http_request.GetLastResponseCode();
|
||||
}
|
||||
else
|
||||
{
|
||||
constexpr char error_message[] = "Failed HTTP request.";
|
||||
server_response.body = error_message;
|
||||
server_response.body_length = sizeof(error_message);
|
||||
server_response.http_status_code = RC_API_SERVER_RESPONSE_RETRYABLE_CLIENT_ERROR;
|
||||
}
|
||||
callback(&server_response, callback_data);
|
||||
});
|
||||
}
|
||||
|
||||
callback(&server_response, callback_data);
|
||||
});
|
||||
// Currently, when rc_client calls the memory peek method provided in its constructor (or in
|
||||
// rc_client_set_read_memory_function) it will do so on the thread that calls DoFrame, which is
|
||||
// currently the host thread, with one exception: an asynchronous callback in the load game process.
|
||||
// This is done to validate/invalidate each memory reference in the downloaded assets, mark assets
|
||||
// as unsupported, and notify the player upon startup that there are unsupported assets and how
|
||||
// many. As such, all that call needs to do is return the number of bytes that can be read with this
|
||||
// call. As only the CPU and host threads are allowed to read from memory, I provide a separate
|
||||
// method for this verification. In lieu of a more convenient set of steps, I provide MemoryVerifier
|
||||
// to rc_client at construction, and in the Load Game callback, after the verification has been
|
||||
// complete, I call rc_client_set_read_memory_function to switch to the usual MemoryPeeker for all
|
||||
// future synchronous calls.
|
||||
u32 AchievementManager::MemoryVerifier(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client)
|
||||
{
|
||||
auto& system = Core::System::GetInstance();
|
||||
u32 ram_size = system.GetMemory().GetRamSizeReal();
|
||||
if (address >= ram_size)
|
||||
return 0;
|
||||
return std::min(ram_size - address, num_bytes);
|
||||
}
|
||||
|
||||
u32 AchievementManager::MemoryPeeker(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client)
|
||||
|
@ -861,6 +1113,11 @@ u32 AchievementManager::MemoryPeeker(u32 address, u8* buffer, u32 num_bytes, rc_
|
|||
if (buffer == nullptr)
|
||||
return 0u;
|
||||
auto& system = Core::System::GetInstance();
|
||||
if (!(Core::IsHostThread() || Core::IsCPUThread()))
|
||||
{
|
||||
ASSERT_MSG(ACHIEVEMENTS, false, "MemoryPeeker called from wrong thread");
|
||||
return 0;
|
||||
}
|
||||
Core::CPUThreadGuard threadguard(system);
|
||||
for (u32 num_read = 0; num_read < num_bytes; num_read++)
|
||||
{
|
||||
|
@ -873,11 +1130,11 @@ u32 AchievementManager::MemoryPeeker(u32 address, u8* buffer, u32 num_bytes, rc_
|
|||
return num_bytes;
|
||||
}
|
||||
|
||||
void AchievementManager::FetchBadge(AchievementManager::BadgeStatus* badge, u32 badge_type,
|
||||
void AchievementManager::FetchBadge(AchievementManager::Badge* badge, u32 badge_type,
|
||||
const AchievementManager::BadgeNameFunction function,
|
||||
const UpdatedItems callback_data)
|
||||
{
|
||||
if (!m_client || !HasAPIToken() || !Config::Get(Config::RA_BADGES_ENABLED))
|
||||
if (!m_client || !HasAPIToken())
|
||||
{
|
||||
m_update_callback(callback_data);
|
||||
if (m_display_welcome_message && badge_type == RC_IMAGE_TYPE_GAME)
|
||||
|
@ -899,40 +1156,71 @@ void AchievementManager::FetchBadge(AchievementManager::BadgeStatus* badge, u32
|
|||
if (name_to_fetch.empty())
|
||||
return;
|
||||
}
|
||||
rc_api_fetch_image_request_t icon_request = {.image_name = name_to_fetch.c_str(),
|
||||
.image_type = badge_type};
|
||||
Badge fetched_badge;
|
||||
rc_api_request_t api_request;
|
||||
Common::HttpRequest http_request;
|
||||
if (rc_api_init_fetch_image_request(&api_request, &icon_request) != RC_OK)
|
||||
|
||||
const std::string cache_path = fmt::format(
|
||||
"{}/badge-{}-{}.png", File::GetUserPath(D_RETROACHIEVEMENTSCACHE_IDX), badge_type,
|
||||
Common::SHA1::DigestToString(Common::SHA1::CalculateDigest(name_to_fetch)));
|
||||
|
||||
AchievementManager::Badge tmp_badge;
|
||||
if (!LoadPNGTexture(&tmp_badge, cache_path))
|
||||
{
|
||||
ERROR_LOG_FMT(ACHIEVEMENTS, "Invalid request for image {}.", name_to_fetch);
|
||||
return;
|
||||
}
|
||||
auto http_response = http_request.Get(api_request.url);
|
||||
if (http_response.has_value() && http_response->size() <= 0)
|
||||
{
|
||||
WARN_LOG_FMT(ACHIEVEMENTS, "RetroAchievements connection failed on image request.\n URL: {}",
|
||||
api_request.url);
|
||||
rc_api_fetch_image_request_t icon_request = {.image_name = name_to_fetch.c_str(),
|
||||
.image_type = badge_type};
|
||||
Badge fetched_badge;
|
||||
rc_api_request_t api_request;
|
||||
Common::HttpRequest http_request;
|
||||
if (rc_api_init_fetch_image_request(&api_request, &icon_request) != RC_OK)
|
||||
{
|
||||
ERROR_LOG_FMT(ACHIEVEMENTS, "Invalid request for image {}.", name_to_fetch);
|
||||
return;
|
||||
}
|
||||
auto http_response = http_request.Get(api_request.url, USER_AGENT_HEADER,
|
||||
Common::HttpRequest::AllowedReturnCodes::All);
|
||||
if (!http_response.has_value() || http_response->empty())
|
||||
{
|
||||
WARN_LOG_FMT(ACHIEVEMENTS,
|
||||
"RetroAchievements connection failed on image request.\n URL: {}",
|
||||
api_request.url);
|
||||
rc_api_destroy_request(&api_request);
|
||||
m_update_callback(callback_data);
|
||||
return;
|
||||
}
|
||||
|
||||
rc_api_destroy_request(&api_request);
|
||||
m_update_callback(callback_data);
|
||||
return;
|
||||
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Successfully downloaded badge id {}.", name_to_fetch);
|
||||
|
||||
if (!LoadPNGTexture(&tmp_badge, *http_response))
|
||||
{
|
||||
ERROR_LOG_FMT(ACHIEVEMENTS, "Badge '{}' failed to load", name_to_fetch);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string temp_path = fmt::format("{}.tmp", cache_path);
|
||||
File::IOFile temp_file(temp_path, "wb");
|
||||
if (!temp_file.IsOpen() ||
|
||||
!temp_file.WriteBytes(http_response->data(), http_response->size()) ||
|
||||
!temp_file.Close() || !File::Rename(temp_path, cache_path))
|
||||
{
|
||||
File::Delete(temp_path);
|
||||
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to store badge '{}' to cache", name_to_fetch);
|
||||
}
|
||||
}
|
||||
|
||||
rc_api_destroy_request(&api_request);
|
||||
fetched_badge = std::move(*http_response);
|
||||
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Successfully downloaded badge id {}.", name_to_fetch);
|
||||
std::lock_guard lg{m_lock};
|
||||
if (function(*this).empty() || name_to_fetch != function(*this))
|
||||
{
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Requested outdated badge id {}.", name_to_fetch);
|
||||
return;
|
||||
}
|
||||
badge->badge = std::move(fetched_badge);
|
||||
badge->name = std::move(name_to_fetch);
|
||||
|
||||
*badge = std::move(tmp_badge);
|
||||
m_update_callback(callback_data);
|
||||
if (badge_type == RC_IMAGE_TYPE_ACHIEVEMENT &&
|
||||
m_active_challenges.contains(*callback_data.achievements.begin()))
|
||||
{
|
||||
m_challenges_updated = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -5,23 +5,34 @@
|
|||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <rcheevos/include/rc_api_runtime.h>
|
||||
#include <rcheevos/include/rc_api_user.h>
|
||||
#include <rcheevos/include/rc_client.h>
|
||||
#include <rcheevos/include/rc_runtime.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Event.h"
|
||||
#include "Common/HttpRequest.h"
|
||||
#include "Common/JsonUtil.h"
|
||||
#include "Common/Lazy.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
#include "DiscIO/Volume.h"
|
||||
#include "VideoCommon/Assets/CustomTextureData.h"
|
||||
|
||||
namespace Core
|
||||
{
|
||||
|
@ -29,10 +40,10 @@ class CPUThreadGuard;
|
|||
class System;
|
||||
} // namespace Core
|
||||
|
||||
namespace OSD
|
||||
namespace PatchEngine
|
||||
{
|
||||
struct Icon;
|
||||
}
|
||||
struct Patch;
|
||||
} // namespace PatchEngine
|
||||
|
||||
class AchievementManager
|
||||
{
|
||||
|
@ -47,19 +58,20 @@ public:
|
|||
using LeaderboardRank = u32;
|
||||
static constexpr size_t RP_SIZE = 256;
|
||||
using RichPresence = std::array<char, RP_SIZE>;
|
||||
using Badge = std::vector<u8>;
|
||||
using NamedIconMap = std::map<std::string, std::unique_ptr<OSD::Icon>, std::less<>>;
|
||||
using Badge = VideoCommon::CustomTextureData::ArraySlice::Level;
|
||||
static constexpr size_t MAX_DISPLAYED_LBOARDS = 4;
|
||||
|
||||
struct BadgeStatus
|
||||
{
|
||||
std::string name = "";
|
||||
Badge badge{};
|
||||
};
|
||||
|
||||
static constexpr std::string_view DEFAULT_PLAYER_BADGE_FILENAME = "achievements_player.png";
|
||||
static constexpr std::string_view DEFAULT_GAME_BADGE_FILENAME = "achievements_game.png";
|
||||
static constexpr std::string_view DEFAULT_LOCKED_BADGE_FILENAME = "achievements_locked.png";
|
||||
static constexpr std::string_view DEFAULT_UNLOCKED_BADGE_FILENAME = "achievements_unlocked.png";
|
||||
static constexpr std::string_view GRAY = "transparent";
|
||||
static constexpr std::string_view GOLD = "#FFD700";
|
||||
static constexpr std::string_view BLUE = "#0B71C1";
|
||||
static constexpr std::string_view APPROVED_LIST_FILENAME = "ApprovedInis.json";
|
||||
static const inline Common::SHA1::Digest APPROVED_LIST_HASH = {
|
||||
0x50, 0x2F, 0x58, 0x02, 0x94, 0x60, 0x1B, 0x9F, 0x92, 0xC7,
|
||||
0x04, 0x17, 0x50, 0x2E, 0xF3, 0x09, 0x8C, 0x8C, 0xD6, 0xC0};
|
||||
|
||||
struct LeaderboardEntry
|
||||
{
|
||||
|
@ -96,27 +108,36 @@ public:
|
|||
bool HasAPIToken() const;
|
||||
void LoadGame(const std::string& file_path, const DiscIO::Volume* volume);
|
||||
bool IsGameLoaded() const;
|
||||
void SetBackgroundExecutionAllowed(bool allowed);
|
||||
|
||||
void FetchPlayerBadge();
|
||||
void FetchGameBadges();
|
||||
|
||||
void DoFrame();
|
||||
|
||||
bool CanPause();
|
||||
void DoIdle();
|
||||
|
||||
std::recursive_mutex& GetLock();
|
||||
void SetHardcoreMode();
|
||||
bool IsHardcoreModeActive() const;
|
||||
void SetGameIniId(const std::string& game_ini_id) { m_game_ini_id = game_ini_id; }
|
||||
void FilterApprovedPatches(std::vector<PatchEngine::Patch>& patches,
|
||||
const std::string& game_ini_id) const;
|
||||
void SetSpectatorMode();
|
||||
std::string_view GetPlayerDisplayName() const;
|
||||
u32 GetPlayerScore() const;
|
||||
const BadgeStatus& GetPlayerBadge() const;
|
||||
const Badge& GetPlayerBadge() const;
|
||||
std::string_view GetGameDisplayName() const;
|
||||
rc_client_t* GetClient();
|
||||
rc_api_fetch_game_data_response_t* GetGameData();
|
||||
const BadgeStatus& GetGameBadge() const;
|
||||
const BadgeStatus& GetAchievementBadge(AchievementId id, bool locked) const;
|
||||
const Badge& GetGameBadge() const;
|
||||
const Badge& GetAchievementBadge(AchievementId id, bool locked) const;
|
||||
const LeaderboardStatus* GetLeaderboardInfo(AchievementId leaderboard_id);
|
||||
RichPresence GetRichPresence() const;
|
||||
const NamedIconMap& GetChallengeIcons() const;
|
||||
bool AreChallengesUpdated() const;
|
||||
void ResetChallengesUpdated();
|
||||
const std::unordered_set<AchievementId>& GetActiveChallenges() const;
|
||||
std::vector<std::string> GetActiveLeaderboards() const;
|
||||
|
||||
void DoState(PointerWrap& p);
|
||||
|
@ -134,7 +155,7 @@ private:
|
|||
std::unique_ptr<DiscIO::Volume> volume;
|
||||
};
|
||||
|
||||
const BadgeStatus m_default_badge;
|
||||
static picojson::value LoadApprovedList();
|
||||
|
||||
static void* FilereaderOpenByFilepath(const char* path_utf8);
|
||||
static void* FilereaderOpenByVolume(const char* path_utf8);
|
||||
|
@ -143,12 +164,13 @@ private:
|
|||
static size_t FilereaderRead(void* file_handle, void* buffer, size_t requested_bytes);
|
||||
static void FilereaderClose(void* file_handle);
|
||||
|
||||
void LoadDefaultBadges();
|
||||
static void LoginCallback(int result, const char* error_message, rc_client_t* client,
|
||||
void* userdata);
|
||||
|
||||
void FetchBoardInfo(AchievementId leaderboard_id);
|
||||
|
||||
std::unique_ptr<DiscIO::Volume>& GetLoadingVolume() { return m_loading_volume; };
|
||||
std::unique_ptr<DiscIO::Volume>& GetLoadingVolume() { return m_loading_volume; }
|
||||
|
||||
static void LoadGameCallback(int result, const char* error_message, rc_client_t* client,
|
||||
void* userdata);
|
||||
|
@ -176,31 +198,42 @@ private:
|
|||
|
||||
static void Request(const rc_api_request_t* request, rc_client_server_callback_t callback,
|
||||
void* callback_data, rc_client_t* client);
|
||||
static u32 MemoryVerifier(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client);
|
||||
static u32 MemoryPeeker(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client);
|
||||
void FetchBadge(BadgeStatus* badge, u32 badge_type, const BadgeNameFunction function,
|
||||
void FetchBadge(Badge* badge, u32 badge_type, const BadgeNameFunction function,
|
||||
const UpdatedItems callback_data);
|
||||
static void EventHandler(const rc_client_event_t* event, rc_client_t* client);
|
||||
|
||||
rc_runtime_t m_runtime{};
|
||||
rc_client_t* m_client{};
|
||||
Core::System* m_system{};
|
||||
std::atomic<Core::System*> m_system{};
|
||||
bool m_is_runtime_initialized = false;
|
||||
UpdateCallback m_update_callback = [](const UpdatedItems&) {};
|
||||
std::unique_ptr<DiscIO::Volume> m_loading_volume;
|
||||
BadgeStatus m_player_badge;
|
||||
Badge m_default_player_badge;
|
||||
Badge m_default_game_badge;
|
||||
Badge m_default_unlocked_badge;
|
||||
Badge m_default_locked_badge;
|
||||
std::atomic_bool m_background_execution_allowed = true;
|
||||
Badge m_player_badge;
|
||||
Hash m_game_hash{};
|
||||
u32 m_game_id = 0;
|
||||
rc_api_fetch_game_data_response_t m_game_data{};
|
||||
bool m_is_game_loaded = false;
|
||||
BadgeStatus m_game_badge;
|
||||
Badge m_game_badge;
|
||||
bool m_display_welcome_message = false;
|
||||
std::unordered_map<AchievementId, BadgeStatus> m_unlocked_badges;
|
||||
std::unordered_map<AchievementId, BadgeStatus> m_locked_badges;
|
||||
std::unordered_map<AchievementId, Badge> m_unlocked_badges;
|
||||
std::unordered_map<AchievementId, Badge> m_locked_badges;
|
||||
RichPresence m_rich_presence;
|
||||
std::chrono::steady_clock::time_point m_last_rp_time = std::chrono::steady_clock::now();
|
||||
std::chrono::steady_clock::time_point m_last_progress_message = std::chrono::steady_clock::now();
|
||||
|
||||
Common::Lazy<picojson::value> m_ini_root{LoadApprovedList};
|
||||
std::string m_game_ini_id;
|
||||
|
||||
std::unordered_map<AchievementId, LeaderboardStatus> m_leaderboard_map;
|
||||
NamedIconMap m_active_challenges;
|
||||
bool m_challenges_updated = false;
|
||||
std::unordered_set<AchievementId> m_active_challenges;
|
||||
std::vector<rc_client_leaderboard_tracker_t> m_active_leaderboards;
|
||||
|
||||
Common::WorkQueueThread<std::function<void()>> m_queue;
|
||||
|
@ -209,4 +242,33 @@ private:
|
|||
std::recursive_mutex m_filereader_lock;
|
||||
}; // class AchievementManager
|
||||
|
||||
#else // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
class Volume;
|
||||
}
|
||||
|
||||
class AchievementManager
|
||||
{
|
||||
public:
|
||||
static AchievementManager& GetInstance()
|
||||
{
|
||||
static AchievementManager s_instance;
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
constexpr bool IsHardcoreModeActive() { return false; }
|
||||
|
||||
constexpr void LoadGame(const std::string&, const DiscIO::Volume*) {}
|
||||
|
||||
constexpr void SetBackgroundExecutionAllowed(bool allowed) {}
|
||||
|
||||
constexpr void DoFrame() {}
|
||||
|
||||
constexpr void CloseGame() {}
|
||||
};
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
|
|
@ -18,7 +18,7 @@ class IniFile;
|
|||
namespace Core
|
||||
{
|
||||
class CPUThreadGuard;
|
||||
};
|
||||
}
|
||||
|
||||
namespace ActionReplay
|
||||
{
|
||||
|
|
|
@ -235,7 +235,7 @@ std::unique_ptr<BootParameters> BootParameters::GenerateFromFile(std::vector<std
|
|||
|
||||
static const std::unordered_set<std::string> disc_image_extensions = {
|
||||
{".gcm", ".iso", ".tgc", ".wbfs", ".ciso", ".gcz", ".wia", ".rvz", ".nfs", ".dol", ".elf"}};
|
||||
if (disc_image_extensions.find(extension) != disc_image_extensions.end())
|
||||
if (disc_image_extensions.contains(extension))
|
||||
{
|
||||
std::unique_ptr<DiscIO::VolumeDisc> disc = DiscIO::CreateDisc(path);
|
||||
if (disc)
|
||||
|
@ -575,9 +575,7 @@ bool CBoot::BootUp(Core::System& system, const Core::CPUThreadGuard& guard,
|
|||
SetupGCMemory(system, guard);
|
||||
}
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().LoadGame(executable.path, nullptr);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
if (!executable.reader->LoadIntoMemory(system))
|
||||
{
|
||||
|
|
|
@ -24,7 +24,7 @@ public:
|
|||
|
||||
bool IsValid() const override { return m_is_valid; }
|
||||
bool IsWii() const override { return m_is_wii; }
|
||||
bool IsAncast() const { return m_is_ancast; };
|
||||
bool IsAncast() const { return m_is_ancast; }
|
||||
u32 GetEntryPoint() const override { return m_dolheader.entryPoint; }
|
||||
bool LoadIntoMemory(Core::System& system, bool only_in_mem1 = false) const override;
|
||||
bool LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db) const override
|
||||
|
|
|
@ -166,9 +166,7 @@ bool BootCore(Core::System& system, std::unique_ptr<BootParameters> boot,
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().CloseGame();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
const bool load_ipl = !system.IsWii() && !Config::Get(Config::MAIN_SKIP_IPL) &&
|
||||
std::holds_alternative<BootParameters::Disc>(boot->parameters);
|
||||
|
@ -211,8 +209,7 @@ static void RestoreSYSCONF()
|
|||
},
|
||||
setting.config_info);
|
||||
}
|
||||
// Save the SYSCONF.
|
||||
Config::GetLayer(Config::LayerType::Base)->Save();
|
||||
ConfigLoaders::SaveToSYSCONF(Config::LayerType::Base);
|
||||
}
|
||||
|
||||
void RestoreConfig()
|
||||
|
|
|
@ -485,8 +485,10 @@ add_library(core
|
|||
PowerPC/BreakPoints.h
|
||||
PowerPC/CachedInterpreter/CachedInterpreter.cpp
|
||||
PowerPC/CachedInterpreter/CachedInterpreter.h
|
||||
PowerPC/CachedInterpreter/InterpreterBlockCache.cpp
|
||||
PowerPC/CachedInterpreter/InterpreterBlockCache.h
|
||||
PowerPC/CachedInterpreter/CachedInterpreterBlockCache.cpp
|
||||
PowerPC/CachedInterpreter/CachedInterpreterBlockCache.h
|
||||
PowerPC/CachedInterpreter/CachedInterpreterEmitter.cpp
|
||||
PowerPC/CachedInterpreter/CachedInterpreterEmitter.h
|
||||
PowerPC/ConditionRegister.cpp
|
||||
PowerPC/ConditionRegister.h
|
||||
PowerPC/Expression.cpp
|
||||
|
@ -783,7 +785,7 @@ if(MSVC)
|
|||
endif()
|
||||
|
||||
if(USE_RETRO_ACHIEVEMENTS)
|
||||
target_link_libraries(core PRIVATE rcheevos)
|
||||
target_compile_definitions(core PRIVATE -DUSE_RETRO_ACHIEVEMENTS)
|
||||
target_compile_definitions(core PRIVATE -DRC_CLIENT_SUPPORTS_HASH)
|
||||
target_link_libraries(core PUBLIC rcheevos)
|
||||
target_compile_definitions(core PUBLIC -DUSE_RETRO_ACHIEVEMENTS)
|
||||
target_compile_definitions(core PUBLIC -DRC_CLIENT_SUPPORTS_HASH)
|
||||
endif()
|
||||
|
|
|
@ -38,7 +38,7 @@ void OnConfigChanged()
|
|||
}
|
||||
}
|
||||
|
||||
}; // namespace
|
||||
} // namespace
|
||||
|
||||
namespace CPUThreadConfigCallback
|
||||
{
|
||||
|
@ -73,4 +73,4 @@ void CheckForConfigChanges()
|
|||
RunCallbacks();
|
||||
}
|
||||
|
||||
}; // namespace CPUThreadConfigCallback
|
||||
} // namespace CPUThreadConfigCallback
|
||||
|
|
|
@ -27,4 +27,4 @@ void RemoveConfigChangedCallback(ConfigChangedCallbackID callback_id);
|
|||
// Should be called regularly from the CPU thread
|
||||
void CheckForConfigChanges();
|
||||
|
||||
}; // namespace CPUThreadConfigCallback
|
||||
} // namespace CPUThreadConfigCallback
|
||||
|
|
|
@ -207,10 +207,8 @@ Cheats::NewSearch(const Core::CPUThreadGuard& guard,
|
|||
PowerPC::RequestedAddressSpace address_space, bool aligned,
|
||||
const std::function<bool(const T& value)>& validator)
|
||||
{
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (AchievementManager::GetInstance().IsHardcoreModeActive())
|
||||
return Cheats::SearchErrorCode::DisabledInHardcoreMode;
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
auto& system = guard.GetSystem();
|
||||
std::vector<Cheats::SearchResult<T>> results;
|
||||
const Core::State core_state = Core::GetState(system);
|
||||
|
@ -262,10 +260,8 @@ Cheats::NextSearch(const Core::CPUThreadGuard& guard,
|
|||
PowerPC::RequestedAddressSpace address_space,
|
||||
const std::function<bool(const T& new_value, const T& old_value)>& validator)
|
||||
{
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (AchievementManager::GetInstance().IsHardcoreModeActive())
|
||||
return Cheats::SearchErrorCode::DisabledInHardcoreMode;
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
auto& system = guard.GetSystem();
|
||||
std::vector<Cheats::SearchResult<T>> results;
|
||||
const Core::State core_state = Core::GetState(system);
|
||||
|
@ -429,10 +425,8 @@ MakeCompareFunctionForLastValue(Cheats::CompareType op)
|
|||
template <typename T>
|
||||
Cheats::SearchErrorCode Cheats::CheatSearchSession<T>::RunSearch(const Core::CPUThreadGuard& guard)
|
||||
{
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (AchievementManager::GetInstance().IsHardcoreModeActive())
|
||||
return Cheats::SearchErrorCode::DisabledInHardcoreMode;
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
Common::Result<SearchErrorCode, std::vector<SearchResult<T>>> result =
|
||||
Cheats::SearchErrorCode::InvalidParameters;
|
||||
if (m_filter_type == FilterType::CompareAgainstSpecificValue)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
namespace Core
|
||||
{
|
||||
class CPUThreadGuard;
|
||||
};
|
||||
}
|
||||
|
||||
namespace Cheats
|
||||
{
|
||||
|
@ -100,10 +100,8 @@ enum class SearchErrorCode
|
|||
// currently off in the emulated game.
|
||||
VirtualAddressesCurrentlyNotAccessible,
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
// Cheats and memory reading are disabled in RetroAchievements hardcore mode.
|
||||
DisabledInHardcoreMode,
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
};
|
||||
|
||||
// Returns the corresponding DataType enum for the value currently held by the given SearchValue.
|
||||
|
|
|
@ -22,9 +22,10 @@ const Info<bool> RA_UNOFFICIAL_ENABLED{{System::Achievements, "Achievements", "U
|
|||
const Info<bool> RA_ENCORE_ENABLED{{System::Achievements, "Achievements", "EncoreEnabled"}, false};
|
||||
const Info<bool> RA_SPECTATOR_ENABLED{{System::Achievements, "Achievements", "SpectatorEnabled"},
|
||||
false};
|
||||
const Info<bool> RA_DISCORD_PRESENCE_ENABLED{
|
||||
{System::Achievements, "Achievements", "DiscordPresenceEnabled"}, false};
|
||||
const Info<bool> RA_PROGRESS_ENABLED{{System::Achievements, "Achievements", "ProgressEnabled"},
|
||||
false};
|
||||
const Info<bool> RA_BADGES_ENABLED{{System::Achievements, "Achievements", "BadgesEnabled"}, false};
|
||||
} // namespace Config
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
|
|
@ -18,8 +18,8 @@ extern const Info<bool> RA_HARDCORE_ENABLED;
|
|||
extern const Info<bool> RA_UNOFFICIAL_ENABLED;
|
||||
extern const Info<bool> RA_ENCORE_ENABLED;
|
||||
extern const Info<bool> RA_SPECTATOR_ENABLED;
|
||||
extern const Info<bool> RA_DISCORD_PRESENCE_ENABLED;
|
||||
extern const Info<bool> RA_PROGRESS_ENABLED;
|
||||
extern const Info<bool> RA_BADGES_ENABLED;
|
||||
} // namespace Config
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
|
|
@ -749,22 +749,14 @@ bool IsDefaultGCIFolderPathConfigured(ExpansionInterface::Slot slot)
|
|||
|
||||
bool AreCheatsEnabled()
|
||||
{
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
return Config::Get(::Config::MAIN_ENABLE_CHEATS) &&
|
||||
!AchievementManager::GetInstance().IsHardcoreModeActive();
|
||||
#else // USE_RETRO_ACHIEVEMENTS
|
||||
return Config::Get(::Config::MAIN_ENABLE_CHEATS);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
}
|
||||
|
||||
bool IsDebuggingEnabled()
|
||||
{
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
return Config::Get(::Config::MAIN_ENABLE_DEBUGGING) &&
|
||||
!AchievementManager::GetInstance().IsHardcoreModeActive();
|
||||
#else // USE_RETRO_ACHIEVEMENTS
|
||||
return Config::Get(::Config::MAIN_ENABLE_DEBUGGING);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
}
|
||||
|
||||
} // namespace Config
|
||||
|
|
|
@ -15,7 +15,7 @@ const Info<u32> SYSCONF_LANGUAGE{{System::SYSCONF, "IPL", "LNG"},
|
|||
const Info<u32> SYSCONF_COUNTRY{{System::SYSCONF, "IPL", "SADR"}, GetDefaultCountry()};
|
||||
const Info<bool> SYSCONF_WIDESCREEN{{System::SYSCONF, "IPL", "AR"}, true};
|
||||
const Info<bool> SYSCONF_PROGRESSIVE_SCAN{{System::SYSCONF, "IPL", "PGS"}, true};
|
||||
const Info<bool> SYSCONF_PAL60{{System::SYSCONF, "IPL", "E60"}, 0x01};
|
||||
const Info<bool> SYSCONF_PAL60{{System::SYSCONF, "IPL", "E60"}, true};
|
||||
const Info<u32> SYSCONF_SOUND_MODE{{System::SYSCONF, "IPL", "SND"}, 0x01};
|
||||
|
||||
// SYSCONF.BT
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "Common/NandPaths.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/Version.h"
|
||||
#include "Common/Config/Config.h"
|
||||
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "Core/Boot/Boot.h"
|
||||
|
@ -86,6 +87,11 @@ SConfig::~SConfig()
|
|||
SaveSettings();
|
||||
}
|
||||
|
||||
namespace Config
|
||||
{
|
||||
const Info<int> MAIN_CODE_HANDLER{{System::Main, "CodeHandler", "CodeHandlerValue"}, 0};
|
||||
}
|
||||
|
||||
void SConfig::SaveSettings()
|
||||
{
|
||||
NOTICE_LOG_FMT(BOOT, "Saving settings to {}", File::GetUserPath(F_DOLPHINCONFIG_IDX));
|
||||
|
@ -169,11 +175,6 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::stri
|
|||
if (!was_changed)
|
||||
return;
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (game_id != "00000000")
|
||||
AchievementManager::GetInstance().CloseGame();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
if (game_id == "00000000")
|
||||
{
|
||||
m_title_name.clear();
|
||||
|
@ -181,6 +182,8 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::stri
|
|||
return;
|
||||
}
|
||||
|
||||
AchievementManager::GetInstance().CloseGame();
|
||||
|
||||
const Core::TitleDatabase title_database;
|
||||
auto& system = Core::System::GetInstance();
|
||||
const DiscIO::Language language = GetLanguageAdjustedForRegion(system.IsWii(), region);
|
||||
|
@ -188,22 +191,22 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::stri
|
|||
m_title_description = title_database.Describe(m_gametdb_id, language);
|
||||
NOTICE_LOG_FMT(CORE, "Active title: {}", m_title_description);
|
||||
Host_TitleChanged();
|
||||
if (Core::IsRunning(system))
|
||||
{
|
||||
|
||||
const bool is_running_or_starting = Core::IsRunningOrStarting(system);
|
||||
if (is_running_or_starting)
|
||||
Core::UpdateTitle(system);
|
||||
}
|
||||
|
||||
Config::AddLayer(ConfigLoaders::GenerateGlobalGameConfigLoader(game_id, revision));
|
||||
Config::AddLayer(ConfigLoaders::GenerateLocalGameConfigLoader(game_id, revision));
|
||||
|
||||
if (Core::IsRunning(system))
|
||||
if (is_running_or_starting)
|
||||
DolphinAnalytics::Instance().ReportGameStart();
|
||||
}
|
||||
|
||||
void SConfig::OnNewTitleLoad(const Core::CPUThreadGuard& guard)
|
||||
{
|
||||
auto& system = guard.GetSystem();
|
||||
if (!Core::IsRunning(system))
|
||||
if (!Core::IsRunningOrStarting(system))
|
||||
return;
|
||||
|
||||
auto& ppc_symbol_db = system.GetPPCSymbolDB();
|
||||
|
@ -454,7 +457,14 @@ std::string SConfig::GetGameTDBImageRegionCode(bool wii, DiscIO::Region region)
|
|||
switch (region)
|
||||
{
|
||||
case DiscIO::Region::NTSC_J:
|
||||
{
|
||||
// Taiwanese games share the Japanese region code however their title ID ends with 'W'.
|
||||
// GameTDB differentiates the covers using the code "ZH".
|
||||
if (m_game_id.size() >= 4 && m_game_id.at(3) == 'W')
|
||||
return "ZH";
|
||||
|
||||
return "JA";
|
||||
}
|
||||
case DiscIO::Region::NTSC_U:
|
||||
return "US";
|
||||
case DiscIO::Region::NTSC_K:
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Config/Config.h"
|
||||
|
||||
namespace Common
|
||||
{
|
||||
|
@ -33,7 +34,11 @@ enum class Region;
|
|||
struct Partition;
|
||||
class Volume;
|
||||
} // namespace DiscIO
|
||||
|
||||
namespace Config
|
||||
{
|
||||
// Define the configuration option
|
||||
extern const Info<int> MAIN_CODE_HANDLER;
|
||||
} // namespace Config
|
||||
namespace IOS::ES
|
||||
{
|
||||
class TMDReader;
|
||||
|
@ -45,7 +50,7 @@ struct SConfig
|
|||
{
|
||||
// Settings
|
||||
bool bBootToPause = false;
|
||||
|
||||
|
||||
bool bJITNoBlockCache = false;
|
||||
bool bJITNoBlockLinking = false;
|
||||
|
||||
|
|
|
@ -101,10 +101,6 @@ namespace Core
|
|||
static bool s_wants_determinism;
|
||||
|
||||
// Declarations and definitions
|
||||
static bool s_is_stopping = false;
|
||||
static bool s_hardware_initialized = false;
|
||||
static bool s_is_started = false;
|
||||
static Common::Flag s_is_booting;
|
||||
static std::thread s_emu_thread;
|
||||
static std::vector<StateChangedCallbackFunc> s_on_state_changed_callbacks;
|
||||
|
||||
|
@ -114,6 +110,10 @@ static std::atomic<double> s_last_actual_emulation_speed{1.0};
|
|||
static bool s_frame_step = false;
|
||||
static std::atomic<bool> s_stop_frame_step;
|
||||
|
||||
// The value Paused is never stored in this variable. The core is considered to be in
|
||||
// the Paused state if this variable is Running and the CPU reports that it's stepping.
|
||||
static std::atomic<State> s_state = State::Uninitialized;
|
||||
|
||||
#ifdef USE_MEMORYWATCHER
|
||||
static std::unique_ptr<MemoryWatcher> s_memory_watcher;
|
||||
#endif
|
||||
|
@ -190,7 +190,7 @@ std::string StopMessage(bool main_thread, std::string_view message)
|
|||
|
||||
void DisplayMessage(std::string message, int time_in_ms)
|
||||
{
|
||||
if (!IsRunning(Core::System::GetInstance()))
|
||||
if (!IsRunningOrStarting(Core::System::GetInstance()))
|
||||
return;
|
||||
|
||||
// Actually displaying non-ASCII could cause things to go pear-shaped
|
||||
|
@ -202,12 +202,13 @@ void DisplayMessage(std::string message, int time_in_ms)
|
|||
|
||||
bool IsRunning(Core::System& system)
|
||||
{
|
||||
return (GetState(system) != State::Uninitialized || s_hardware_initialized) && !s_is_stopping;
|
||||
return s_state.load() == State::Running;
|
||||
}
|
||||
|
||||
bool IsRunningAndStarted()
|
||||
bool IsRunningOrStarting(Core::System& system)
|
||||
{
|
||||
return s_is_started && !s_is_stopping;
|
||||
const State state = s_state.load();
|
||||
return state == State::Running || state == State::Starting;
|
||||
}
|
||||
|
||||
bool IsCPUThread()
|
||||
|
@ -262,7 +263,7 @@ bool Init(Core::System& system, std::unique_ptr<BootParameters> boot, const Wind
|
|||
g_video_backend->PrepareWindow(prepared_wsi);
|
||||
|
||||
// Start the emu thread
|
||||
s_is_booting.Set();
|
||||
s_state.store(State::Starting);
|
||||
s_emu_thread = std::thread(EmuThread, std::ref(system), std::move(boot), prepared_wsi);
|
||||
return true;
|
||||
}
|
||||
|
@ -281,17 +282,13 @@ static void ResetRumble()
|
|||
// Called from GUI thread
|
||||
void Stop(Core::System& system) // - Hammertime!
|
||||
{
|
||||
if (const State state = GetState(system);
|
||||
state == State::Stopping || state == State::Uninitialized)
|
||||
{
|
||||
const State state = s_state.load();
|
||||
if (state == State::Stopping || state == State::Uninitialized)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().CloseGame();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
s_is_stopping = true;
|
||||
s_state.store(State::Stopping);
|
||||
|
||||
CallOnStateChangedCallbacks(State::Stopping);
|
||||
|
||||
|
@ -356,7 +353,7 @@ static void CPUSetInitialExecutionState(bool force_paused = false)
|
|||
// SetState must be called on the host thread, so we defer it for later.
|
||||
QueueHostJob([force_paused](Core::System& system) {
|
||||
bool paused = SConfig::GetInstance().bBootToPause || force_paused;
|
||||
SetState(system, paused ? State::Paused : State::Running);
|
||||
SetState(system, paused ? State::Paused : State::Running, true, true);
|
||||
Host_UpdateDisasmDialog();
|
||||
Host_UpdateMainFrame();
|
||||
Host_Message(HostMessageID::WMUserCreate);
|
||||
|
@ -396,11 +393,15 @@ static void CpuThread(Core::System& system, const std::optional<std::string>& sa
|
|||
File::Delete(*savestate_path);
|
||||
}
|
||||
|
||||
s_is_started = true;
|
||||
// If s_state is Starting, change it to Running. But if it's already been set to Stopping
|
||||
// by the host thread, don't change it.
|
||||
State expected = State::Starting;
|
||||
s_state.compare_exchange_strong(expected, State::Running);
|
||||
|
||||
{
|
||||
#ifndef _WIN32
|
||||
std::string gdb_socket = Config::Get(Config::MAIN_GDB_SOCKET);
|
||||
if (!gdb_socket.empty())
|
||||
if (!gdb_socket.empty() && !AchievementManager::GetInstance().IsHardcoreModeActive())
|
||||
{
|
||||
GDBStub::InitLocal(gdb_socket.data());
|
||||
CPUSetInitialExecutionState(true);
|
||||
|
@ -409,7 +410,7 @@ static void CpuThread(Core::System& system, const std::optional<std::string>& sa
|
|||
#endif
|
||||
{
|
||||
int gdb_port = Config::Get(Config::MAIN_GDB_PORT);
|
||||
if (gdb_port > 0)
|
||||
if (gdb_port > 0 && !AchievementManager::GetInstance().IsHardcoreModeActive())
|
||||
{
|
||||
GDBStub::Init(gdb_port);
|
||||
CPUSetInitialExecutionState(true);
|
||||
|
@ -428,8 +429,6 @@ static void CpuThread(Core::System& system, const std::optional<std::string>& sa
|
|||
s_memory_watcher.reset();
|
||||
#endif
|
||||
|
||||
s_is_started = false;
|
||||
|
||||
if (exception_handler)
|
||||
EMM::UninstallExceptionHandler();
|
||||
|
||||
|
@ -455,12 +454,15 @@ static void FifoPlayerThread(Core::System& system, const std::optional<std::stri
|
|||
if (auto cpu_core = system.GetFifoPlayer().GetCPUCore())
|
||||
{
|
||||
system.GetPowerPC().InjectExternalCPUCore(cpu_core.get());
|
||||
s_is_started = true;
|
||||
|
||||
// If s_state is Starting, change it to Running. But if it's already been set to Stopping
|
||||
// by the host thread, don't change it.
|
||||
State expected = State::Starting;
|
||||
s_state.compare_exchange_strong(expected, State::Running);
|
||||
|
||||
CPUSetInitialExecutionState();
|
||||
system.GetCPU().Run();
|
||||
|
||||
s_is_started = false;
|
||||
system.GetPowerPC().InjectExternalCPUCore(nullptr);
|
||||
system.GetFifoPlayer().Close();
|
||||
}
|
||||
|
@ -481,10 +483,7 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
|
|||
{
|
||||
CallOnStateChangedCallbacks(State::Starting);
|
||||
Common::ScopeGuard flag_guard{[] {
|
||||
s_is_booting.Clear();
|
||||
s_is_started = false;
|
||||
s_is_stopping = false;
|
||||
s_wants_determinism = false;
|
||||
s_state.store(State::Uninitialized);
|
||||
|
||||
CallOnStateChangedCallbacks(State::Uninitialized);
|
||||
|
||||
|
@ -562,8 +561,6 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
|
|||
NetPlay::NetPlay_RegisterEvents();
|
||||
|
||||
Common::ScopeGuard hw_guard{[&system] {
|
||||
// We must set up this flag before executing HW::Shutdown()
|
||||
s_hardware_initialized = false;
|
||||
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(false, "Shutting down HW"));
|
||||
HW::Shutdown(system);
|
||||
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(false, "HW shutdown"));
|
||||
|
@ -607,10 +604,6 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
|
|||
|
||||
AudioCommon::PostInitSoundStream(system);
|
||||
|
||||
// The hardware is initialized.
|
||||
s_hardware_initialized = true;
|
||||
s_is_booting.Clear();
|
||||
|
||||
// Set execution state to known values (CPU/FIFO/Audio Paused)
|
||||
system.GetCPU().Break();
|
||||
|
||||
|
@ -703,24 +696,32 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
|
|||
|
||||
// Set or get the running state
|
||||
|
||||
void SetState(Core::System& system, State state, bool report_state_change)
|
||||
void SetState(Core::System& system, State state, bool report_state_change,
|
||||
bool initial_execution_state)
|
||||
{
|
||||
// State cannot be controlled until the CPU Thread is operational
|
||||
if (!IsRunningAndStarted())
|
||||
if (s_state.load() != State::Running)
|
||||
return;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case State::Paused:
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (!initial_execution_state && !AchievementManager::GetInstance().CanPause())
|
||||
return;
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
// NOTE: GetState() will return State::Paused immediately, even before anything has
|
||||
// stopped (including the CPU).
|
||||
system.GetCPU().EnableStepping(true); // Break
|
||||
system.GetCPU().SetStepping(true); // Break
|
||||
Wiimote::Pause();
|
||||
ResetRumble();
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().DoIdle();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
break;
|
||||
case State::Running:
|
||||
{
|
||||
system.GetCPU().EnableStepping(false);
|
||||
system.GetCPU().SetStepping(false);
|
||||
Wiimote::Resume();
|
||||
break;
|
||||
}
|
||||
|
@ -737,21 +738,11 @@ void SetState(Core::System& system, State state, bool report_state_change)
|
|||
|
||||
State GetState(Core::System& system)
|
||||
{
|
||||
if (s_is_stopping)
|
||||
return State::Stopping;
|
||||
|
||||
if (s_hardware_initialized)
|
||||
{
|
||||
if (system.GetCPU().IsStepping())
|
||||
return State::Paused;
|
||||
|
||||
return State::Running;
|
||||
}
|
||||
|
||||
if (s_is_booting.IsSet())
|
||||
return State::Starting;
|
||||
|
||||
return State::Uninitialized;
|
||||
const State state = s_state.load();
|
||||
if (state == State::Running && system.GetCPU().IsStepping())
|
||||
return State::Paused;
|
||||
else
|
||||
return state;
|
||||
}
|
||||
|
||||
static std::string GenerateScreenshotFolderPath()
|
||||
|
@ -805,7 +796,7 @@ static bool PauseAndLock(Core::System& system, bool do_lock, bool unpause_on_unl
|
|||
{
|
||||
// WARNING: PauseAndLock is not fully threadsafe so is only valid on the Host Thread
|
||||
|
||||
if (!IsRunningAndStarted())
|
||||
if (!IsRunning(system))
|
||||
return true;
|
||||
|
||||
bool was_unpaused = true;
|
||||
|
@ -833,7 +824,7 @@ static bool PauseAndLock(Core::System& system, bool do_lock, bool unpause_on_unl
|
|||
// The CPU is responsible for managing the Audio and FIFO state so we use its
|
||||
// mechanism to unpause them. If we unpaused the systems above when releasing
|
||||
// the locks then they could call CPU::Break which would require detecting it
|
||||
// and re-pausing with CPU::EnableStepping.
|
||||
// and re-pausing with CPU::SetStepping.
|
||||
was_unpaused = system.GetCPU().PauseAndLock(false, unpause_on_unlock, true);
|
||||
}
|
||||
|
||||
|
@ -911,9 +902,7 @@ void Callback_NewField(Core::System& system)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().DoFrame();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
}
|
||||
|
||||
void UpdateTitle(Core::System& system)
|
||||
|
@ -1034,13 +1023,12 @@ void HostDispatchJobs(Core::System& system)
|
|||
HostJob job = std::move(s_host_jobs_queue.front());
|
||||
s_host_jobs_queue.pop();
|
||||
|
||||
// NOTE: Memory ordering is important. The booting flag needs to be
|
||||
// checked first because the state transition is:
|
||||
// Core::State::Uninitialized: s_is_booting -> s_hardware_initialized
|
||||
// We need to check variables in the same order as the state
|
||||
// transition, otherwise we race and get transient failures.
|
||||
if (!job.run_after_stop && !s_is_booting.IsSet() && !IsRunning(system))
|
||||
continue;
|
||||
if (!job.run_after_stop)
|
||||
{
|
||||
const State state = s_state.load();
|
||||
if (state == State::Stopping || state == State::Uninitialized)
|
||||
continue;
|
||||
}
|
||||
|
||||
guard.unlock();
|
||||
job.job(system);
|
||||
|
@ -1051,13 +1039,11 @@ void HostDispatchJobs(Core::System& system)
|
|||
// NOTE: Host Thread
|
||||
void DoFrameStep(Core::System& system)
|
||||
{
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (AchievementManager::GetInstance().IsHardcoreModeActive())
|
||||
{
|
||||
OSD::AddMessage("Frame stepping is disabled in RetroAchievements hardcore mode");
|
||||
return;
|
||||
}
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
if (GetState(system) == State::Paused)
|
||||
{
|
||||
// if already paused, frame advance for 1 frame
|
||||
|
@ -1076,7 +1062,8 @@ void UpdateInputGate(bool require_focus, bool require_full_focus)
|
|||
{
|
||||
// If the user accepts background input, controls should pass even if an on screen interface is on
|
||||
const bool focus_passes =
|
||||
!require_focus || (Host_RendererHasFocus() && !Host_UIBlocksControllerState());
|
||||
!require_focus ||
|
||||
((Host_RendererHasFocus() || Host_TASInputHasFocus()) && !Host_UIBlocksControllerState());
|
||||
// Ignore full focus if we don't require basic focus
|
||||
const bool full_focus_passes =
|
||||
!require_focus || !require_full_focus || (focus_passes && Host_RendererHasFullFocus());
|
||||
|
@ -1096,4 +1083,31 @@ CPUThreadGuard::~CPUThreadGuard()
|
|||
PauseAndLock(m_system, false, m_was_unpaused);
|
||||
}
|
||||
|
||||
static GameName mGameBeingPlayed = GameName::UnknownGame;
|
||||
const std::map<std::string, GameName> mGameMap = {{"GMPE01", GameName::MarioParty4},
|
||||
{"GP5E01", GameName::MarioParty5},
|
||||
{"GP6E01", GameName::MarioParty6},
|
||||
{"GP7E01", GameName::MarioParty7},
|
||||
{"RM8E01", GameName::MarioParty8}};
|
||||
|
||||
std::optional<std::pair<u32,u32>> getGameFreeMemory()
|
||||
{
|
||||
switch (mGameBeingPlayed) {
|
||||
case GameName::MarioParty4:
|
||||
return std::nullopt;
|
||||
case GameName::MarioParty5:
|
||||
return std::make_pair(0x801A811C, 0x801A9B5C);
|
||||
case GameName::MarioParty6:
|
||||
return std::make_pair(0x80213974, 0x80216014);
|
||||
case GameName::MarioParty7:
|
||||
return std::make_pair(0x8023CF6C, 0x8023F9D0);
|
||||
case GameName::MarioParty8:
|
||||
return std::make_pair(0x802D5100, 0x802D9500);
|
||||
case GameName::UnknownGame:
|
||||
return std::nullopt;
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
|
|
@ -135,15 +135,16 @@ void UndeclareAsHostThread();
|
|||
std::string StopMessage(bool main_thread, std::string_view message);
|
||||
|
||||
bool IsRunning(Core::System& system);
|
||||
bool IsRunningAndStarted(); // is running and the CPU loop has been entered
|
||||
bool IsCPUThread(); // this tells us whether we are the CPU thread.
|
||||
bool IsRunningOrStarting(Core::System& system);
|
||||
bool IsCPUThread(); // this tells us whether we are the CPU thread.
|
||||
bool IsGPUThread();
|
||||
bool IsHostThread();
|
||||
|
||||
bool WantsDeterminism();
|
||||
|
||||
// [NOT THREADSAFE] For use by Host only
|
||||
void SetState(Core::System& system, State state, bool report_state_change = true);
|
||||
void SetState(Core::System& system, State state, bool report_state_change = true,
|
||||
bool initial_execution_state = false);
|
||||
State GetState(Core::System& system);
|
||||
|
||||
void SaveScreenShot();
|
||||
|
@ -191,4 +192,14 @@ void UpdateInputGate(bool require_focus, bool require_full_focus = false);
|
|||
|
||||
void UpdateTitle(Core::System& system);
|
||||
|
||||
enum class GameName : u8 {
|
||||
UnknownGame = 0,
|
||||
MarioParty4 = 1,
|
||||
MarioParty5 = 2,
|
||||
MarioParty6 = 3,
|
||||
MarioParty7 = 4,
|
||||
MarioParty8 = 5
|
||||
};
|
||||
|
||||
std::optional<std::pair<u32,u32>> getGameFreeMemory();
|
||||
} // namespace Core
|
||||
|
|
|
@ -73,7 +73,7 @@ EventType* CoreTimingManager::RegisterEvent(const std::string& name, TimedCallba
|
|||
{
|
||||
// check for existing type with same name.
|
||||
// we want event type names to remain unique so that we can use them for serialization.
|
||||
ASSERT_MSG(POWERPC, m_event_types.find(name) == m_event_types.end(),
|
||||
ASSERT_MSG(POWERPC, !m_event_types.contains(name),
|
||||
"CoreTiming Event \"{}\" is already registered. Events should only be registered "
|
||||
"during Init to avoid breaking save states.",
|
||||
name);
|
||||
|
@ -138,7 +138,6 @@ void CoreTimingManager::RefreshConfig()
|
|||
|
||||
m_max_variance = std::chrono::duration_cast<DT>(DT_ms(Config::Get(Config::MAIN_TIMING_VARIANCE)));
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (AchievementManager::GetInstance().IsHardcoreModeActive() &&
|
||||
Config::Get(Config::MAIN_EMULATION_SPEED) < 1.0f &&
|
||||
Config::Get(Config::MAIN_EMULATION_SPEED) > 0.0f)
|
||||
|
@ -147,7 +146,6 @@ void CoreTimingManager::RefreshConfig()
|
|||
m_emulation_speed = 1.0f;
|
||||
OSD::AddMessage("Minimum speed is 100% in Hardcore Mode");
|
||||
}
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
m_emulation_speed = Config::Get(Config::MAIN_EMULATION_SPEED);
|
||||
}
|
||||
|
@ -305,13 +303,12 @@ void CoreTimingManager::ScheduleExternalEvent(u64 timepoint, EventType* event_ty
|
|||
|
||||
void CoreTimingManager::RemoveEvent(EventType* event_type)
|
||||
{
|
||||
auto itr = std::remove_if(m_event_queue.begin(), m_event_queue.end(),
|
||||
[&](const Event& e) { return e.type == event_type; });
|
||||
const size_t erased =
|
||||
std::erase_if(m_event_queue, [&](const Event& e) { return e.type == event_type; });
|
||||
|
||||
// Removing random items breaks the invariant so we have to re-establish it.
|
||||
if (itr != m_event_queue.end())
|
||||
if (erased != 0)
|
||||
{
|
||||
m_event_queue.erase(itr, m_event_queue.end());
|
||||
std::make_heap(m_event_queue.begin(), m_event_queue.end(), std::greater<Event>());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -142,7 +142,7 @@ public:
|
|||
m_collection_pf.size();
|
||||
}
|
||||
std::size_t GetBlacklistSize() const { return m_blacklist_size; }
|
||||
Phase GetRecordingPhase() const { return m_recording_phase; };
|
||||
Phase GetRecordingPhase() const { return m_recording_phase; }
|
||||
|
||||
// An empty selection in reduction mode can't be reconstructed when loading from a file.
|
||||
bool CanSave() const { return !(m_recording_phase == Phase::Reduction && m_selection.empty()); }
|
||||
|
|
|
@ -189,7 +189,6 @@ AutoStepResults CodeTrace::AutoStepping(const Core::CPUThreadGuard& guard, bool
|
|||
stop_condition = HitType::ACTIVE;
|
||||
|
||||
auto& power_pc = guard.GetSystem().GetPowerPC();
|
||||
power_pc.GetBreakPoints().ClearAllTemporary();
|
||||
using clock = std::chrono::steady_clock;
|
||||
clock::time_point timeout = clock::now() + std::chrono::seconds(4);
|
||||
|
||||
|
|
|
@ -73,8 +73,8 @@ public:
|
|||
}
|
||||
virtual bool IsAlive() const { return true; }
|
||||
virtual bool IsBreakpoint(u32 /*address*/) const { return false; }
|
||||
virtual void SetBreakpoint(u32 /*address*/) {}
|
||||
virtual void ClearBreakpoint(u32 /*address*/) {}
|
||||
virtual void AddBreakpoint(u32 /*address*/) {}
|
||||
virtual void RemoveBreakpoint(u32 /*address*/) {}
|
||||
virtual void ClearAllBreakpoints() {}
|
||||
virtual void ToggleBreakpoint(u32 /*address*/) {}
|
||||
virtual void ClearAllMemChecks() {}
|
||||
|
@ -99,7 +99,7 @@ public:
|
|||
virtual u32 GetPC() const { return 0; }
|
||||
virtual void SetPC(u32 /*address*/) {}
|
||||
virtual void Step() {}
|
||||
virtual void RunToBreakpoint() {}
|
||||
virtual void RunTo(u32 /*address*/) {}
|
||||
virtual u32 GetColor(const CPUThreadGuard* /*guard*/, u32 /*address*/) const
|
||||
{
|
||||
return 0xFFFFFFFF;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
namespace Core
|
||||
{
|
||||
class CPUThreadGuard;
|
||||
};
|
||||
}
|
||||
|
||||
namespace Core::Debug
|
||||
{
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/Debugger/OSThread.h"
|
||||
#include "Core/HW/CPU.h"
|
||||
#include "Core/HW/DSP.h"
|
||||
#include "Core/PatchEngine.h"
|
||||
#include "Core/PowerPC/MMU.h"
|
||||
|
@ -30,10 +31,9 @@
|
|||
void ApplyMemoryPatch(const Core::CPUThreadGuard& guard, Common::Debug::MemoryPatch& patch,
|
||||
bool store_existing_value)
|
||||
{
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (AchievementManager::GetInstance().IsHardcoreModeActive())
|
||||
return;
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
if (patch.value.empty())
|
||||
return;
|
||||
|
||||
|
@ -350,7 +350,7 @@ u32 PPCDebugInterface::ReadInstruction(const Core::CPUThreadGuard& guard, u32 ad
|
|||
|
||||
bool PPCDebugInterface::IsAlive() const
|
||||
{
|
||||
return Core::IsRunningAndStarted();
|
||||
return Core::IsRunning(m_system);
|
||||
}
|
||||
|
||||
bool PPCDebugInterface::IsBreakpoint(u32 address) const
|
||||
|
@ -358,12 +358,12 @@ bool PPCDebugInterface::IsBreakpoint(u32 address) const
|
|||
return m_system.GetPowerPC().GetBreakPoints().IsAddressBreakPoint(address);
|
||||
}
|
||||
|
||||
void PPCDebugInterface::SetBreakpoint(u32 address)
|
||||
void PPCDebugInterface::AddBreakpoint(u32 address)
|
||||
{
|
||||
m_system.GetPowerPC().GetBreakPoints().Add(address);
|
||||
}
|
||||
|
||||
void PPCDebugInterface::ClearBreakpoint(u32 address)
|
||||
void PPCDebugInterface::RemoveBreakpoint(u32 address)
|
||||
{
|
||||
m_system.GetPowerPC().GetBreakPoints().Remove(address);
|
||||
}
|
||||
|
@ -375,11 +375,7 @@ void PPCDebugInterface::ClearAllBreakpoints()
|
|||
|
||||
void PPCDebugInterface::ToggleBreakpoint(u32 address)
|
||||
{
|
||||
auto& breakpoints = m_system.GetPowerPC().GetBreakPoints();
|
||||
if (breakpoints.IsAddressBreakPoint(address))
|
||||
breakpoints.Remove(address);
|
||||
else
|
||||
breakpoints.Add(address);
|
||||
m_system.GetPowerPC().GetBreakPoints().ToggleBreakPoint(address);
|
||||
}
|
||||
|
||||
void PPCDebugInterface::ClearAllMemChecks()
|
||||
|
@ -507,8 +503,11 @@ void PPCDebugInterface::SetPC(u32 address)
|
|||
m_system.GetPPCState().pc = address;
|
||||
}
|
||||
|
||||
void PPCDebugInterface::RunToBreakpoint()
|
||||
void PPCDebugInterface::RunTo(u32 address)
|
||||
{
|
||||
auto& breakpoints = m_system.GetPowerPC().GetBreakPoints();
|
||||
breakpoints.SetTemporary(address);
|
||||
m_system.GetCPU().SetStepping(false);
|
||||
}
|
||||
|
||||
std::shared_ptr<Core::NetworkCaptureLogger> PPCDebugInterface::NetworkLogger()
|
||||
|
|
|
@ -80,8 +80,8 @@ public:
|
|||
u32 address) const override;
|
||||
bool IsAlive() const override;
|
||||
bool IsBreakpoint(u32 address) const override;
|
||||
void SetBreakpoint(u32 address) override;
|
||||
void ClearBreakpoint(u32 address) override;
|
||||
void AddBreakpoint(u32 address) override;
|
||||
void RemoveBreakpoint(u32 address) override;
|
||||
void ClearAllBreakpoints() override;
|
||||
void ToggleBreakpoint(u32 address) override;
|
||||
void ClearAllMemChecks() override;
|
||||
|
@ -100,7 +100,7 @@ public:
|
|||
u32 GetPC() const override;
|
||||
void SetPC(u32 address) override;
|
||||
void Step() override {}
|
||||
void RunToBreakpoint() override;
|
||||
void RunTo(u32 address) override;
|
||||
u32 GetColor(const Core::CPUThreadGuard* guard, u32 address) const override;
|
||||
std::string_view GetDescription(u32 address) const override;
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ class PPCSymbolDB;
|
|||
namespace Core
|
||||
{
|
||||
class CPUThreadGuard;
|
||||
};
|
||||
}
|
||||
|
||||
struct RSOEntry
|
||||
{
|
||||
|
|
|
@ -134,6 +134,9 @@ public:
|
|||
// to once per game run.
|
||||
void ReportGameQuirk(GameQuirk quirk);
|
||||
|
||||
// Get the base builder for building a report
|
||||
const Common::AnalyticsReportBuilder& BaseBuilder() const { return m_base_builder; }
|
||||
|
||||
struct PerformanceSample
|
||||
{
|
||||
double speed_ratio; // See SystemTimers::GetEstimatedEmulationPerformance().
|
||||
|
|
|
@ -228,7 +228,7 @@ public:
|
|||
IsPlayingBackFifologWithBrokenEFBCopies = m_parent->m_File->HasBrokenEFBCopies();
|
||||
// Without this call, we deadlock in initialization in dual core, as the FIFO is disabled and
|
||||
// thus ClearEfb()'s call to WaitForGPUInactive() never returns
|
||||
m_parent->m_system.GetCPU().EnableStepping(false);
|
||||
m_parent->m_system.GetCPU().SetStepping(false);
|
||||
|
||||
m_parent->m_CurrentFrame = m_parent->m_FrameRangeStart;
|
||||
m_parent->LoadMemory();
|
||||
|
@ -243,7 +243,7 @@ public:
|
|||
void SingleStep() override
|
||||
{
|
||||
// NOTE: AdvanceFrame() will get stuck forever in Dual Core because the FIFO
|
||||
// is disabled by CPU::EnableStepping(true) so the frame never gets displayed.
|
||||
// is disabled by CPU::SetStepping(true) so the frame never gets displayed.
|
||||
PanicAlertFmtT("Cannot SingleStep the FIFO. Use Frame Advance instead.");
|
||||
}
|
||||
|
||||
|
|
|
@ -46,11 +46,7 @@ void Config::Refresh()
|
|||
}
|
||||
|
||||
camera_config.control_type = ::Config::Get(::Config::FL1_CONTROL_TYPE);
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
enabled = ::Config::Get(::Config::FREE_LOOK_ENABLED) &&
|
||||
!AchievementManager::GetInstance().IsHardcoreModeActive();
|
||||
#else // USE_RETRO_ACHIEVEMENTS
|
||||
enabled = ::Config::Get(::Config::FREE_LOOK_ENABLED);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
}
|
||||
} // namespace FreeLook
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
#include "Core/Core.h"
|
||||
#include "Core/PowerPC/MMU.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
#include "ConfigManager.h"
|
||||
#include "Config/MainSettings.h"
|
||||
#include "VideoCommon/OnScreenDisplay.h"
|
||||
|
||||
#include "Core/System.h"
|
||||
|
||||
namespace Gecko
|
||||
|
@ -116,15 +120,42 @@ std::vector<GeckoCode> SetAndReturnActiveCodes(std::span<const GeckoCode> gcodes
|
|||
return s_active_codes;
|
||||
}
|
||||
|
||||
const char* GetGeckoCodeHandlerPath()
|
||||
{
|
||||
int code_handler_value = Config::Get(Config::MAIN_CODE_HANDLER);
|
||||
switch (code_handler_value)
|
||||
{
|
||||
case 0:
|
||||
return GECKO_CODE_HANDLER; // Dolphin (Stock)
|
||||
case 1:
|
||||
return GECKO_CODE_HANDLER_MPN; // MPN (Extended)
|
||||
case 2:
|
||||
return GECKO_CODE_HANDLER_MPN_SUPER; // MPN (Super Extended)
|
||||
default:
|
||||
return GECKO_CODE_HANDLER; // Fallback
|
||||
}
|
||||
}
|
||||
|
||||
bool IsGeckoCodeHandlerEnabled()
|
||||
{
|
||||
int code_handler_value = Config::Get(Config::MAIN_CODE_HANDLER);
|
||||
return code_handler_value == 1 || code_handler_value == 2;
|
||||
}
|
||||
|
||||
bool IsGeckoCodeHandlerSUPER()
|
||||
{
|
||||
int code_handler_value = Config::Get(Config::MAIN_CODE_HANDLER);
|
||||
return code_handler_value == 2;
|
||||
}
|
||||
|
||||
// Requires s_active_codes_lock
|
||||
// NOTE: Refer to "codehandleronly.s" from Gecko OS.
|
||||
static Installation InstallCodeHandlerLocked(const Core::CPUThreadGuard& guard)
|
||||
{
|
||||
std::string data;
|
||||
if (!File::ReadFileToString(File::GetSysDirectory() + GECKO_CODE_HANDLER, data))
|
||||
if (!File::ReadFileToString(File::GetSysDirectory() + GetGeckoCodeHandlerPath(), data))
|
||||
{
|
||||
ERROR_LOG_FMT(ACTIONREPLAY,
|
||||
"Could not enable cheats because " GECKO_CODE_HANDLER " was missing.");
|
||||
ERROR_LOG_FMT(ACTIONREPLAY, "Could not enable cheats because the selected codehandler was missing.");
|
||||
return Installation::Failed;
|
||||
}
|
||||
|
||||
|
@ -156,9 +187,47 @@ static Installation InstallCodeHandlerLocked(const Core::CPUThreadGuard& guard)
|
|||
}
|
||||
}
|
||||
|
||||
const u32 codelist_base_address =
|
||||
INSTALLER_BASE_ADDRESS + static_cast<u32>(data.size()) - CODE_SIZE;
|
||||
const u32 codelist_end_address = INSTALLER_END_ADDRESS;
|
||||
const bool is_mpn_handler_and_game_id_rm8e01 =
|
||||
IsGeckoCodeHandlerSUPER() && (SConfig::GetInstance().GetGameID() == "RM8E01");
|
||||
//const bool is_mpn_handler_and_game_id_gp7e01 =
|
||||
// IsGeckoCodeHandlerSUPER() && (SConfig::GetInstance().GetGameID() == "GP7E01");
|
||||
const bool is_mpn_handler_and_game_id_gp6e01 =
|
||||
IsGeckoCodeHandlerSUPER() && (SConfig::GetInstance().GetGameID() == "GP6E01");
|
||||
const bool is_mpn_handler_and_game_id_gp5e01 =
|
||||
IsGeckoCodeHandlerSUPER() && (SConfig::GetInstance().GetGameID() == "GP5E01");
|
||||
const bool is_mpn_handler_and_game_id_gmpe01 =
|
||||
IsGeckoCodeHandlerSUPER() && (SConfig::GetInstance().GetGameID() == "GMPE01");
|
||||
|
||||
u32 codelist_base_address =
|
||||
is_mpn_handler_and_game_id_rm8e01 ? INSTALLER_BASE_ADDRESS_MP8 :
|
||||
//is_mpn_handler_and_game_id_gp7e01 ? INSTALLER_BASE_ADDRESS_MP7 :
|
||||
is_mpn_handler_and_game_id_gp6e01 ? INSTALLER_BASE_ADDRESS_MP6 :
|
||||
is_mpn_handler_and_game_id_gp5e01 ? INSTALLER_BASE_ADDRESS_MP5 :
|
||||
is_mpn_handler_and_game_id_gmpe01 ? INSTALLER_BASE_ADDRESS_MP4 :
|
||||
INSTALLER_BASE_ADDRESS + static_cast<u32>(data.size()) -
|
||||
CODE_SIZE;
|
||||
|
||||
u32 codelist_end_address = is_mpn_handler_and_game_id_rm8e01 ? INSTALLER_END_ADDRESS_MP8 :
|
||||
// is_mpn_handler_and_game_id_gp7e01 ? INSTALLER_END_ADDRESS_MP7 :
|
||||
is_mpn_handler_and_game_id_gp6e01 ? INSTALLER_END_ADDRESS_MP6 :
|
||||
is_mpn_handler_and_game_id_gp5e01 ? INSTALLER_END_ADDRESS_MP5 :
|
||||
is_mpn_handler_and_game_id_gmpe01 ? INSTALLER_END_ADDRESS_MP4 :
|
||||
INSTALLER_END_ADDRESS;
|
||||
|
||||
if (is_mpn_handler_and_game_id_rm8e01 || // is_mpn_handler_and_game_id_gp7e01 ||
|
||||
is_mpn_handler_and_game_id_gp6e01 || is_mpn_handler_and_game_id_gp5e01 ||
|
||||
is_mpn_handler_and_game_id_gmpe01)
|
||||
{
|
||||
// Move Gecko code handler to the free mem region
|
||||
for (u32 addr = codelist_base_address; addr < codelist_end_address; addr += 4)
|
||||
{
|
||||
PowerPC::MMU::HostWrite_U32(guard, 0x00000000, addr);
|
||||
}
|
||||
PowerPC::MMU::HostWrite_U32(guard, ((codelist_base_address & 0xFFFF0000) >> 16) + 0x3DE00000,
|
||||
0x800018F8);
|
||||
PowerPC::MMU::HostWrite_U32(guard, (codelist_base_address & 0x0000FFFF) + 0x61EF0000,
|
||||
0x800018FC);
|
||||
}
|
||||
|
||||
// Write a magic value to 'gameid' (codehandleronly does not actually read this).
|
||||
// This value will be read back and modified over time by HLE_Misc::GeckoCodeHandlerICacheFlush.
|
||||
|
@ -184,6 +253,12 @@ static Installation InstallCodeHandlerLocked(const Core::CPUThreadGuard& guard)
|
|||
"not write: \"{}\". Need {} bytes, only {} remain.",
|
||||
active_code.name, active_code.codes.size() * CODE_SIZE,
|
||||
end_address - next_address);
|
||||
OSD::AddMessage(
|
||||
fmt::format("Too many GeckoCodes! Ran out of storage space in Game RAM. Could "
|
||||
"not write: \"{}\". Need {} bytes, only {} remain.",
|
||||
active_code.name, active_code.codes.size() * CODE_SIZE,
|
||||
end_address - next_address),
|
||||
OSD::Duration::VERY_LONG);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -198,6 +273,9 @@ static Installation InstallCodeHandlerLocked(const Core::CPUThreadGuard& guard)
|
|||
WARN_LOG_FMT(ACTIONREPLAY, "GeckoCodes: Using {} of {} bytes", next_address - start_address,
|
||||
end_address - start_address);
|
||||
|
||||
|
||||
OSD::AddMessage(fmt::format("Gecko Codes: Using {} of {} bytes", next_address - start_address, end_address - start_address));
|
||||
|
||||
// Stop code. Tells the handler that this is the end of the list.
|
||||
PowerPC::MMU::HostWrite_U32(guard, 0xF0000000, next_address);
|
||||
PowerPC::MMU::HostWrite_U32(guard, 0x00000000, next_address + 4);
|
||||
|
@ -208,11 +286,9 @@ static Installation InstallCodeHandlerLocked(const Core::CPUThreadGuard& guard)
|
|||
|
||||
// Invalidate the icache and any asm codes
|
||||
auto& ppc_state = guard.GetSystem().GetPPCState();
|
||||
auto& memory = guard.GetSystem().GetMemory();
|
||||
auto& jit_interface = guard.GetSystem().GetJitInterface();
|
||||
for (u32 j = 0; j < (INSTALLER_END_ADDRESS - INSTALLER_BASE_ADDRESS); j += 32)
|
||||
{
|
||||
ppc_state.iCache.Invalidate(memory, jit_interface, INSTALLER_BASE_ADDRESS + j);
|
||||
ppc_state.iCache.Invalidate(INSTALLER_BASE_ADDRESS + j);
|
||||
}
|
||||
return Installation::Installed;
|
||||
}
|
||||
|
|