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