25 changed files with 949 additions and 1759 deletions
-
197src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.java
-
140src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/MotionAlertDialog.java
-
10src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.java
-
382src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputBindingSetting.java
-
12src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/PremiumHeader.java
-
59src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/PremiumSingleChoiceSetting.java
-
11src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.java
-
125src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.java
-
198src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.java
-
55src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputBindingSettingViewHolder.java
-
57src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/PremiumViewHolder.java
-
14src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.java
-
102src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.java
-
1src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.java
-
54src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java
-
11src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java
-
215src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BillingManager.java
-
3src/android/app/src/main/jni/CMakeLists.txt
-
284src/android/app/src/main/jni/config.cpp
-
37src/android/app/src/main/jni/config.h
-
499src/android/app/src/main/jni/default_ini.h
-
12src/android/app/src/main/jni/native.cpp
-
5src/android/app/src/main/res/menu/menu_game_grid.xml
-
112src/android/app/src/main/res/values/arrays.xml
-
113src/android/app/src/main/res/values/strings.xml
@ -1,140 +0,0 @@ |
|||||
package org.yuzu.yuzu_emu.dialogs; |
|
||||
|
|
||||
import android.content.Context; |
|
||||
import android.view.InputDevice; |
|
||||
import android.view.KeyEvent; |
|
||||
import android.view.MotionEvent; |
|
||||
|
|
||||
import androidx.annotation.NonNull; |
|
||||
import androidx.appcompat.app.AlertDialog; |
|
||||
|
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.InputBindingSetting; |
|
||||
import org.yuzu.yuzu_emu.utils.Log; |
|
||||
|
|
||||
import java.util.ArrayList; |
|
||||
import java.util.List; |
|
||||
|
|
||||
/** |
|
||||
* {@link AlertDialog} derivative that listens for |
|
||||
* motion events from controllers and joysticks. |
|
||||
*/ |
|
||||
public final class MotionAlertDialog extends AlertDialog { |
|
||||
// The selected input preference |
|
||||
private final InputBindingSetting setting; |
|
||||
private final ArrayList<Float> mPreviousValues = new ArrayList<>(); |
|
||||
private int mPrevDeviceId = 0; |
|
||||
private boolean mWaitingForEvent = true; |
|
||||
|
|
||||
/** |
|
||||
* Constructor |
|
||||
* |
|
||||
* @param context The current {@link Context}. |
|
||||
* @param setting The Preference to show this dialog for. |
|
||||
*/ |
|
||||
public MotionAlertDialog(Context context, InputBindingSetting setting) { |
|
||||
super(context); |
|
||||
|
|
||||
this.setting = setting; |
|
||||
} |
|
||||
|
|
||||
public boolean onKeyEvent(int keyCode, KeyEvent event) { |
|
||||
Log.debug("[MotionAlertDialog] Received key event: " + event.getAction()); |
|
||||
switch (event.getAction()) { |
|
||||
case KeyEvent.ACTION_UP: |
|
||||
setting.onKeyInput(event); |
|
||||
dismiss(); |
|
||||
// Even if we ignore the key, we still consume it. Thus return true regardless. |
|
||||
return true; |
|
||||
|
|
||||
default: |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public boolean onKeyLongPress(int keyCode, @NonNull KeyEvent event) { |
|
||||
return super.onKeyLongPress(keyCode, event); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public boolean dispatchKeyEvent(KeyEvent event) { |
|
||||
// Handle this key if we care about it, otherwise pass it down the framework |
|
||||
return onKeyEvent(event.getKeyCode(), event) || super.dispatchKeyEvent(event); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public boolean dispatchGenericMotionEvent(@NonNull MotionEvent event) { |
|
||||
// Handle this event if we care about it, otherwise pass it down the framework |
|
||||
return onMotionEvent(event) || super.dispatchGenericMotionEvent(event); |
|
||||
} |
|
||||
|
|
||||
private boolean onMotionEvent(MotionEvent event) { |
|
||||
if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0) |
|
||||
return false; |
|
||||
if (event.getAction() != MotionEvent.ACTION_MOVE) |
|
||||
return false; |
|
||||
|
|
||||
InputDevice input = event.getDevice(); |
|
||||
|
|
||||
List<InputDevice.MotionRange> motionRanges = input.getMotionRanges(); |
|
||||
|
|
||||
if (input.getId() != mPrevDeviceId) { |
|
||||
mPreviousValues.clear(); |
|
||||
} |
|
||||
mPrevDeviceId = input.getId(); |
|
||||
boolean firstEvent = mPreviousValues.isEmpty(); |
|
||||
|
|
||||
int numMovedAxis = 0; |
|
||||
float axisMoveValue = 0.0f; |
|
||||
InputDevice.MotionRange lastMovedRange = null; |
|
||||
char lastMovedDir = '?'; |
|
||||
if (mWaitingForEvent) { |
|
||||
for (int i = 0; i < motionRanges.size(); i++) { |
|
||||
InputDevice.MotionRange range = motionRanges.get(i); |
|
||||
int axis = range.getAxis(); |
|
||||
float origValue = event.getAxisValue(axis); |
|
||||
float value = origValue;//ControllerMappingHelper.scaleAxis(input, axis, origValue); |
|
||||
if (firstEvent) { |
|
||||
mPreviousValues.add(value); |
|
||||
} else { |
|
||||
float previousValue = mPreviousValues.get(i); |
|
||||
|
|
||||
// Only handle the axes that are not neutral (more than 0.5) |
|
||||
// but ignore any axis that has a constant value (e.g. always 1) |
|
||||
if (Math.abs(value) > 0.5f && value != previousValue) { |
|
||||
// It is common to have multiple axes with the same physical input. For example, |
|
||||
// shoulder butters are provided as both AXIS_LTRIGGER and AXIS_BRAKE. |
|
||||
// To handle this, we ignore an axis motion that's the exact same as a motion |
|
||||
// we already saw. This way, we ignore axes with two names, but catch the case |
|
||||
// where a joystick is moved in two directions. |
|
||||
// ref: bottom of https://developer.android.com/training/game-controllers/controller-input.html |
|
||||
if (value != axisMoveValue) { |
|
||||
axisMoveValue = value; |
|
||||
numMovedAxis++; |
|
||||
lastMovedRange = range; |
|
||||
lastMovedDir = value < 0.0f ? '-' : '+'; |
|
||||
} |
|
||||
} |
|
||||
// Special case for d-pads (axis value jumps between 0 and 1 without any values |
|
||||
// in between). Without this, the user would need to press the d-pad twice |
|
||||
// due to the first press being caught by the "if (firstEvent)" case further up. |
|
||||
else if (Math.abs(value) < 0.25f && Math.abs(previousValue) > 0.75f) { |
|
||||
numMovedAxis++; |
|
||||
lastMovedRange = range; |
|
||||
lastMovedDir = previousValue < 0.0f ? '-' : '+'; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
mPreviousValues.set(i, value); |
|
||||
} |
|
||||
|
|
||||
// If only one axis moved, that's the winner. |
|
||||
if (numMovedAxis == 1) { |
|
||||
mWaitingForEvent = false; |
|
||||
setting.onMotionInput(input, lastMovedRange, lastMovedDir); |
|
||||
dismiss(); |
|
||||
} |
|
||||
} |
|
||||
return true; |
|
||||
} |
|
||||
} |
|
||||
@ -1,382 +0,0 @@ |
|||||
package org.yuzu.yuzu_emu.features.settings.model.view; |
|
||||
|
|
||||
import android.content.SharedPreferences; |
|
||||
import android.preference.PreferenceManager; |
|
||||
import android.view.InputDevice; |
|
||||
import android.view.KeyEvent; |
|
||||
import android.widget.Toast; |
|
||||
|
|
||||
import org.yuzu.yuzu_emu.YuzuApplication; |
|
||||
import org.yuzu.yuzu_emu.NativeLibrary; |
|
||||
import org.yuzu.yuzu_emu.R; |
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Setting; |
|
||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting; |
|
||||
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile; |
|
||||
|
|
||||
public final class InputBindingSetting extends SettingsItem { |
|
||||
private static final String INPUT_MAPPING_PREFIX = "InputMapping"; |
|
||||
|
|
||||
public InputBindingSetting(String key, String section, int titleId, Setting setting) { |
|
||||
super(key, section, setting, titleId, 0); |
|
||||
} |
|
||||
|
|
||||
public String getValue() { |
|
||||
if (getSetting() == null) { |
|
||||
return ""; |
|
||||
} |
|
||||
|
|
||||
StringSetting setting = (StringSetting) getSetting(); |
|
||||
return setting.getValue(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Returns true if this key is for the 3DS Circle Pad |
|
||||
*/ |
|
||||
private boolean IsCirclePad() { |
|
||||
switch (getKey()) { |
|
||||
case SettingsFile.KEY_CIRCLEPAD_AXIS_HORIZONTAL: |
|
||||
case SettingsFile.KEY_CIRCLEPAD_AXIS_VERTICAL: |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Returns true if this key is for a horizontal axis for a 3DS analog stick or D-pad |
|
||||
*/ |
|
||||
public boolean IsHorizontalOrientation() { |
|
||||
switch (getKey()) { |
|
||||
case SettingsFile.KEY_CIRCLEPAD_AXIS_HORIZONTAL: |
|
||||
case SettingsFile.KEY_CSTICK_AXIS_HORIZONTAL: |
|
||||
case SettingsFile.KEY_DPAD_AXIS_HORIZONTAL: |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Returns true if this key is for the 3DS C-Stick |
|
||||
*/ |
|
||||
private boolean IsCStick() { |
|
||||
switch (getKey()) { |
|
||||
case SettingsFile.KEY_CSTICK_AXIS_HORIZONTAL: |
|
||||
case SettingsFile.KEY_CSTICK_AXIS_VERTICAL: |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Returns true if this key is for the 3DS D-Pad |
|
||||
*/ |
|
||||
private boolean IsDPad() { |
|
||||
switch (getKey()) { |
|
||||
case SettingsFile.KEY_DPAD_AXIS_HORIZONTAL: |
|
||||
case SettingsFile.KEY_DPAD_AXIS_VERTICAL: |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Returns true if this key is for the 3DS L/R or ZL/ZR buttons. Note, these are not real |
|
||||
* triggers on the 3DS, but we support them as such on a physical gamepad. |
|
||||
*/ |
|
||||
public boolean IsTrigger() { |
|
||||
switch (getKey()) { |
|
||||
case SettingsFile.KEY_BUTTON_L: |
|
||||
case SettingsFile.KEY_BUTTON_R: |
|
||||
case SettingsFile.KEY_BUTTON_ZL: |
|
||||
case SettingsFile.KEY_BUTTON_ZR: |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Returns true if a gamepad axis can be used to map this key. |
|
||||
*/ |
|
||||
public boolean IsAxisMappingSupported() { |
|
||||
return IsCirclePad() || IsCStick() || IsDPad() || IsTrigger(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Returns true if a gamepad button can be used to map this key. |
|
||||
*/ |
|
||||
private boolean IsButtonMappingSupported() { |
|
||||
return !IsAxisMappingSupported() || IsTrigger(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Returns the yuzu button code for the settings key. |
|
||||
*/ |
|
||||
private int getButtonCode() { |
|
||||
switch (getKey()) { |
|
||||
case SettingsFile.KEY_BUTTON_A: |
|
||||
return NativeLibrary.ButtonType.BUTTON_A; |
|
||||
case SettingsFile.KEY_BUTTON_B: |
|
||||
return NativeLibrary.ButtonType.BUTTON_B; |
|
||||
case SettingsFile.KEY_BUTTON_X: |
|
||||
return NativeLibrary.ButtonType.BUTTON_X; |
|
||||
case SettingsFile.KEY_BUTTON_Y: |
|
||||
return NativeLibrary.ButtonType.BUTTON_Y; |
|
||||
case SettingsFile.KEY_BUTTON_L: |
|
||||
return NativeLibrary.ButtonType.TRIGGER_L; |
|
||||
case SettingsFile.KEY_BUTTON_R: |
|
||||
return NativeLibrary.ButtonType.TRIGGER_R; |
|
||||
case SettingsFile.KEY_BUTTON_ZL: |
|
||||
return NativeLibrary.ButtonType.BUTTON_ZL; |
|
||||
case SettingsFile.KEY_BUTTON_ZR: |
|
||||
return NativeLibrary.ButtonType.BUTTON_ZR; |
|
||||
case SettingsFile.KEY_BUTTON_SELECT: |
|
||||
return NativeLibrary.ButtonType.BUTTON_SELECT; |
|
||||
case SettingsFile.KEY_BUTTON_START: |
|
||||
return NativeLibrary.ButtonType.BUTTON_START; |
|
||||
case SettingsFile.KEY_BUTTON_UP: |
|
||||
return NativeLibrary.ButtonType.DPAD_UP; |
|
||||
case SettingsFile.KEY_BUTTON_DOWN: |
|
||||
return NativeLibrary.ButtonType.DPAD_DOWN; |
|
||||
case SettingsFile.KEY_BUTTON_LEFT: |
|
||||
return NativeLibrary.ButtonType.DPAD_LEFT; |
|
||||
case SettingsFile.KEY_BUTTON_RIGHT: |
|
||||
return NativeLibrary.ButtonType.DPAD_RIGHT; |
|
||||
} |
|
||||
return -1; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Returns the settings key for the specified yuzu button code. |
|
||||
*/ |
|
||||
private static String getButtonKey(int buttonCode) { |
|
||||
switch (buttonCode) { |
|
||||
case NativeLibrary.ButtonType.BUTTON_A: |
|
||||
return SettingsFile.KEY_BUTTON_A; |
|
||||
case NativeLibrary.ButtonType.BUTTON_B: |
|
||||
return SettingsFile.KEY_BUTTON_B; |
|
||||
case NativeLibrary.ButtonType.BUTTON_X: |
|
||||
return SettingsFile.KEY_BUTTON_X; |
|
||||
case NativeLibrary.ButtonType.BUTTON_Y: |
|
||||
return SettingsFile.KEY_BUTTON_Y; |
|
||||
case NativeLibrary.ButtonType.TRIGGER_L: |
|
||||
return SettingsFile.KEY_BUTTON_L; |
|
||||
case NativeLibrary.ButtonType.TRIGGER_R: |
|
||||
return SettingsFile.KEY_BUTTON_R; |
|
||||
case NativeLibrary.ButtonType.BUTTON_ZL: |
|
||||
return SettingsFile.KEY_BUTTON_ZL; |
|
||||
case NativeLibrary.ButtonType.BUTTON_ZR: |
|
||||
return SettingsFile.KEY_BUTTON_ZR; |
|
||||
case NativeLibrary.ButtonType.BUTTON_SELECT: |
|
||||
return SettingsFile.KEY_BUTTON_SELECT; |
|
||||
case NativeLibrary.ButtonType.BUTTON_START: |
|
||||
return SettingsFile.KEY_BUTTON_START; |
|
||||
case NativeLibrary.ButtonType.DPAD_UP: |
|
||||
return SettingsFile.KEY_BUTTON_UP; |
|
||||
case NativeLibrary.ButtonType.DPAD_DOWN: |
|
||||
return SettingsFile.KEY_BUTTON_DOWN; |
|
||||
case NativeLibrary.ButtonType.DPAD_LEFT: |
|
||||
return SettingsFile.KEY_BUTTON_LEFT; |
|
||||
case NativeLibrary.ButtonType.DPAD_RIGHT: |
|
||||
return SettingsFile.KEY_BUTTON_RIGHT; |
|
||||
} |
|
||||
return ""; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Returns the key used to lookup the reverse mapping for this key, which is used to cleanup old |
|
||||
* settings on re-mapping or clearing of a setting. |
|
||||
*/ |
|
||||
private String getReverseKey() { |
|
||||
String reverseKey = INPUT_MAPPING_PREFIX + "_ReverseMapping_" + getKey(); |
|
||||
|
|
||||
if (IsAxisMappingSupported() && !IsTrigger()) { |
|
||||
// Triggers are the only axis-supported mappings without orientation |
|
||||
reverseKey += "_" + (IsHorizontalOrientation() ? 0 : 1); |
|
||||
} |
|
||||
|
|
||||
return reverseKey; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Removes the old mapping for this key from the settings, e.g. on user clearing the setting. |
|
||||
*/ |
|
||||
public void removeOldMapping() { |
|
||||
// Get preferences editor |
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.getAppContext()); |
|
||||
SharedPreferences.Editor editor = preferences.edit(); |
|
||||
|
|
||||
// Try remove all possible keys we wrote for this setting |
|
||||
String oldKey = preferences.getString(getReverseKey(), ""); |
|
||||
if (!oldKey.equals("")) { |
|
||||
editor.remove(getKey()); // Used for ui text |
|
||||
editor.remove(oldKey); // Used for button mapping |
|
||||
editor.remove(oldKey + "_GuestOrientation"); // Used for axis orientation |
|
||||
editor.remove(oldKey + "_GuestButton"); // Used for axis button |
|
||||
} |
|
||||
|
|
||||
// Apply changes |
|
||||
editor.apply(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Helper function to get the settings key for an gamepad button. |
|
||||
*/ |
|
||||
public static String getInputButtonKey(int keyCode) { |
|
||||
return INPUT_MAPPING_PREFIX + "_Button_" + keyCode; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Helper function to get the settings key for an gamepad axis. |
|
||||
*/ |
|
||||
public static String getInputAxisKey(int axis) { |
|
||||
return INPUT_MAPPING_PREFIX + "_HostAxis_" + axis; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Helper function to get the settings key for an gamepad axis button (stick or trigger). |
|
||||
*/ |
|
||||
public static String getInputAxisButtonKey(int axis) { |
|
||||
return getInputAxisKey(axis) + "_GuestButton"; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Helper function to get the settings key for an gamepad axis orientation. |
|
||||
*/ |
|
||||
public static String getInputAxisOrientationKey(int axis) { |
|
||||
return getInputAxisKey(axis) + "_GuestOrientation"; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Helper function to write a gamepad button mapping for the setting. |
|
||||
*/ |
|
||||
private void WriteButtonMapping(String key) { |
|
||||
// Get preferences editor |
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.getAppContext()); |
|
||||
SharedPreferences.Editor editor = preferences.edit(); |
|
||||
|
|
||||
// Remove mapping for another setting using this input |
|
||||
int oldButtonCode = preferences.getInt(key, -1); |
|
||||
if (oldButtonCode != -1) { |
|
||||
String oldKey = getButtonKey(oldButtonCode); |
|
||||
editor.remove(oldKey); // Only need to remove UI text setting, others will be overwritten |
|
||||
} |
|
||||
|
|
||||
// Cleanup old mapping for this setting |
|
||||
removeOldMapping(); |
|
||||
|
|
||||
// Write new mapping |
|
||||
editor.putInt(key, getButtonCode()); |
|
||||
|
|
||||
// Write next reverse mapping for future cleanup |
|
||||
editor.putString(getReverseKey(), key); |
|
||||
|
|
||||
// Apply changes |
|
||||
editor.apply(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Helper function to write a gamepad axis mapping for the setting. |
|
||||
*/ |
|
||||
private void WriteAxisMapping(int axis, int value) { |
|
||||
// Get preferences editor |
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.getAppContext()); |
|
||||
SharedPreferences.Editor editor = preferences.edit(); |
|
||||
|
|
||||
// Cleanup old mapping |
|
||||
removeOldMapping(); |
|
||||
|
|
||||
// Write new mapping |
|
||||
editor.putInt(getInputAxisOrientationKey(axis), IsHorizontalOrientation() ? 0 : 1); |
|
||||
editor.putInt(getInputAxisButtonKey(axis), value); |
|
||||
|
|
||||
// Write next reverse mapping for future cleanup |
|
||||
editor.putString(getReverseKey(), getInputAxisKey(axis)); |
|
||||
|
|
||||
// Apply changes |
|
||||
editor.apply(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Saves the provided key input setting as an Android preference. |
|
||||
* |
|
||||
* @param keyEvent KeyEvent of this key press. |
|
||||
*/ |
|
||||
public void onKeyInput(KeyEvent keyEvent) { |
|
||||
if (!IsButtonMappingSupported()) { |
|
||||
Toast.makeText(YuzuApplication.getAppContext(), R.string.input_message_analog_only, Toast.LENGTH_LONG).show(); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
InputDevice device = keyEvent.getDevice(); |
|
||||
|
|
||||
WriteButtonMapping(getInputButtonKey(keyEvent.getKeyCode())); |
|
||||
|
|
||||
String uiString = device.getName() + ": Button " + keyEvent.getKeyCode(); |
|
||||
setUiString(uiString); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Saves the provided motion input setting as an Android preference. |
|
||||
* |
|
||||
* @param device InputDevice from which the input event originated. |
|
||||
* @param motionRange MotionRange of the movement |
|
||||
* @param axisDir Either '-' or '+' (currently unused) |
|
||||
*/ |
|
||||
public void onMotionInput(InputDevice device, InputDevice.MotionRange motionRange, |
|
||||
char axisDir) { |
|
||||
if (!IsAxisMappingSupported()) { |
|
||||
Toast.makeText(YuzuApplication.getAppContext(), R.string.input_message_button_only, Toast.LENGTH_LONG).show(); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.getAppContext()); |
|
||||
SharedPreferences.Editor editor = preferences.edit(); |
|
||||
|
|
||||
int button; |
|
||||
if (IsCirclePad()) { |
|
||||
button = NativeLibrary.ButtonType.STICK_LEFT; |
|
||||
} else if (IsCStick()) { |
|
||||
button = NativeLibrary.ButtonType.STICK_C; |
|
||||
} else if (IsDPad()) { |
|
||||
button = NativeLibrary.ButtonType.DPAD; |
|
||||
} else { |
|
||||
button = getButtonCode(); |
|
||||
} |
|
||||
|
|
||||
WriteAxisMapping(motionRange.getAxis(), button); |
|
||||
|
|
||||
String uiString = device.getName() + ": Axis " + motionRange.getAxis(); |
|
||||
setUiString(uiString); |
|
||||
|
|
||||
editor.apply(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Sets the string to use in the configuration UI for the gamepad input. |
|
||||
*/ |
|
||||
private StringSetting setUiString(String ui) { |
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.getAppContext()); |
|
||||
SharedPreferences.Editor editor = preferences.edit(); |
|
||||
|
|
||||
if (getSetting() == null) { |
|
||||
StringSetting setting = new StringSetting(getKey(), getSection(), ""); |
|
||||
setSetting(setting); |
|
||||
|
|
||||
editor.putString(setting.getKey(), ui); |
|
||||
editor.apply(); |
|
||||
|
|
||||
return setting; |
|
||||
} else { |
|
||||
StringSetting setting = (StringSetting) getSetting(); |
|
||||
|
|
||||
editor.putString(setting.getKey(), ui); |
|
||||
editor.apply(); |
|
||||
|
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public int getType() { |
|
||||
return TYPE_INPUT_BINDING; |
|
||||
} |
|
||||
} |
|
||||
@ -1,12 +0,0 @@ |
|||||
package org.yuzu.yuzu_emu.features.settings.model.view; |
|
||||
|
|
||||
public final class PremiumHeader extends SettingsItem { |
|
||||
public PremiumHeader() { |
|
||||
super(null, null, null, 0, 0); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public int getType() { |
|
||||
return SettingsItem.TYPE_PREMIUM; |
|
||||
} |
|
||||
} |
|
||||
@ -1,59 +0,0 @@ |
|||||
package org.yuzu.yuzu_emu.features.settings.model.view; |
|
||||
|
|
||||
import android.content.SharedPreferences; |
|
||||
import android.preference.PreferenceManager; |
|
||||
|
|
||||
import org.yuzu.yuzu_emu.YuzuApplication; |
|
||||
import org.yuzu.yuzu_emu.R; |
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Setting; |
|
||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsFragmentView; |
|
||||
|
|
||||
public final class PremiumSingleChoiceSetting extends SettingsItem { |
|
||||
private int mDefaultValue; |
|
||||
|
|
||||
private int mChoicesId; |
|
||||
private int mValuesId; |
|
||||
private SettingsFragmentView mView; |
|
||||
|
|
||||
private static SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.getAppContext()); |
|
||||
|
|
||||
public PremiumSingleChoiceSetting(String key, String section, int titleId, int descriptionId, |
|
||||
int choicesId, int valuesId, int defaultValue, Setting setting, SettingsFragmentView view) { |
|
||||
super(key, section, setting, titleId, descriptionId); |
|
||||
mValuesId = valuesId; |
|
||||
mChoicesId = choicesId; |
|
||||
mDefaultValue = defaultValue; |
|
||||
mView = view; |
|
||||
} |
|
||||
|
|
||||
public int getChoicesId() { |
|
||||
return mChoicesId; |
|
||||
} |
|
||||
|
|
||||
public int getValuesId() { |
|
||||
return mValuesId; |
|
||||
} |
|
||||
|
|
||||
public int getSelectedValue() { |
|
||||
return mPreferences.getInt(getKey(), mDefaultValue); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Write a value to the backing int. If that int was previously null, |
|
||||
* initializes a new one and returns it, so it can be added to the Hashmap. |
|
||||
* |
|
||||
* @param selection New value of the int. |
|
||||
* @return null if overwritten successfully otherwise; a newly created IntSetting. |
|
||||
*/ |
|
||||
public void setSelectedValue(int selection) { |
|
||||
final SharedPreferences.Editor editor = mPreferences.edit(); |
|
||||
editor.putInt(getKey(), selection); |
|
||||
editor.apply(); |
|
||||
mView.showToastMessage(YuzuApplication.getAppContext().getString(R.string.design_updated), false); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public int getType() { |
|
||||
return TYPE_SINGLE_CHOICE; |
|
||||
} |
|
||||
} |
|
||||
@ -1,55 +0,0 @@ |
|||||
package org.yuzu.yuzu_emu.features.settings.ui.viewholder; |
|
||||
|
|
||||
import android.content.Context; |
|
||||
import android.content.SharedPreferences; |
|
||||
import android.preference.PreferenceManager; |
|
||||
import android.view.View; |
|
||||
import android.widget.TextView; |
|
||||
|
|
||||
import org.yuzu.yuzu_emu.R; |
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.InputBindingSetting; |
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem; |
|
||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter; |
|
||||
|
|
||||
public final class InputBindingSettingViewHolder extends SettingViewHolder { |
|
||||
private InputBindingSetting mItem; |
|
||||
|
|
||||
private TextView mTextSettingName; |
|
||||
private TextView mTextSettingDescription; |
|
||||
|
|
||||
private Context mContext; |
|
||||
|
|
||||
public InputBindingSettingViewHolder(View itemView, SettingsAdapter adapter, Context context) { |
|
||||
super(itemView, adapter); |
|
||||
|
|
||||
mContext = context; |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
protected void findViews(View root) { |
|
||||
mTextSettingName = root.findViewById(R.id.text_setting_name); |
|
||||
mTextSettingDescription = root.findViewById(R.id.text_setting_description); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void bind(SettingsItem item) { |
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); |
|
||||
|
|
||||
mItem = (InputBindingSetting) item; |
|
||||
|
|
||||
mTextSettingName.setText(item.getNameId()); |
|
||||
|
|
||||
String key = sharedPreferences.getString(mItem.getKey(), ""); |
|
||||
if (key != null && !key.isEmpty()) { |
|
||||
mTextSettingDescription.setText(key); |
|
||||
mTextSettingDescription.setVisibility(View.VISIBLE); |
|
||||
} else { |
|
||||
mTextSettingDescription.setVisibility(View.GONE); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onClick(View clicked) { |
|
||||
getAdapter().onInputBindingClick(mItem, getAdapterPosition()); |
|
||||
} |
|
||||
} |
|
||||
@ -1,57 +0,0 @@ |
|||||
package org.yuzu.yuzu_emu.features.settings.ui.viewholder; |
|
||||
|
|
||||
import android.view.View; |
|
||||
import android.widget.TextView; |
|
||||
|
|
||||
import org.yuzu.yuzu_emu.R; |
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem; |
|
||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter; |
|
||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsFragmentView; |
|
||||
import org.yuzu.yuzu_emu.ui.main.MainActivity; |
|
||||
|
|
||||
public final class PremiumViewHolder extends SettingViewHolder { |
|
||||
private TextView mHeaderName; |
|
||||
private TextView mTextDescription; |
|
||||
private SettingsFragmentView mView; |
|
||||
|
|
||||
public PremiumViewHolder(View itemView, SettingsAdapter adapter, SettingsFragmentView view) { |
|
||||
super(itemView, adapter); |
|
||||
mView = view; |
|
||||
itemView.setOnClickListener(this); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
protected void findViews(View root) { |
|
||||
mHeaderName = root.findViewById(R.id.text_setting_name); |
|
||||
mTextDescription = root.findViewById(R.id.text_setting_description); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void bind(SettingsItem item) { |
|
||||
updateText(); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onClick(View clicked) { |
|
||||
if (MainActivity.isPremiumActive()) { |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// Invoke billing flow if Premium is not already active, then refresh the UI to indicate |
|
||||
// the purchase has completed. |
|
||||
MainActivity.invokePremiumBilling(() -> updateText()); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Update the text shown to the user, based on whether Premium is active |
|
||||
*/ |
|
||||
private void updateText() { |
|
||||
if (MainActivity.isPremiumActive()) { |
|
||||
mHeaderName.setText(R.string.premium_settings_welcome); |
|
||||
mTextDescription.setText(R.string.premium_settings_welcome_description); |
|
||||
} else { |
|
||||
mHeaderName.setText(R.string.premium_settings_upsell); |
|
||||
mTextDescription.setText(R.string.premium_settings_upsell_description); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,215 +0,0 @@ |
|||||
package org.yuzu.yuzu_emu.utils; |
|
||||
|
|
||||
import android.app.Activity; |
|
||||
import android.content.SharedPreferences; |
|
||||
import android.preference.PreferenceManager; |
|
||||
import android.widget.Toast; |
|
||||
|
|
||||
import com.android.billingclient.api.AcknowledgePurchaseParams; |
|
||||
import com.android.billingclient.api.AcknowledgePurchaseResponseListener; |
|
||||
import com.android.billingclient.api.BillingClient; |
|
||||
import com.android.billingclient.api.BillingClientStateListener; |
|
||||
import com.android.billingclient.api.BillingFlowParams; |
|
||||
import com.android.billingclient.api.BillingResult; |
|
||||
import com.android.billingclient.api.Purchase; |
|
||||
import com.android.billingclient.api.Purchase.PurchasesResult; |
|
||||
import com.android.billingclient.api.PurchasesUpdatedListener; |
|
||||
import com.android.billingclient.api.SkuDetails; |
|
||||
import com.android.billingclient.api.SkuDetailsParams; |
|
||||
|
|
||||
import org.yuzu.yuzu_emu.YuzuApplication; |
|
||||
import org.yuzu.yuzu_emu.R; |
|
||||
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile; |
|
||||
import org.yuzu.yuzu_emu.ui.main.MainActivity; |
|
||||
|
|
||||
import java.util.ArrayList; |
|
||||
import java.util.List; |
|
||||
|
|
||||
public class BillingManager implements PurchasesUpdatedListener { |
|
||||
private final String BILLING_SKU_PREMIUM = "yuzu.yuzu_emu.product_id.premium"; |
|
||||
|
|
||||
private final Activity mActivity; |
|
||||
private BillingClient mBillingClient; |
|
||||
private SkuDetails mSkuPremium; |
|
||||
private boolean mIsPremiumActive = false; |
|
||||
private boolean mIsServiceConnected = false; |
|
||||
private Runnable mUpdateBillingCallback; |
|
||||
|
|
||||
private static SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.getAppContext()); |
|
||||
|
|
||||
public BillingManager(Activity activity) { |
|
||||
mActivity = activity; |
|
||||
mBillingClient = BillingClient.newBuilder(mActivity).enablePendingPurchases().setListener(this).build(); |
|
||||
querySkuDetails(); |
|
||||
} |
|
||||
|
|
||||
static public boolean isPremiumCached() { |
|
||||
return mPreferences.getBoolean(SettingsFile.KEY_PREMIUM, false); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* @return true if Premium subscription is currently active |
|
||||
*/ |
|
||||
public boolean isPremiumActive() { |
|
||||
return mIsPremiumActive; |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Invokes the billing flow for Premium |
|
||||
* |
|
||||
* @param callback Optional callback, called once, on completion of billing |
|
||||
*/ |
|
||||
public void invokePremiumBilling(Runnable callback) { |
|
||||
if (mSkuPremium == null) { |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// Optional callback to refresh the UI for the caller when billing completes |
|
||||
mUpdateBillingCallback = callback; |
|
||||
|
|
||||
// Invoke the billing flow |
|
||||
BillingFlowParams flowParams = BillingFlowParams.newBuilder() |
|
||||
.setSkuDetails(mSkuPremium) |
|
||||
.build(); |
|
||||
mBillingClient.launchBillingFlow(mActivity, flowParams); |
|
||||
} |
|
||||
|
|
||||
private void updatePremiumState(boolean isPremiumActive) { |
|
||||
mIsPremiumActive = isPremiumActive; |
|
||||
|
|
||||
// Cache state for synchronous UI |
|
||||
SharedPreferences.Editor editor = mPreferences.edit(); |
|
||||
editor.putBoolean(SettingsFile.KEY_PREMIUM, isPremiumActive); |
|
||||
editor.apply(); |
|
||||
|
|
||||
// No need to show button in action bar if Premium is active |
|
||||
MainActivity.setPremiumButtonVisible(!isPremiumActive); |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchaseList) { |
|
||||
if (purchaseList == null || purchaseList.isEmpty()) { |
|
||||
// Premium is not active, or billing is unavailable |
|
||||
updatePremiumState(false); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
Purchase premiumPurchase = null; |
|
||||
for (Purchase purchase : purchaseList) { |
|
||||
if (purchase.getSku().equals(BILLING_SKU_PREMIUM)) { |
|
||||
premiumPurchase = purchase; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (premiumPurchase != null && premiumPurchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) { |
|
||||
// Premium has been purchased |
|
||||
updatePremiumState(true); |
|
||||
|
|
||||
// Acknowledge the purchase if it hasn't already been acknowledged. |
|
||||
if (!premiumPurchase.isAcknowledged()) { |
|
||||
AcknowledgePurchaseParams acknowledgePurchaseParams = |
|
||||
AcknowledgePurchaseParams.newBuilder() |
|
||||
.setPurchaseToken(premiumPurchase.getPurchaseToken()) |
|
||||
.build(); |
|
||||
|
|
||||
AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = billingResult1 -> { |
|
||||
Toast.makeText(mActivity, R.string.premium_settings_welcome, Toast.LENGTH_SHORT).show(); |
|
||||
}; |
|
||||
mBillingClient.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener); |
|
||||
} |
|
||||
|
|
||||
if (mUpdateBillingCallback != null) { |
|
||||
try { |
|
||||
mUpdateBillingCallback.run(); |
|
||||
} catch (Exception e) { |
|
||||
e.printStackTrace(); |
|
||||
} |
|
||||
mUpdateBillingCallback = null; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void onQuerySkuDetailsFinished(List<SkuDetails> skuDetailsList) { |
|
||||
if (skuDetailsList == null) { |
|
||||
// This can happen when no user is signed in |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (skuDetailsList.isEmpty()) { |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
mSkuPremium = skuDetailsList.get(0); |
|
||||
|
|
||||
queryPurchases(); |
|
||||
} |
|
||||
|
|
||||
private void querySkuDetails() { |
|
||||
Runnable queryToExecute = new Runnable() { |
|
||||
@Override |
|
||||
public void run() { |
|
||||
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder(); |
|
||||
List<String> skuList = new ArrayList<>(); |
|
||||
|
|
||||
skuList.add(BILLING_SKU_PREMIUM); |
|
||||
params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP); |
|
||||
|
|
||||
mBillingClient.querySkuDetailsAsync(params.build(), |
|
||||
(billingResult, skuDetailsList) -> onQuerySkuDetailsFinished(skuDetailsList)); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
executeServiceRequest(queryToExecute); |
|
||||
} |
|
||||
|
|
||||
private void onQueryPurchasesFinished(PurchasesResult result) { |
|
||||
// Have we been disposed of in the meantime? If so, or bad result code, then quit |
|
||||
if (mBillingClient == null || result.getResponseCode() != BillingClient.BillingResponseCode.OK) { |
|
||||
updatePremiumState(false); |
|
||||
return; |
|
||||
} |
|
||||
// Update the UI and purchases inventory with new list of purchases |
|
||||
onPurchasesUpdated(result.getBillingResult(), result.getPurchasesList()); |
|
||||
} |
|
||||
|
|
||||
private void queryPurchases() { |
|
||||
Runnable queryToExecute = new Runnable() { |
|
||||
@Override |
|
||||
public void run() { |
|
||||
final PurchasesResult purchasesResult = mBillingClient.queryPurchases(BillingClient.SkuType.INAPP); |
|
||||
onQueryPurchasesFinished(purchasesResult); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
executeServiceRequest(queryToExecute); |
|
||||
} |
|
||||
|
|
||||
private void startServiceConnection(final Runnable executeOnFinish) { |
|
||||
mBillingClient.startConnection(new BillingClientStateListener() { |
|
||||
@Override |
|
||||
public void onBillingSetupFinished(BillingResult billingResult) { |
|
||||
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) { |
|
||||
mIsServiceConnected = true; |
|
||||
} |
|
||||
|
|
||||
if (executeOnFinish != null) { |
|
||||
executeOnFinish.run(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@Override |
|
||||
public void onBillingServiceDisconnected() { |
|
||||
mIsServiceConnected = false; |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
private void executeServiceRequest(Runnable runnable) { |
|
||||
if (mIsServiceConnected) { |
|
||||
runnable.run(); |
|
||||
} else { |
|
||||
// If billing service was disconnected, we try to reconnect 1 time. |
|
||||
startServiceConnection(runnable); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,284 @@ |
|||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
|
||||
|
#include <memory>
|
||||
|
#include <optional>
|
||||
|
#include <sstream>
|
||||
|
|
||||
|
#include <INIReader.h>
|
||||
|
#include "common/fs/file.h"
|
||||
|
#include "common/fs/fs.h"
|
||||
|
#include "common/fs/path_util.h"
|
||||
|
#include "common/logging/log.h"
|
||||
|
#include "common/settings.h"
|
||||
|
#include "core/hle/service/acc/profile_manager.h"
|
||||
|
#include "input_common/main.h"
|
||||
|
#include "jni/config.h"
|
||||
|
#include "jni/default_ini.h"
|
||||
|
|
||||
|
namespace FS = Common::FS; |
||||
|
|
||||
|
const std::filesystem::path default_config_path = |
||||
|
FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "config.ini"; |
||||
|
|
||||
|
Config::Config(std::optional<std::filesystem::path> config_path) |
||||
|
: config_loc{config_path.value_or(default_config_path)}, |
||||
|
config{std::make_unique<INIReader>(FS::PathToUTF8String(config_loc))} { |
||||
|
Reload(); |
||||
|
} |
||||
|
|
||||
|
Config::~Config() = default; |
||||
|
|
||||
|
bool Config::LoadINI(const std::string& default_contents, bool retry) { |
||||
|
const auto config_loc_str = FS::PathToUTF8String(config_loc); |
||||
|
if (config->ParseError() < 0) { |
||||
|
if (retry) { |
||||
|
LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", |
||||
|
config_loc_str); |
||||
|
|
||||
|
void(FS::CreateParentDir(config_loc)); |
||||
|
void(FS::WriteStringToFile(config_loc, FS::FileType::TextFile, default_contents)); |
||||
|
|
||||
|
config = std::make_unique<INIReader>(config_loc_str); |
||||
|
|
||||
|
return LoadINI(default_contents, false); |
||||
|
} |
||||
|
LOG_ERROR(Config, "Failed."); |
||||
|
return false; |
||||
|
} |
||||
|
LOG_INFO(Config, "Successfully loaded {}", config_loc_str); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
template <> |
||||
|
void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) { |
||||
|
std::string setting_value = config->Get(group, setting.GetLabel(), setting.GetDefault()); |
||||
|
if (setting_value.empty()) { |
||||
|
setting_value = setting.GetDefault(); |
||||
|
} |
||||
|
setting = std::move(setting_value); |
||||
|
} |
||||
|
|
||||
|
template <> |
||||
|
void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) { |
||||
|
setting = config->GetBoolean(group, setting.GetLabel(), setting.GetDefault()); |
||||
|
} |
||||
|
|
||||
|
template <typename Type, bool ranged> |
||||
|
void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) { |
||||
|
setting = static_cast<Type>(config->GetInteger(group, setting.GetLabel(), |
||||
|
static_cast<long>(setting.GetDefault()))); |
||||
|
} |
||||
|
|
||||
|
void Config::ReadValues() { |
||||
|
ReadSetting("ControlsGeneral", Settings::values.mouse_enabled); |
||||
|
ReadSetting("ControlsGeneral", Settings::values.touch_device); |
||||
|
ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled); |
||||
|
ReadSetting("ControlsGeneral", Settings::values.debug_pad_enabled); |
||||
|
ReadSetting("ControlsGeneral", Settings::values.vibration_enabled); |
||||
|
ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations); |
||||
|
ReadSetting("ControlsGeneral", Settings::values.motion_enabled); |
||||
|
Settings::values.touchscreen.enabled = |
||||
|
config->GetBoolean("ControlsGeneral", "touch_enabled", true); |
||||
|
Settings::values.touchscreen.rotation_angle = |
||||
|
config->GetInteger("ControlsGeneral", "touch_angle", 0); |
||||
|
Settings::values.touchscreen.diameter_x = |
||||
|
config->GetInteger("ControlsGeneral", "touch_diameter_x", 15); |
||||
|
Settings::values.touchscreen.diameter_y = |
||||
|
config->GetInteger("ControlsGeneral", "touch_diameter_y", 15); |
||||
|
|
||||
|
int num_touch_from_button_maps = |
||||
|
config->GetInteger("ControlsGeneral", "touch_from_button_map", 0); |
||||
|
if (num_touch_from_button_maps > 0) { |
||||
|
for (int i = 0; i < num_touch_from_button_maps; ++i) { |
||||
|
Settings::TouchFromButtonMap map; |
||||
|
map.name = config->Get("ControlsGeneral", |
||||
|
std::string("touch_from_button_maps_") + std::to_string(i) + |
||||
|
std::string("_name"), |
||||
|
"default"); |
||||
|
const int num_touch_maps = config->GetInteger( |
||||
|
"ControlsGeneral", |
||||
|
std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"), |
||||
|
0); |
||||
|
map.buttons.reserve(num_touch_maps); |
||||
|
|
||||
|
for (int j = 0; j < num_touch_maps; ++j) { |
||||
|
std::string touch_mapping = |
||||
|
config->Get("ControlsGeneral", |
||||
|
std::string("touch_from_button_maps_") + std::to_string(i) + |
||||
|
std::string("_bind_") + std::to_string(j), |
||||
|
""); |
||||
|
map.buttons.emplace_back(std::move(touch_mapping)); |
||||
|
} |
||||
|
|
||||
|
Settings::values.touch_from_button_maps.emplace_back(std::move(map)); |
||||
|
} |
||||
|
} else { |
||||
|
Settings::values.touch_from_button_maps.emplace_back( |
||||
|
Settings::TouchFromButtonMap{"default", {}}); |
||||
|
num_touch_from_button_maps = 1; |
||||
|
} |
||||
|
Settings::values.touch_from_button_map_index = std::clamp( |
||||
|
Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); |
||||
|
|
||||
|
ReadSetting("ControlsGeneral", Settings::values.udp_input_servers); |
||||
|
|
||||
|
// Data Storage
|
||||
|
ReadSetting("Data Storage", Settings::values.use_virtual_sd); |
||||
|
FS::SetYuzuPath(FS::YuzuPath::NANDDir, |
||||
|
config->Get("Data Storage", "nand_directory", |
||||
|
FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); |
||||
|
FS::SetYuzuPath(FS::YuzuPath::SDMCDir, |
||||
|
config->Get("Data Storage", "sdmc_directory", |
||||
|
FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); |
||||
|
FS::SetYuzuPath(FS::YuzuPath::LoadDir, |
||||
|
config->Get("Data Storage", "load_directory", |
||||
|
FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); |
||||
|
FS::SetYuzuPath(FS::YuzuPath::DumpDir, |
||||
|
config->Get("Data Storage", "dump_directory", |
||||
|
FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); |
||||
|
ReadSetting("Data Storage", Settings::values.gamecard_inserted); |
||||
|
ReadSetting("Data Storage", Settings::values.gamecard_current_game); |
||||
|
ReadSetting("Data Storage", Settings::values.gamecard_path); |
||||
|
|
||||
|
// System
|
||||
|
ReadSetting("System", Settings::values.use_docked_mode); |
||||
|
|
||||
|
ReadSetting("System", Settings::values.current_user); |
||||
|
Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0, |
||||
|
Service::Account::MAX_USERS - 1); |
||||
|
|
||||
|
const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false); |
||||
|
if (rng_seed_enabled) { |
||||
|
Settings::values.rng_seed.SetValue(config->GetInteger("System", "rng_seed", 0)); |
||||
|
} else { |
||||
|
Settings::values.rng_seed.SetValue(std::nullopt); |
||||
|
} |
||||
|
|
||||
|
const auto custom_rtc_enabled = config->GetBoolean("System", "custom_rtc_enabled", false); |
||||
|
if (custom_rtc_enabled) { |
||||
|
Settings::values.custom_rtc = config->GetInteger("System", "custom_rtc", 0); |
||||
|
} else { |
||||
|
Settings::values.custom_rtc = std::nullopt; |
||||
|
} |
||||
|
|
||||
|
ReadSetting("System", Settings::values.language_index); |
||||
|
ReadSetting("System", Settings::values.region_index); |
||||
|
ReadSetting("System", Settings::values.time_zone_index); |
||||
|
ReadSetting("System", Settings::values.sound_index); |
||||
|
|
||||
|
// Core
|
||||
|
ReadSetting("Core", Settings::values.use_multi_core); |
||||
|
ReadSetting("Core", Settings::values.use_unsafe_extended_memory_layout); |
||||
|
|
||||
|
// Cpu
|
||||
|
ReadSetting("Cpu", Settings::values.cpu_accuracy); |
||||
|
ReadSetting("Cpu", Settings::values.cpu_debug_mode); |
||||
|
ReadSetting("Cpu", Settings::values.cpuopt_page_tables); |
||||
|
ReadSetting("Cpu", Settings::values.cpuopt_block_linking); |
||||
|
ReadSetting("Cpu", Settings::values.cpuopt_return_stack_buffer); |
||||
|
ReadSetting("Cpu", Settings::values.cpuopt_fast_dispatcher); |
||||
|
ReadSetting("Cpu", Settings::values.cpuopt_context_elimination); |
||||
|
ReadSetting("Cpu", Settings::values.cpuopt_const_prop); |
||||
|
ReadSetting("Cpu", Settings::values.cpuopt_misc_ir); |
||||
|
ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks); |
||||
|
ReadSetting("Cpu", Settings::values.cpuopt_fastmem); |
||||
|
ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives); |
||||
|
ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives); |
||||
|
ReadSetting("Cpu", Settings::values.cpuopt_ignore_memory_aborts); |
||||
|
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma); |
||||
|
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error); |
||||
|
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr); |
||||
|
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan); |
||||
|
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check); |
||||
|
ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_global_monitor); |
||||
|
|
||||
|
// Renderer
|
||||
|
ReadSetting("Renderer", Settings::values.renderer_backend); |
||||
|
ReadSetting("Renderer", Settings::values.renderer_force_max_clock); |
||||
|
ReadSetting("Renderer", Settings::values.renderer_debug); |
||||
|
ReadSetting("Renderer", Settings::values.renderer_shader_feedback); |
||||
|
ReadSetting("Renderer", Settings::values.enable_nsight_aftermath); |
||||
|
ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks); |
||||
|
ReadSetting("Renderer", Settings::values.vulkan_device); |
||||
|
|
||||
|
ReadSetting("Renderer", Settings::values.resolution_setup); |
||||
|
ReadSetting("Renderer", Settings::values.scaling_filter); |
||||
|
ReadSetting("Renderer", Settings::values.fsr_sharpening_slider); |
||||
|
ReadSetting("Renderer", Settings::values.anti_aliasing); |
||||
|
ReadSetting("Renderer", Settings::values.fullscreen_mode); |
||||
|
ReadSetting("Renderer", Settings::values.aspect_ratio); |
||||
|
ReadSetting("Renderer", Settings::values.max_anisotropy); |
||||
|
ReadSetting("Renderer", Settings::values.use_speed_limit); |
||||
|
ReadSetting("Renderer", Settings::values.speed_limit); |
||||
|
ReadSetting("Renderer", Settings::values.use_disk_shader_cache); |
||||
|
ReadSetting("Renderer", Settings::values.gpu_accuracy); |
||||
|
ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation); |
||||
|
ReadSetting("Renderer", Settings::values.vsync_mode); |
||||
|
ReadSetting("Renderer", Settings::values.shader_backend); |
||||
|
ReadSetting("Renderer", Settings::values.use_asynchronous_shaders); |
||||
|
ReadSetting("Renderer", Settings::values.nvdec_emulation); |
||||
|
ReadSetting("Renderer", Settings::values.accelerate_astc); |
||||
|
ReadSetting("Renderer", Settings::values.use_fast_gpu_time); |
||||
|
ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache); |
||||
|
|
||||
|
ReadSetting("Renderer", Settings::values.bg_red); |
||||
|
ReadSetting("Renderer", Settings::values.bg_green); |
||||
|
ReadSetting("Renderer", Settings::values.bg_blue); |
||||
|
|
||||
|
// Audio
|
||||
|
ReadSetting("Audio", Settings::values.sink_id); |
||||
|
ReadSetting("Audio", Settings::values.audio_output_device_id); |
||||
|
ReadSetting("Audio", Settings::values.volume); |
||||
|
|
||||
|
// Miscellaneous
|
||||
|
// log_filter has a different default here than from common
|
||||
|
Settings::values.log_filter = "*:Info"; |
||||
|
ReadSetting("Miscellaneous", Settings::values.use_dev_keys); |
||||
|
|
||||
|
// Debugging
|
||||
|
Settings::values.record_frame_times = |
||||
|
config->GetBoolean("Debugging", "record_frame_times", false); |
||||
|
ReadSetting("Debugging", Settings::values.dump_exefs); |
||||
|
ReadSetting("Debugging", Settings::values.dump_nso); |
||||
|
ReadSetting("Debugging", Settings::values.enable_fs_access_log); |
||||
|
ReadSetting("Debugging", Settings::values.reporting_services); |
||||
|
ReadSetting("Debugging", Settings::values.quest_flag); |
||||
|
ReadSetting("Debugging", Settings::values.use_debug_asserts); |
||||
|
ReadSetting("Debugging", Settings::values.use_auto_stub); |
||||
|
ReadSetting("Debugging", Settings::values.disable_macro_jit); |
||||
|
ReadSetting("Debugging", Settings::values.disable_macro_hle); |
||||
|
ReadSetting("Debugging", Settings::values.use_gdbstub); |
||||
|
ReadSetting("Debugging", Settings::values.gdbstub_port); |
||||
|
|
||||
|
const auto title_list = config->Get("AddOns", "title_ids", ""); |
||||
|
std::stringstream ss(title_list); |
||||
|
std::string line; |
||||
|
while (std::getline(ss, line, '|')) { |
||||
|
const auto title_id = std::stoul(line, nullptr, 16); |
||||
|
const auto disabled_list = config->Get("AddOns", "disabled_" + line, ""); |
||||
|
|
||||
|
std::stringstream inner_ss(disabled_list); |
||||
|
std::string inner_line; |
||||
|
std::vector<std::string> out; |
||||
|
while (std::getline(inner_ss, inner_line, '|')) { |
||||
|
out.push_back(inner_line); |
||||
|
} |
||||
|
|
||||
|
Settings::values.disabled_addons.insert_or_assign(title_id, out); |
||||
|
} |
||||
|
|
||||
|
// Web Service
|
||||
|
ReadSetting("WebService", Settings::values.enable_telemetry); |
||||
|
ReadSetting("WebService", Settings::values.web_api_url); |
||||
|
ReadSetting("WebService", Settings::values.yuzu_username); |
||||
|
ReadSetting("WebService", Settings::values.yuzu_token); |
||||
|
|
||||
|
// Network
|
||||
|
ReadSetting("Network", Settings::values.network_interface); |
||||
|
} |
||||
|
|
||||
|
void Config::Reload() { |
||||
|
LoadINI(DefaultINI::android_config_file); |
||||
|
ReadValues(); |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <filesystem> |
||||
|
#include <memory> |
||||
|
#include <optional> |
||||
|
#include <string> |
||||
|
|
||||
|
#include "common/settings.h" |
||||
|
|
||||
|
class INIReader; |
||||
|
|
||||
|
class Config { |
||||
|
std::filesystem::path config_loc; |
||||
|
std::unique_ptr<INIReader> config; |
||||
|
|
||||
|
bool LoadINI(const std::string& default_contents = "", bool retry = true); |
||||
|
void ReadValues(); |
||||
|
|
||||
|
public: |
||||
|
explicit Config(std::optional<std::filesystem::path> config_path = std::nullopt); |
||||
|
~Config(); |
||||
|
|
||||
|
void Reload(); |
||||
|
|
||||
|
private: |
||||
|
/** |
||||
|
* Applies a value read from the sdl2_config to a Setting. |
||||
|
* |
||||
|
* @param group The name of the INI group |
||||
|
* @param setting The yuzu setting to modify |
||||
|
*/ |
||||
|
template <typename Type, bool ranged> |
||||
|
void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting); |
||||
|
}; |
||||
@ -0,0 +1,499 @@ |
|||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
namespace DefaultINI { |
||||
|
|
||||
|
const char* android_config_file = R"( |
||||
|
|
||||
|
[ControlsP0] |
||||
|
# The input devices and parameters for each Switch native input |
||||
|
# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ... |
||||
|
# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..." |
||||
|
# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values |
||||
|
|
||||
|
# Indicates if this player should be connected at boot |
||||
|
connected= |
||||
|
|
||||
|
# for button input, the following devices are available: |
||||
|
# - "keyboard" (default) for keyboard input. Required parameters: |
||||
|
# - "code": the code of the key to bind |
||||
|
# - "sdl" for joystick input using SDL. Required parameters: |
||||
|
# - "guid": SDL identification GUID of the joystick |
||||
|
# - "port": the index of the joystick to bind |
||||
|
# - "button"(optional): the index of the button to bind |
||||
|
# - "hat"(optional): the index of the hat to bind as direction buttons |
||||
|
# - "axis"(optional): the index of the axis to bind |
||||
|
# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right" |
||||
|
# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is |
||||
|
# triggered if the axis value crosses |
||||
|
# - "direction"(only used for axis): "+" means the button is triggered when the axis value |
||||
|
# is greater than the threshold; "-" means the button is triggered when the axis value |
||||
|
# is smaller than the threshold |
||||
|
button_a= |
||||
|
button_b= |
||||
|
button_x= |
||||
|
button_y= |
||||
|
button_lstick= |
||||
|
button_rstick= |
||||
|
button_l= |
||||
|
button_r= |
||||
|
button_zl= |
||||
|
button_zr= |
||||
|
button_plus= |
||||
|
button_minus= |
||||
|
button_dleft= |
||||
|
button_dup= |
||||
|
button_dright= |
||||
|
button_ddown= |
||||
|
button_lstick_left= |
||||
|
button_lstick_up= |
||||
|
button_lstick_right= |
||||
|
button_lstick_down= |
||||
|
button_sl= |
||||
|
button_sr= |
||||
|
button_home= |
||||
|
button_screenshot= |
||||
|
|
||||
|
# for analog input, the following devices are available: |
||||
|
# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters: |
||||
|
# - "up", "down", "left", "right": sub-devices for each direction. |
||||
|
# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00" |
||||
|
# - "modifier": sub-devices as a modifier. |
||||
|
# - "modifier_scale": a float number representing the applied modifier scale to the analog input. |
||||
|
# Must be in range of 0.0-1.0. Defaults to 0.5 |
||||
|
# - "sdl" for joystick input using SDL. Required parameters: |
||||
|
# - "guid": SDL identification GUID of the joystick |
||||
|
# - "port": the index of the joystick to bind |
||||
|
# - "axis_x": the index of the axis to bind as x-axis (default to 0) |
||||
|
# - "axis_y": the index of the axis to bind as y-axis (default to 1) |
||||
|
lstick= |
||||
|
rstick= |
||||
|
|
||||
|
# for motion input, the following devices are available: |
||||
|
# - "keyboard" (default) for emulating random motion input from buttons. Required parameters: |
||||
|
# - "code": the code of the key to bind |
||||
|
# - "sdl" for motion input using SDL. Required parameters: |
||||
|
# - "guid": SDL identification GUID of the joystick |
||||
|
# - "port": the index of the joystick to bind |
||||
|
# - "motion": the index of the motion sensor to bind |
||||
|
# - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters: |
||||
|
# - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001" |
||||
|
# - "port": the port of the cemu hook server |
||||
|
# - "pad": the index of the joystick |
||||
|
# - "motion": the index of the motion sensor of the joystick to bind |
||||
|
motionleft= |
||||
|
motionright= |
||||
|
|
||||
|
[ControlsGeneral] |
||||
|
# To use the debug_pad, prepend `debug_pad_` before each button setting above. |
||||
|
# i.e. debug_pad_button_a= |
||||
|
|
||||
|
# Enable debug pad inputs to the guest |
||||
|
# 0 (default): Disabled, 1: Enabled |
||||
|
debug_pad_enabled = |
||||
|
|
||||
|
# Whether to enable or disable vibration |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
vibration_enabled= |
||||
|
|
||||
|
# Whether to enable or disable accurate vibrations |
||||
|
# 0 (default): Disabled, 1: Enabled |
||||
|
enable_accurate_vibrations= |
||||
|
|
||||
|
# Enables controller motion inputs |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
motion_enabled = |
||||
|
|
||||
|
# Defines the udp device's touch screen coordinate system for cemuhookudp devices |
||||
|
# - "min_x", "min_y", "max_x", "max_y" |
||||
|
touch_device= |
||||
|
|
||||
|
# for mapping buttons to touch inputs. |
||||
|
#touch_from_button_map=1 |
||||
|
#touch_from_button_maps_0_name=default |
||||
|
#touch_from_button_maps_0_count=2 |
||||
|
#touch_from_button_maps_0_bind_0=foo |
||||
|
#touch_from_button_maps_0_bind_1=bar |
||||
|
# etc. |
||||
|
|
||||
|
# List of Cemuhook UDP servers, delimited by ','. |
||||
|
# Default: 127.0.0.1:26760 |
||||
|
# Example: 127.0.0.1:26760,123.4.5.67:26761 |
||||
|
udp_input_servers = |
||||
|
|
||||
|
# Enable controlling an axis via a mouse input. |
||||
|
# 0 (default): Off, 1: On |
||||
|
mouse_panning = |
||||
|
|
||||
|
# Set mouse sensitivity. |
||||
|
# Default: 1.0 |
||||
|
mouse_panning_sensitivity = |
||||
|
|
||||
|
# Emulate an analog control stick from keyboard inputs. |
||||
|
# 0 (default): Disabled, 1: Enabled |
||||
|
emulate_analog_keyboard = |
||||
|
|
||||
|
# Enable mouse inputs to the guest |
||||
|
# 0 (default): Disabled, 1: Enabled |
||||
|
mouse_enabled = |
||||
|
|
||||
|
# Enable keyboard inputs to the guest |
||||
|
# 0 (default): Disabled, 1: Enabled |
||||
|
keyboard_enabled = |
||||
|
|
||||
|
[Core] |
||||
|
# Whether to use multi-core for CPU emulation |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
use_multi_core = |
||||
|
|
||||
|
# Enable unsafe extended guest system memory layout (8GB DRAM) |
||||
|
# 0 (default): Disabled, 1: Enabled |
||||
|
use_unsafe_extended_memory_layout = |
||||
|
|
||||
|
[Cpu] |
||||
|
# Adjusts various optimizations. |
||||
|
# Auto-select mode enables choice unsafe optimizations. |
||||
|
# Accurate enables only safe optimizations. |
||||
|
# Unsafe allows any unsafe optimizations. |
||||
|
# 0 (default): Auto-select, 1: Accurate, 2: Enable unsafe optimizations |
||||
|
cpu_accuracy = |
||||
|
|
||||
|
# Allow disabling safe optimizations. |
||||
|
# 0 (default): Disabled, 1: Enabled |
||||
|
cpu_debug_mode = |
||||
|
|
||||
|
# Enable inline page tables optimization (faster guest memory access) |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
cpuopt_page_tables = |
||||
|
|
||||
|
# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps) |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
cpuopt_block_linking = |
||||
|
|
||||
|
# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns) |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
cpuopt_return_stack_buffer = |
||||
|
|
||||
|
# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture) |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
cpuopt_fast_dispatcher = |
||||
|
|
||||
|
# Enable context elimination CPU Optimization (reduce host memory use for guest context) |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
cpuopt_context_elimination = |
||||
|
|
||||
|
# Enable constant propagation CPU optimization (basic IR optimization) |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
cpuopt_const_prop = |
||||
|
|
||||
|
# Enable miscellaneous CPU optimizations (basic IR optimization) |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
cpuopt_misc_ir = |
||||
|
|
||||
|
# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access) |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
cpuopt_reduce_misalign_checks = |
||||
|
|
||||
|
# Enable Host MMU Emulation (faster guest memory access) |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
cpuopt_fastmem = |
||||
|
|
||||
|
# Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access) |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
cpuopt_fastmem_exclusives = |
||||
|
|
||||
|
# Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access) |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
cpuopt_recompile_exclusives = |
||||
|
|
||||
|
# Enable optimization to ignore invalid memory accesses (faster guest memory access) |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
cpuopt_ignore_memory_aborts = |
||||
|
|
||||
|
# Enable unfuse FMA (improve performance on CPUs without FMA) |
||||
|
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
cpuopt_unsafe_unfuse_fma = |
||||
|
|
||||
|
# Enable faster FRSQRTE and FRECPE |
||||
|
# Only enabled if cpu_accuracy is set to Unsafe. |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
cpuopt_unsafe_reduce_fp_error = |
||||
|
|
||||
|
# Enable faster ASIMD instructions (32 bits only) |
||||
|
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
cpuopt_unsafe_ignore_standard_fpcr = |
||||
|
|
||||
|
# Enable inaccurate NaN handling |
||||
|
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
cpuopt_unsafe_inaccurate_nan = |
||||
|
|
||||
|
# Disable address space checks (64 bits only) |
||||
|
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
cpuopt_unsafe_fastmem_check = |
||||
|
|
||||
|
# Enable faster exclusive instructions |
||||
|
# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. |
||||
|
# 0: Disabled, 1 (default): Enabled |
||||
|
cpuopt_unsafe_ignore_global_monitor = |
||||
|
|
||||
|
[Renderer] |
||||
|
# Which backend API to use. |
||||
|
# 0: OpenGL (unsupported), 1 (default): Vulkan, 2: Null |
||||
|
backend = |
||||
|
|
||||
|
# Enable graphics API debugging mode. |
||||
|
# 0 (default): Disabled, 1: Enabled |
||||
|
debug = |
||||
|
|
||||
|
# Enable shader feedback. |
||||
|
# 0 (default): Disabled, 1: Enabled |
||||
|
renderer_shader_feedback = |
||||
|
|
||||
|
# Enable Nsight Aftermath crash dumps |
||||
|
# 0 (default): Disabled, 1: Enabled |
||||
|
nsight_aftermath = |
||||
|
|
||||
|
# Disable shader loop safety checks, executing the shader without loop logic changes |
||||
|
# 0 (default): Disabled, 1: Enabled |
||||
|
disable_shader_loop_safety_checks = |
||||
|
|
||||
|
# Which Vulkan physical device to use (defaults to 0) |
||||
|
vulkan_device = |
||||
|
|
||||
|
# 0: 0.5x (360p/540p) [EXPERIMENTAL] |
||||
|
# 1: 0.75x (540p/810p) [EXPERIMENTAL] |
||||
|
# 2 (default): 1x (720p/1080p) |
||||
|
# 3: 2x (1440p/2160p) |
||||
|
# 4: 3x (2160p/3240p) |
||||
|
# 5: 4x (2880p/4320p) |
||||
|
# 6: 5x (3600p/5400p) |
||||
|
# 7: 6x (4320p/6480p) |
||||
|
resolution_setup = |
||||
|
|
||||
|
# Pixel filter to use when up- or down-sampling rendered frames. |
||||
|
# 0: Nearest Neighbor |
||||
|
# 1 (default): Bilinear |
||||
|
# 2: Bicubic |
||||
|
# 3: Gaussian |
||||
|
# 4: ScaleForce |
||||
|
# 5: AMD FidelityFX™️ Super Resolution [Vulkan Only] |
||||
|
scaling_filter = |
||||
|
|
||||
|
# Anti-Aliasing (AA) |
||||
|
# 0 (default): None, 1: FXAA |
||||
|
anti_aliasing = |
||||
|
|
||||
|
# Whether to use fullscreen or borderless window mode |
||||
|
# 0 (Windows default): Borderless window, 1 (All other default): Exclusive fullscreen |
||||
|
fullscreen_mode = |
||||
|
|
||||
|
# Aspect ratio |
||||
|
# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window |
||||
|
aspect_ratio = |
||||
|
|
||||
|
# Anisotropic filtering |
||||
|
# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x |
||||
|
max_anisotropy = |
||||
|
|
||||
|
# Whether to enable VSync or not. |
||||
|
# OpenGL: Values other than 0 enable VSync |
||||
|
# Vulkan: FIFO is selected if the requested mode is not supported by the driver. |
||||
|
# FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. |
||||
|
# FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down. |
||||
|
# Mailbox can have lower latency than FIFO and does not tear but may drop frames. |
||||
|
# Immediate (no synchronization) just presents whatever is available and can exhibit tearing. |
||||
|
# 0: Immediate (Off), 1: Mailbox, 2 (Default): FIFO (On), 3: FIFO Relaxed |
||||
|
use_vsync = |
||||
|
|
||||
|
# Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is |
||||
|
# not available and GLASM is selected, GLSL will be used. |
||||
|
# 0: GLSL, 1 (default): GLASM, 2: SPIR-V |
||||
|
shader_backend = |
||||
|
|
||||
|
# Whether to allow asynchronous shader building. |
||||
|
# 0 (default): Off, 1: On |
||||
|
use_asynchronous_shaders = |
||||
|
|
||||
|
# NVDEC emulation. |
||||
|
# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding |
||||
|
nvdec_emulation = |
||||
|
|
||||
|
# Accelerate ASTC texture decoding. |
||||
|
# 0: Off, 1 (default): On |
||||
|
accelerate_astc = |
||||
|
|
||||
|
# Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value |
||||
|
# 0: Off, 1: On (default) |
||||
|
use_speed_limit = |
||||
|
|
||||
|
# Limits the speed of the game to run no faster than this value as a percentage of target speed |
||||
|
# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default) |
||||
|
speed_limit = |
||||
|
|
||||
|
# Whether to use disk based shader cache |
||||
|
# 0: Off, 1 (default): On |
||||
|
use_disk_shader_cache = |
||||
|
|
||||
|
# Which gpu accuracy level to use |
||||
|
# 0: Normal, 1 (default): High, 2: Extreme (Very slow) |
||||
|
gpu_accuracy = |
||||
|
|
||||
|
# Whether to use asynchronous GPU emulation |
||||
|
# 0 : Off (slow), 1 (default): On (fast) |
||||
|
use_asynchronous_gpu_emulation = |
||||
|
|
||||
|
# Inform the guest that GPU operations completed more quickly than they did. |
||||
|
# 0: Off, 1 (default): On |
||||
|
use_fast_gpu_time = |
||||
|
|
||||
|
# Force unmodified buffers to be flushed, which can cost performance. |
||||
|
# 0: Off (default), 1: On |
||||
|
use_pessimistic_flushes = |
||||
|
|
||||
|
# Whether to use garbage collection or not for GPU caches. |
||||
|
# 0 (default): Off, 1: On |
||||
|
use_caches_gc = |
||||
|
|
||||
|
# The clear color for the renderer. What shows up on the sides of the bottom screen. |
||||
|
# Must be in range of 0-255. Defaults to 0 for all. |
||||
|
bg_red = |
||||
|
bg_blue = |
||||
|
bg_green = |
||||
|
|
||||
|
[Audio] |
||||
|
# Which audio output engine to use. |
||||
|
# auto (default): Auto-select |
||||
|
# cubeb: Cubeb audio engine (if available) |
||||
|
# sdl2: SDL2 audio engine (if available) |
||||
|
# null: No audio output |
||||
|
output_engine = |
||||
|
|
||||
|
# Which audio device to use. |
||||
|
# auto (default): Auto-select |
||||
|
output_device = |
||||
|
|
||||
|
# Output volume. |
||||
|
# 100 (default): 100%, 0; mute |
||||
|
volume = |
||||
|
|
||||
|
[Data Storage] |
||||
|
# Whether to create a virtual SD card. |
||||
|
# 1 (default): Yes, 0: No |
||||
|
use_virtual_sd = |
||||
|
|
||||
|
# Whether or not to enable gamecard emulation |
||||
|
# 1: Yes, 0 (default): No |
||||
|
gamecard_inserted = |
||||
|
|
||||
|
# Whether or not the gamecard should be emulated as the current game |
||||
|
# If 'gamecard_inserted' is 0 this setting is irrelevant |
||||
|
# 1: Yes, 0 (default): No |
||||
|
gamecard_current_game = |
||||
|
|
||||
|
# Path to an XCI file to use as the gamecard |
||||
|
# If 'gamecard_inserted' is 0 this setting is irrelevant |
||||
|
# If 'gamecard_current_game' is 1 this setting is irrelevant |
||||
|
gamecard_path = |
||||
|
|
||||
|
[System] |
||||
|
# Whether the system is docked |
||||
|
# 1 (default): Yes, 0: No |
||||
|
use_docked_mode = |
||||
|
|
||||
|
# Sets the seed for the RNG generator built into the switch |
||||
|
# rng_seed will be ignored and randomly generated if rng_seed_enabled is false |
||||
|
rng_seed_enabled = |
||||
|
rng_seed = |
||||
|
|
||||
|
# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service |
||||
|
# This will auto-increment, with the time set being the time the game is started |
||||
|
# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used |
||||
|
custom_rtc_enabled = |
||||
|
custom_rtc = |
||||
|
|
||||
|
# Sets the systems language index |
||||
|
# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, |
||||
|
# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French, |
||||
|
# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese |
||||
|
language_index = |
||||
|
|
||||
|
# The system region that yuzu will use during emulation |
||||
|
# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan |
||||
|
region_index = |
||||
|
|
||||
|
# The system time zone that yuzu will use during emulation |
||||
|
# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone |
||||
|
time_zone_index = |
||||
|
|
||||
|
# Sets the sound output mode. |
||||
|
# 0: Mono, 1 (default): Stereo, 2: Surround |
||||
|
sound_index = |
||||
|
|
||||
|
[Miscellaneous] |
||||
|
# A filter which removes logs below a certain logging level. |
||||
|
# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical |
||||
|
log_filter = *:Trace |
||||
|
|
||||
|
# Use developer keys |
||||
|
# 0 (default): Disabled, 1: Enabled |
||||
|
use_dev_keys = |
||||
|
|
||||
|
[Debugging] |
||||
|
# Record frame time data, can be found in the log directory. Boolean value |
||||
|
record_frame_times = |
||||
|
# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them |
||||
|
dump_exefs=false |
||||
|
# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them |
||||
|
dump_nso=false |
||||
|
# Determines whether or not yuzu will save the filesystem access log. |
||||
|
enable_fs_access_log=false |
||||
|
# Enables verbose reporting services |
||||
|
reporting_services = |
||||
|
# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode |
||||
|
# false: Retail/Normal Mode (default), true: Kiosk Mode |
||||
|
quest_flag = |
||||
|
# Determines whether debug asserts should be enabled, which will throw an exception on asserts. |
||||
|
# false: Disabled (default), true: Enabled |
||||
|
use_debug_asserts = |
||||
|
# Determines whether unimplemented HLE service calls should be automatically stubbed. |
||||
|
# false: Disabled (default), true: Enabled |
||||
|
use_auto_stub = |
||||
|
# Enables/Disables the macro JIT compiler |
||||
|
disable_macro_jit=false |
||||
|
# Determines whether to enable the GDB stub and wait for the debugger to attach before running. |
||||
|
# false: Disabled (default), true: Enabled |
||||
|
use_gdbstub=false |
||||
|
# The port to use for the GDB server, if it is enabled. |
||||
|
gdbstub_port=6543 |
||||
|
|
||||
|
[WebService] |
||||
|
# Whether or not to enable telemetry |
||||
|
# 0: No, 1 (default): Yes |
||||
|
enable_telemetry = |
||||
|
# URL for Web API |
||||
|
web_api_url = https://api.yuzu-emu.org |
||||
|
# Username and token for yuzu Web Service |
||||
|
# See https://profile.yuzu-emu.org/ for more info |
||||
|
yuzu_username = |
||||
|
yuzu_token = |
||||
|
|
||||
|
[Network] |
||||
|
# Name of the network interface device to use with yuzu LAN play. |
||||
|
# e.g. On *nix: 'enp7s0', 'wlp6s0u1u3u3', 'lo' |
||||
|
# e.g. On Windows: 'Ethernet', 'Wi-Fi' |
||||
|
network_interface = |
||||
|
|
||||
|
[AddOns] |
||||
|
# Used to disable add-ons |
||||
|
# List of title IDs of games that will have add-ons disabled (separated by '|'): |
||||
|
title_ids = |
||||
|
# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|') |
||||
|
# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey |
||||
|
)"; |
||||
|
} // namespace DefaultINI |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue