android: Integrate settings frontend with yuzu & remove unused code.

This commit is contained in:
bunnei 2023-01-08 17:31:30 -08:00
parent f7a3f1ddf4
commit 4f903d8d35
25 changed files with 949 additions and 1759 deletions

View file

@ -3,17 +3,13 @@ package org.yuzu.yuzu_emu.activities;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.SparseIntArray;
import android.view.InputDevice; import android.view.InputDevice;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.widget.SeekBar; import android.widget.SeekBar;
@ -31,20 +27,14 @@ import androidx.fragment.app.FragmentManager;
import org.yuzu.yuzu_emu.NativeLibrary; import org.yuzu.yuzu_emu.NativeLibrary;
import org.yuzu.yuzu_emu.R; import org.yuzu.yuzu_emu.R;
import org.yuzu.yuzu_emu.features.settings.model.view.InputBindingSetting;
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity;
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile;
import org.yuzu.yuzu_emu.fragments.EmulationFragment; import org.yuzu.yuzu_emu.fragments.EmulationFragment;
import org.yuzu.yuzu_emu.fragments.MenuFragment; import org.yuzu.yuzu_emu.fragments.MenuFragment;
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper; import org.yuzu.yuzu_emu.utils.ControllerMappingHelper;
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings;
import org.yuzu.yuzu_emu.utils.ForegroundService; import org.yuzu.yuzu_emu.utils.ForegroundService;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.util.List; import java.util.List;
import static android.Manifest.permission.CAMERA;
import static android.Manifest.permission.RECORD_AUDIO;
import static java.lang.annotation.RetentionPolicy.SOURCE; import static java.lang.annotation.RetentionPolicy.SOURCE;
public final class EmulationActivity extends AppCompatActivity { public final class EmulationActivity extends AppCompatActivity {
@ -198,77 +188,6 @@ public final class EmulationActivity extends AppCompatActivity {
} }
} }
// Gets button presses
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (mMenuVisible || event.getKeyCode() == KeyEvent.KEYCODE_BACK)
{
return super.dispatchKeyEvent(event);
}
int action;
int button = mPreferences.getInt(InputBindingSetting.getInputButtonKey(event.getKeyCode()), event.getKeyCode());
switch (event.getAction()) {
case KeyEvent.ACTION_DOWN:
// Handling the case where the back button is pressed.
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
onBackPressed();
return true;
}
// Normal key events.
action = NativeLibrary.ButtonState.PRESSED;
break;
case KeyEvent.ACTION_UP:
action = NativeLibrary.ButtonState.RELEASED;
break;
default:
return false;
}
InputDevice input = event.getDevice();
if (input == null) {
// Controller was disconnected
return false;
}
return NativeLibrary.onGamePadEvent(input.getDescriptor(), button, action);
}
private void toggleControls() {
final SharedPreferences.Editor editor = mPreferences.edit();
boolean[] enabledButtons = new boolean[14];
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.emulation_toggle_controls);
for (int i = 0; i < enabledButtons.length; i++) {
// Buttons that are disabled by default
boolean defaultValue = true;
switch (i) {
case 6: // ZL
case 7: // ZR
case 12: // C-stick
defaultValue = false;
break;
}
enabledButtons[i] = mPreferences.getBoolean("buttonToggle" + i, defaultValue);
}
builder.setMultiChoiceItems(R.array.n3dsButtons, enabledButtons,
(dialog, indexSelected, isChecked) -> editor
.putBoolean("buttonToggle" + indexSelected, isChecked));
builder.setPositiveButton(android.R.string.ok, (dialogInterface, i) ->
{
editor.apply();
mEmulationFragment.refreshInputOverlay();
});
AlertDialog alertDialog = builder.create();
alertDialog.show();
}
private void adjustScale() { private void adjustScale() {
LayoutInflater inflater = LayoutInflater.from(this); LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.dialog_seekbar, null); View view = inflater.inflate(R.layout.dialog_seekbar, null);
@ -377,122 +296,6 @@ public final class EmulationActivity extends AppCompatActivity {
return super.dispatchTouchEvent(event); return super.dispatchTouchEvent(event);
} }
@Override
public boolean dispatchGenericMotionEvent(MotionEvent event) {
if (mMenuVisible)
{
return false;
}
if (((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0)) {
return super.dispatchGenericMotionEvent(event);
}
// Don't attempt to do anything if we are disconnecting a device.
if (event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
return true;
}
InputDevice input = event.getDevice();
List<InputDevice.MotionRange> motions = input.getMotionRanges();
float[] axisValuesCirclePad = {0.0f, 0.0f};
float[] axisValuesCStick = {0.0f, 0.0f};
float[] axisValuesDPad = {0.0f, 0.0f};
boolean isTriggerPressedLMapped = false;
boolean isTriggerPressedRMapped = false;
boolean isTriggerPressedZLMapped = false;
boolean isTriggerPressedZRMapped = false;
boolean isTriggerPressedL = false;
boolean isTriggerPressedR = false;
boolean isTriggerPressedZL = false;
boolean isTriggerPressedZR = false;
for (InputDevice.MotionRange range : motions) {
int axis = range.getAxis();
float origValue = event.getAxisValue(axis);
float value = mControllerMappingHelper.scaleAxis(input, axis, origValue);
int nextMapping = mPreferences.getInt(InputBindingSetting.getInputAxisButtonKey(axis), -1);
int guestOrientation = mPreferences.getInt(InputBindingSetting.getInputAxisOrientationKey(axis), -1);
if (nextMapping == -1 || guestOrientation == -1) {
// Axis is unmapped
continue;
}
if ((value > 0.f && value < 0.1f) || (value < 0.f && value > -0.1f)) {
// Skip joystick wobble
value = 0.f;
}
if (nextMapping == NativeLibrary.ButtonType.STICK_LEFT) {
axisValuesCirclePad[guestOrientation] = value;
} else if (nextMapping == NativeLibrary.ButtonType.STICK_C) {
axisValuesCStick[guestOrientation] = value;
} else if (nextMapping == NativeLibrary.ButtonType.DPAD) {
axisValuesDPad[guestOrientation] = value;
} else if (nextMapping == NativeLibrary.ButtonType.TRIGGER_L) {
isTriggerPressedLMapped = true;
isTriggerPressedL = value != 0.f;
} else if (nextMapping == NativeLibrary.ButtonType.TRIGGER_R) {
isTriggerPressedRMapped = true;
isTriggerPressedR = value != 0.f;
} else if (nextMapping == NativeLibrary.ButtonType.BUTTON_ZL) {
isTriggerPressedZLMapped = true;
isTriggerPressedZL = value != 0.f;
} else if (nextMapping == NativeLibrary.ButtonType.BUTTON_ZR) {
isTriggerPressedZRMapped = true;
isTriggerPressedZR = value != 0.f;
}
}
// Circle-Pad and C-Stick status
NativeLibrary.onGamePadMoveEvent(input.getDescriptor(), NativeLibrary.ButtonType.STICK_LEFT, axisValuesCirclePad[0], axisValuesCirclePad[1]);
NativeLibrary.onGamePadMoveEvent(input.getDescriptor(), NativeLibrary.ButtonType.STICK_C, axisValuesCStick[0], axisValuesCStick[1]);
// Triggers L/R and ZL/ZR
if (isTriggerPressedLMapped) {
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.TRIGGER_L, isTriggerPressedL ? NativeLibrary.ButtonState.PRESSED : NativeLibrary.ButtonState.RELEASED);
}
if (isTriggerPressedRMapped) {
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.TRIGGER_R, isTriggerPressedR ? NativeLibrary.ButtonState.PRESSED : NativeLibrary.ButtonState.RELEASED);
}
if (isTriggerPressedZLMapped) {
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.BUTTON_ZL, isTriggerPressedZL ? NativeLibrary.ButtonState.PRESSED : NativeLibrary.ButtonState.RELEASED);
}
if (isTriggerPressedZRMapped) {
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.BUTTON_ZR, isTriggerPressedZR ? NativeLibrary.ButtonState.PRESSED : NativeLibrary.ButtonState.RELEASED);
}
// Work-around to allow D-pad axis to be bound to emulated buttons
if (axisValuesDPad[0] == 0.f) {
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_LEFT, NativeLibrary.ButtonState.RELEASED);
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_RIGHT, NativeLibrary.ButtonState.RELEASED);
}
if (axisValuesDPad[0] < 0.f) {
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_LEFT, NativeLibrary.ButtonState.PRESSED);
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_RIGHT, NativeLibrary.ButtonState.RELEASED);
}
if (axisValuesDPad[0] > 0.f) {
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_LEFT, NativeLibrary.ButtonState.RELEASED);
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_RIGHT, NativeLibrary.ButtonState.PRESSED);
}
if (axisValuesDPad[1] == 0.f) {
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_UP, NativeLibrary.ButtonState.RELEASED);
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_DOWN, NativeLibrary.ButtonState.RELEASED);
}
if (axisValuesDPad[1] < 0.f) {
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_UP, NativeLibrary.ButtonState.PRESSED);
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_DOWN, NativeLibrary.ButtonState.RELEASED);
}
if (axisValuesDPad[1] > 0.f) {
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_UP, NativeLibrary.ButtonState.RELEASED);
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_DOWN, NativeLibrary.ButtonState.PRESSED);
}
return true;
}
public boolean isActivityRecreated() { public boolean isActivityRecreated() {
return activityRecreated; return activityRecreated;
} }

View file

@ -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;
}
}

View file

@ -14,22 +14,18 @@ import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
public class Settings { public class Settings {
public static final String SECTION_PREMIUM = "Premium"; public static final String SECTION_GENERAL = "General";
public static final String SECTION_CORE = "Core";
public static final String SECTION_SYSTEM = "System"; public static final String SECTION_SYSTEM = "System";
public static final String SECTION_CONTROLS = "Controls";
public static final String SECTION_RENDERER = "Renderer"; public static final String SECTION_RENDERER = "Renderer";
public static final String SECTION_LAYOUT = "Layout";
public static final String SECTION_UTILITY = "Utility";
public static final String SECTION_AUDIO = "Audio"; public static final String SECTION_AUDIO = "Audio";
public static final String SECTION_DEBUG = "Debug"; public static final String SECTION_CPU = "Cpu";
private String gameId; private String gameId;
private static final Map<String, List<String>> configFileSectionsMap = new HashMap<>(); private static final Map<String, List<String>> configFileSectionsMap = new HashMap<>();
static { static {
configFileSectionsMap.put(SettingsFile.FILE_NAME_CONFIG, Arrays.asList(SECTION_PREMIUM, SECTION_CORE, SECTION_SYSTEM, SECTION_CONTROLS, SECTION_RENDERER, SECTION_LAYOUT, SECTION_UTILITY, SECTION_AUDIO, SECTION_DEBUG)); configFileSectionsMap.put(SettingsFile.FILE_NAME_CONFIG, Arrays.asList(SECTION_GENERAL, SECTION_SYSTEM, SECTION_RENDERER, SECTION_AUDIO, SECTION_CPU));
} }
/** /**

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -17,10 +17,8 @@ public abstract class SettingsItem {
public static final int TYPE_SINGLE_CHOICE = 2; public static final int TYPE_SINGLE_CHOICE = 2;
public static final int TYPE_SLIDER = 3; public static final int TYPE_SLIDER = 3;
public static final int TYPE_SUBMENU = 4; public static final int TYPE_SUBMENU = 4;
public static final int TYPE_INPUT_BINDING = 5; public static final int TYPE_STRING_SINGLE_CHOICE = 5;
public static final int TYPE_STRING_SINGLE_CHOICE = 6; public static final int TYPE_DATETIME_SETTING = 6;
public static final int TYPE_DATETIME_SETTING = 7;
public static final int TYPE_PREMIUM = 8;
private String mKey; private String mKey;
private String mSection; private String mSection;
@ -48,7 +46,6 @@ public abstract class SettingsItem {
mSetting = setting; mSetting = setting;
mNameId = nameId; mNameId = nameId;
mDescriptionId = descriptionId; mDescriptionId = descriptionId;
mIsPremium = (section == Settings.SECTION_PREMIUM);
} }
/** /**
@ -93,10 +90,6 @@ public abstract class SettingsItem {
return mDescriptionId; return mDescriptionId;
} }
public boolean isPremium() {
return mIsPremium;
}
/** /**
* Used by {@link SettingsAdapter}'s onCreateViewHolder() * Used by {@link SettingsAdapter}'s onCreateViewHolder()
* method to determine which type of ViewHolder should be created. * method to determine which type of ViewHolder should be created.

View file

@ -14,14 +14,11 @@ import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import org.yuzu.yuzu_emu.R; import org.yuzu.yuzu_emu.R;
import org.yuzu.yuzu_emu.dialogs.MotionAlertDialog;
import org.yuzu.yuzu_emu.features.settings.model.FloatSetting; import org.yuzu.yuzu_emu.features.settings.model.FloatSetting;
import org.yuzu.yuzu_emu.features.settings.model.IntSetting; import org.yuzu.yuzu_emu.features.settings.model.IntSetting;
import org.yuzu.yuzu_emu.features.settings.model.StringSetting; import org.yuzu.yuzu_emu.features.settings.model.StringSetting;
import org.yuzu.yuzu_emu.features.settings.model.view.CheckBoxSetting; import org.yuzu.yuzu_emu.features.settings.model.view.CheckBoxSetting;
import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting; import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting;
import org.yuzu.yuzu_emu.features.settings.model.view.InputBindingSetting;
import org.yuzu.yuzu_emu.features.settings.model.view.PremiumSingleChoiceSetting;
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem; import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem;
import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting; import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting;
import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting; import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting;
@ -30,13 +27,10 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting;
import org.yuzu.yuzu_emu.features.settings.ui.viewholder.CheckBoxSettingViewHolder; import org.yuzu.yuzu_emu.features.settings.ui.viewholder.CheckBoxSettingViewHolder;
import org.yuzu.yuzu_emu.features.settings.ui.viewholder.DateTimeViewHolder; import org.yuzu.yuzu_emu.features.settings.ui.viewholder.DateTimeViewHolder;
import org.yuzu.yuzu_emu.features.settings.ui.viewholder.HeaderViewHolder; import org.yuzu.yuzu_emu.features.settings.ui.viewholder.HeaderViewHolder;
import org.yuzu.yuzu_emu.features.settings.ui.viewholder.InputBindingSettingViewHolder;
import org.yuzu.yuzu_emu.features.settings.ui.viewholder.PremiumViewHolder;
import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SettingViewHolder; import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SettingViewHolder;
import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SingleChoiceViewHolder; import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SingleChoiceViewHolder;
import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SliderViewHolder; import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SliderViewHolder;
import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SubmenuViewHolder; import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SubmenuViewHolder;
import org.yuzu.yuzu_emu.ui.main.MainActivity;
import org.yuzu.yuzu_emu.utils.Log; import org.yuzu.yuzu_emu.utils.Log;
import java.util.ArrayList; import java.util.ArrayList;
@ -87,18 +81,10 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
view = inflater.inflate(R.layout.list_item_setting, parent, false); view = inflater.inflate(R.layout.list_item_setting, parent, false);
return new SubmenuViewHolder(view, this); return new SubmenuViewHolder(view, this);
case SettingsItem.TYPE_INPUT_BINDING:
view = inflater.inflate(R.layout.list_item_setting, parent, false);
return new InputBindingSettingViewHolder(view, this, mContext);
case SettingsItem.TYPE_DATETIME_SETTING: case SettingsItem.TYPE_DATETIME_SETTING:
view = inflater.inflate(R.layout.list_item_setting, parent, false); view = inflater.inflate(R.layout.list_item_setting, parent, false);
return new DateTimeViewHolder(view, this); return new DateTimeViewHolder(view, this);
case SettingsItem.TYPE_PREMIUM:
view = inflater.inflate(R.layout.premium_item_setting, parent, false);
return new PremiumViewHolder(view, this, mView);
default: default:
Log.error("[SettingsAdapter] Invalid view type: " + viewType); Log.error("[SettingsAdapter] Invalid view type: " + viewType);
return null; return null;
@ -144,19 +130,6 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
mView.onSettingChanged(); mView.onSettingChanged();
} }
public void onSingleChoiceClick(PremiumSingleChoiceSetting item) {
mClickedItem = item;
int value = getSelectionForSingleChoiceValue(item);
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
builder.setTitle(item.getNameId());
builder.setSingleChoiceItems(item.getChoicesId(), value, this);
mDialog = builder.show();
}
public void onSingleChoiceClick(SingleChoiceSetting item) { public void onSingleChoiceClick(SingleChoiceSetting item) {
mClickedItem = item; mClickedItem = item;
@ -172,28 +145,7 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
public void onSingleChoiceClick(SingleChoiceSetting item, int position) { public void onSingleChoiceClick(SingleChoiceSetting item, int position) {
mClickedPosition = position; mClickedPosition = position;
onSingleChoiceClick(item);
if (!item.isPremium() || MainActivity.isPremiumActive()) {
// Setting is either not Premium, or the user has Premium
onSingleChoiceClick(item);
return;
}
// User needs Premium, invoke the billing flow
MainActivity.invokePremiumBilling(() -> onSingleChoiceClick(item));
}
public void onSingleChoiceClick(PremiumSingleChoiceSetting item, int position) {
mClickedPosition = position;
if (!item.isPremium() || MainActivity.isPremiumActive()) {
// Setting is either not Premium, or the user has Premium
onSingleChoiceClick(item);
return;
}
// User needs Premium, invoke the billing flow
MainActivity.invokePremiumBilling(() -> onSingleChoiceClick(item));
} }
public void onStringSingleChoiceClick(StringSingleChoiceSetting item) { public void onStringSingleChoiceClick(StringSingleChoiceSetting item) {
@ -209,15 +161,7 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
public void onStringSingleChoiceClick(StringSingleChoiceSetting item, int position) { public void onStringSingleChoiceClick(StringSingleChoiceSetting item, int position) {
mClickedPosition = position; mClickedPosition = position;
onStringSingleChoiceClick(item);
if (!item.isPremium() || MainActivity.isPremiumActive()) {
// Setting is either not Premium, or the user has Premium
onStringSingleChoiceClick(item);
return;
}
// User needs Premium, invoke the billing flow
MainActivity.invokePremiumBilling(() -> onStringSingleChoiceClick(item));
} }
DialogInterface.OnClickListener defaultCancelListener = (dialog, which) -> closeDialog(); DialogInterface.OnClickListener defaultCancelListener = (dialog, which) -> closeDialog();
@ -309,37 +253,6 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
mView.loadSubMenu(item.getMenuKey()); mView.loadSubMenu(item.getMenuKey());
} }
public void onInputBindingClick(final InputBindingSetting item, final int position) {
final MotionAlertDialog dialog = new MotionAlertDialog(mContext, item);
dialog.setTitle(R.string.input_binding);
int messageResId = R.string.input_binding_description;
if (item.IsAxisMappingSupported() && !item.IsTrigger()) {
// Use specialized message for axis left/right or up/down
if (item.IsHorizontalOrientation()) {
messageResId = R.string.input_binding_description_horizontal_axis;
} else {
messageResId = R.string.input_binding_description_vertical_axis;
}
}
dialog.setMessage(String.format(mContext.getString(messageResId), mContext.getString(item.getNameId())));
dialog.setButton(AlertDialog.BUTTON_NEGATIVE, mContext.getString(android.R.string.cancel), this);
dialog.setButton(AlertDialog.BUTTON_NEUTRAL, mContext.getString(R.string.clear), (dialogInterface, i) ->
item.removeOldMapping());
dialog.setOnDismissListener(dialog1 ->
{
StringSetting setting = new StringSetting(item.getKey(), item.getSection(), item.getValue());
notifyItemChanged(position);
mView.putSetting(setting);
mView.onSettingChanged();
});
dialog.setCanceledOnTouchOutside(false);
dialog.show();
}
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
if (mClickedItem instanceof SingleChoiceSetting) { if (mClickedItem instanceof SingleChoiceSetting) {
@ -356,10 +269,6 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
mView.putSetting(setting); mView.putSetting(setting);
} }
closeDialog();
} else if (mClickedItem instanceof PremiumSingleChoiceSetting) {
PremiumSingleChoiceSetting scSetting = (PremiumSingleChoiceSetting) mClickedItem;
scSetting.setSelectedValue(getValueForSingleChoiceSelection(scSetting, which));
closeDialog(); closeDialog();
} else if (mClickedItem instanceof StringSingleChoiceSetting) { } else if (mClickedItem instanceof StringSingleChoiceSetting) {
StringSingleChoiceSetting scSetting = (StringSingleChoiceSetting) mClickedItem; StringSingleChoiceSetting scSetting = (StringSingleChoiceSetting) mClickedItem;
@ -436,17 +345,6 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
} }
} }
private int getValueForSingleChoiceSelection(PremiumSingleChoiceSetting item, int which) {
int valuesId = item.getValuesId();
if (valuesId > 0) {
int[] valuesArray = mContext.getResources().getIntArray(valuesId);
return valuesArray[which];
} else {
return which;
}
}
private int getSelectionForSingleChoiceValue(SingleChoiceSetting item) { private int getSelectionForSingleChoiceValue(SingleChoiceSetting item) {
int value = item.getSelectedValue(); int value = item.getSelectedValue();
int valuesId = item.getValuesId(); int valuesId = item.getValuesId();
@ -465,23 +363,4 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
return -1; return -1;
} }
private int getSelectionForSingleChoiceValue(PremiumSingleChoiceSetting item) {
int value = item.getSelectedValue();
int valuesId = item.getValuesId();
if (valuesId > 0) {
int[] valuesArray = mContext.getResources().getIntArray(valuesId);
for (int index = 0; index < valuesArray.length; index++) {
int current = valuesArray[index];
if (current == value) {
return index;
}
}
} else {
return value;
}
return -1;
}
} }

View file

@ -1,10 +1,5 @@
package org.yuzu.yuzu_emu.features.settings.ui; package org.yuzu.yuzu_emu.features.settings.ui;
import android.app.Activity;
import android.content.Context;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import android.text.TextUtils; import android.text.TextUtils;
import org.yuzu.yuzu_emu.R; import org.yuzu.yuzu_emu.R;
@ -15,20 +10,13 @@ import org.yuzu.yuzu_emu.features.settings.model.StringSetting;
import org.yuzu.yuzu_emu.features.settings.model.view.CheckBoxSetting; import org.yuzu.yuzu_emu.features.settings.model.view.CheckBoxSetting;
import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting; import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting;
import org.yuzu.yuzu_emu.features.settings.model.view.HeaderSetting; import org.yuzu.yuzu_emu.features.settings.model.view.HeaderSetting;
import org.yuzu.yuzu_emu.features.settings.model.view.InputBindingSetting;
import org.yuzu.yuzu_emu.features.settings.model.view.PremiumHeader;
import org.yuzu.yuzu_emu.features.settings.model.view.PremiumSingleChoiceSetting;
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem; import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem;
import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting; import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting;
import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting; import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting;
import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting;
import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting; import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting;
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile; import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile;
import org.yuzu.yuzu_emu.utils.Log;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
public final class SettingsFragmentPresenter { public final class SettingsFragmentPresenter {
private SettingsFragmentView mView; private SettingsFragmentView mView;
@ -106,27 +94,18 @@ public final class SettingsFragmentPresenter {
case SettingsFile.FILE_NAME_CONFIG: case SettingsFile.FILE_NAME_CONFIG:
addConfigSettings(sl); addConfigSettings(sl);
break; break;
case Settings.SECTION_PREMIUM: case Settings.SECTION_GENERAL:
addPremiumSettings(sl);
break;
case Settings.SECTION_CORE:
addGeneralSettings(sl); addGeneralSettings(sl);
break; break;
case Settings.SECTION_SYSTEM: case Settings.SECTION_SYSTEM:
addSystemSettings(sl); addSystemSettings(sl);
break; break;
case Settings.SECTION_CONTROLS:
addInputSettings(sl);
break;
case Settings.SECTION_RENDERER: case Settings.SECTION_RENDERER:
addGraphicsSettings(sl); addGraphicsSettings(sl);
break; break;
case Settings.SECTION_AUDIO: case Settings.SECTION_AUDIO:
addAudioSettings(sl); addAudioSettings(sl);
break; break;
case Settings.SECTION_DEBUG:
addDebugSettings(sl);
break;
default: default:
mView.showToastMessage("Unimplemented menu", false); mView.showToastMessage("Unimplemented menu", false);
return; return;
@ -139,184 +118,61 @@ public final class SettingsFragmentPresenter {
private void addConfigSettings(ArrayList<SettingsItem> sl) { private void addConfigSettings(ArrayList<SettingsItem> sl) {
mView.getActivity().setTitle(R.string.preferences_settings); mView.getActivity().setTitle(R.string.preferences_settings);
sl.add(new SubmenuSetting(null, null, R.string.preferences_premium, 0, Settings.SECTION_PREMIUM)); sl.add(new SubmenuSetting(null, null, R.string.preferences_general, 0, Settings.SECTION_GENERAL));
sl.add(new SubmenuSetting(null, null, R.string.preferences_general, 0, Settings.SECTION_CORE));
sl.add(new SubmenuSetting(null, null, R.string.preferences_system, 0, Settings.SECTION_SYSTEM)); sl.add(new SubmenuSetting(null, null, R.string.preferences_system, 0, Settings.SECTION_SYSTEM));
sl.add(new SubmenuSetting(null, null, R.string.preferences_controls, 0, Settings.SECTION_CONTROLS));
sl.add(new SubmenuSetting(null, null, R.string.preferences_graphics, 0, Settings.SECTION_RENDERER)); sl.add(new SubmenuSetting(null, null, R.string.preferences_graphics, 0, Settings.SECTION_RENDERER));
sl.add(new SubmenuSetting(null, null, R.string.preferences_audio, 0, Settings.SECTION_AUDIO)); sl.add(new SubmenuSetting(null, null, R.string.preferences_audio, 0, Settings.SECTION_AUDIO));
sl.add(new SubmenuSetting(null, null, R.string.preferences_debug, 0, Settings.SECTION_DEBUG));
}
private void addPremiumSettings(ArrayList<SettingsItem> sl) {
mView.getActivity().setTitle(R.string.preferences_premium);
SettingSection premiumSection = mSettings.getSection(Settings.SECTION_PREMIUM);
Setting design = premiumSection.getSetting(SettingsFile.KEY_DESIGN);
sl.add(new PremiumHeader());
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
sl.add(new PremiumSingleChoiceSetting(SettingsFile.KEY_DESIGN, Settings.SECTION_PREMIUM, R.string.design, 0, R.array.designNames, R.array.designValues, 0, design, mView));
} else {
// Pre-Android 10 does not support System Default
sl.add(new PremiumSingleChoiceSetting(SettingsFile.KEY_DESIGN, Settings.SECTION_PREMIUM, R.string.design, 0, R.array.designNamesOld, R.array.designValuesOld, 0, design, mView));
}
//Setting textureFilterName = premiumSection.getSetting(SettingsFile.KEY_TEXTURE_FILTER_NAME);
//sl.add(new StringSingleChoiceSetting(SettingsFile.KEY_TEXTURE_FILTER_NAME, Settings.SECTION_PREMIUM, R.string.texture_filter_name, R.string.texture_filter_description, textureFilterNames, textureFilterNames, "none", textureFilterName));
} }
private void addGeneralSettings(ArrayList<SettingsItem> sl) { private void addGeneralSettings(ArrayList<SettingsItem> sl) {
mView.getActivity().setTitle(R.string.preferences_general); mView.getActivity().setTitle(R.string.preferences_general);
SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER); SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER);
Setting frameLimitEnable = rendererSection.getSetting(SettingsFile.KEY_FRAME_LIMIT_ENABLED); Setting frameLimitEnable = rendererSection.getSetting(SettingsFile.KEY_RENDERER_USE_SPEED_LIMIT);
Setting frameLimitValue = rendererSection.getSetting(SettingsFile.KEY_FRAME_LIMIT); Setting frameLimitValue = rendererSection.getSetting(SettingsFile.KEY_RENDERER_SPEED_LIMIT);
sl.add(new CheckBoxSetting(SettingsFile.KEY_FRAME_LIMIT_ENABLED, Settings.SECTION_RENDERER, R.string.frame_limit_enable, R.string.frame_limit_enable_description, true, frameLimitEnable)); sl.add(new CheckBoxSetting(SettingsFile.KEY_RENDERER_USE_SPEED_LIMIT, Settings.SECTION_RENDERER, R.string.frame_limit_enable, R.string.frame_limit_enable_description, true, frameLimitEnable));
sl.add(new SliderSetting(SettingsFile.KEY_FRAME_LIMIT, Settings.SECTION_RENDERER, R.string.frame_limit_slider, R.string.frame_limit_slider_description, 1, 200, "%", 100, frameLimitValue)); sl.add(new SliderSetting(SettingsFile.KEY_RENDERER_SPEED_LIMIT, Settings.SECTION_RENDERER, R.string.frame_limit_slider, R.string.frame_limit_slider_description, 1, 200, "%", 100, frameLimitValue));
SettingSection cpuSection = mSettings.getSection(Settings.SECTION_CPU);
Setting cpuAccuracy = cpuSection.getSetting(SettingsFile.KEY_CPU_ACCURACY);
sl.add(new SingleChoiceSetting(SettingsFile.KEY_CPU_ACCURACY, Settings.SECTION_CPU, R.string.cpu_accuracy, 0, R.array.cpuAccuracyNames, R.array.cpuAccuracyValues, 0, cpuAccuracy));
} }
private void addSystemSettings(ArrayList<SettingsItem> sl) { private void addSystemSettings(ArrayList<SettingsItem> sl) {
mView.getActivity().setTitle(R.string.preferences_system); mView.getActivity().setTitle(R.string.preferences_system);
SettingSection systemSection = mSettings.getSection(Settings.SECTION_SYSTEM); SettingSection systemSection = mSettings.getSection(Settings.SECTION_SYSTEM);
Setting region = systemSection.getSetting(SettingsFile.KEY_REGION_VALUE); Setting dockedMode = systemSection.getSetting(SettingsFile.KEY_USE_DOCKED_MODE);
Setting language = systemSection.getSetting(SettingsFile.KEY_LANGUAGE); Setting region = systemSection.getSetting(SettingsFile.KEY_REGION_INDEX);
Setting systemClock = systemSection.getSetting(SettingsFile.KEY_INIT_CLOCK); Setting language = systemSection.getSetting(SettingsFile.KEY_LANGUAGE_INDEX);
Setting dateTime = systemSection.getSetting(SettingsFile.KEY_INIT_TIME);
sl.add(new SingleChoiceSetting(SettingsFile.KEY_REGION_VALUE, Settings.SECTION_SYSTEM, R.string.emulated_region, 0, R.array.regionNames, R.array.regionValues, -1, region)); sl.add(new CheckBoxSetting(SettingsFile.KEY_USE_DOCKED_MODE, Settings.SECTION_SYSTEM, R.string.use_docked_mode, R.string.use_docked_mode_description, true, dockedMode));
sl.add(new SingleChoiceSetting(SettingsFile.KEY_LANGUAGE, Settings.SECTION_SYSTEM, R.string.emulated_language, 0, R.array.languageNames, R.array.languageValues, 1, language)); sl.add(new SingleChoiceSetting(SettingsFile.KEY_REGION_INDEX, Settings.SECTION_SYSTEM, R.string.emulated_region, 0, R.array.regionNames, R.array.regionValues, -1, region));
sl.add(new SingleChoiceSetting(SettingsFile.KEY_INIT_CLOCK, Settings.SECTION_SYSTEM, R.string.init_clock, R.string.init_clock_description, R.array.systemClockNames, R.array.systemClockValues, 0, systemClock)); sl.add(new SingleChoiceSetting(SettingsFile.KEY_LANGUAGE_INDEX, Settings.SECTION_SYSTEM, R.string.emulated_language, 0, R.array.languageNames, R.array.languageValues, 1, language));
sl.add(new DateTimeSetting(SettingsFile.KEY_INIT_TIME, Settings.SECTION_SYSTEM, R.string.init_time, R.string.init_time_description, "2000-01-01 00:00:01", dateTime));
}
private void addInputSettings(ArrayList<SettingsItem> sl) {
mView.getActivity().setTitle(R.string.preferences_controls);
SettingSection controlsSection = mSettings.getSection(Settings.SECTION_CONTROLS);
Setting buttonA = controlsSection.getSetting(SettingsFile.KEY_BUTTON_A);
Setting buttonB = controlsSection.getSetting(SettingsFile.KEY_BUTTON_B);
Setting buttonX = controlsSection.getSetting(SettingsFile.KEY_BUTTON_X);
Setting buttonY = controlsSection.getSetting(SettingsFile.KEY_BUTTON_Y);
Setting buttonSelect = controlsSection.getSetting(SettingsFile.KEY_BUTTON_SELECT);
Setting buttonStart = controlsSection.getSetting(SettingsFile.KEY_BUTTON_START);
Setting circlepadAxisVert = controlsSection.getSetting(SettingsFile.KEY_CIRCLEPAD_AXIS_VERTICAL);
Setting circlepadAxisHoriz = controlsSection.getSetting(SettingsFile.KEY_CIRCLEPAD_AXIS_HORIZONTAL);
Setting cstickAxisVert = controlsSection.getSetting(SettingsFile.KEY_CSTICK_AXIS_VERTICAL);
Setting cstickAxisHoriz = controlsSection.getSetting(SettingsFile.KEY_CSTICK_AXIS_HORIZONTAL);
Setting dpadAxisVert = controlsSection.getSetting(SettingsFile.KEY_DPAD_AXIS_VERTICAL);
Setting dpadAxisHoriz = controlsSection.getSetting(SettingsFile.KEY_DPAD_AXIS_HORIZONTAL);
// Setting buttonUp = controlsSection.getSetting(SettingsFile.KEY_BUTTON_UP);
// Setting buttonDown = controlsSection.getSetting(SettingsFile.KEY_BUTTON_DOWN);
// Setting buttonLeft = controlsSection.getSetting(SettingsFile.KEY_BUTTON_LEFT);
// Setting buttonRight = controlsSection.getSetting(SettingsFile.KEY_BUTTON_RIGHT);
Setting buttonL = controlsSection.getSetting(SettingsFile.KEY_BUTTON_L);
Setting buttonR = controlsSection.getSetting(SettingsFile.KEY_BUTTON_R);
Setting buttonZL = controlsSection.getSetting(SettingsFile.KEY_BUTTON_ZL);
Setting buttonZR = controlsSection.getSetting(SettingsFile.KEY_BUTTON_ZR);
sl.add(new HeaderSetting(null, null, R.string.generic_buttons, 0));
sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_A, Settings.SECTION_CONTROLS, R.string.button_a, buttonA));
sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_B, Settings.SECTION_CONTROLS, R.string.button_b, buttonB));
sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_X, Settings.SECTION_CONTROLS, R.string.button_x, buttonX));
sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_Y, Settings.SECTION_CONTROLS, R.string.button_y, buttonY));
sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_SELECT, Settings.SECTION_CONTROLS, R.string.button_select, buttonSelect));
sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_START, Settings.SECTION_CONTROLS, R.string.button_start, buttonStart));
sl.add(new HeaderSetting(null, null, R.string.controller_circlepad, 0));
sl.add(new InputBindingSetting(SettingsFile.KEY_CIRCLEPAD_AXIS_VERTICAL, Settings.SECTION_CONTROLS, R.string.controller_axis_vertical, circlepadAxisVert));
sl.add(new InputBindingSetting(SettingsFile.KEY_CIRCLEPAD_AXIS_HORIZONTAL, Settings.SECTION_CONTROLS, R.string.controller_axis_horizontal, circlepadAxisHoriz));
sl.add(new HeaderSetting(null, null, R.string.controller_c, 0));
sl.add(new InputBindingSetting(SettingsFile.KEY_CSTICK_AXIS_VERTICAL, Settings.SECTION_CONTROLS, R.string.controller_axis_vertical, cstickAxisVert));
sl.add(new InputBindingSetting(SettingsFile.KEY_CSTICK_AXIS_HORIZONTAL, Settings.SECTION_CONTROLS, R.string.controller_axis_horizontal, cstickAxisHoriz));
sl.add(new HeaderSetting(null, null, R.string.controller_dpad, 0));
sl.add(new InputBindingSetting(SettingsFile.KEY_DPAD_AXIS_VERTICAL, Settings.SECTION_CONTROLS, R.string.controller_axis_vertical, dpadAxisVert));
sl.add(new InputBindingSetting(SettingsFile.KEY_DPAD_AXIS_HORIZONTAL, Settings.SECTION_CONTROLS, R.string.controller_axis_horizontal, dpadAxisHoriz));
// TODO(bunnei): Figure out what to do with these. Configuring is functional, but removing for MVP because they are confusing.
// sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_UP, Settings.SECTION_CONTROLS, R.string.generic_up, buttonUp));
// sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_DOWN, Settings.SECTION_CONTROLS, R.string.generic_down, buttonDown));
// sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_LEFT, Settings.SECTION_CONTROLS, R.string.generic_left, buttonLeft));
// sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_RIGHT, Settings.SECTION_CONTROLS, R.string.generic_right, buttonRight));
sl.add(new HeaderSetting(null, null, R.string.controller_triggers, 0));
sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_L, Settings.SECTION_CONTROLS, R.string.button_l, buttonL));
sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_R, Settings.SECTION_CONTROLS, R.string.button_r, buttonR));
sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_ZL, Settings.SECTION_CONTROLS, R.string.button_zl, buttonZL));
sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_ZR, Settings.SECTION_CONTROLS, R.string.button_zr, buttonZR));
} }
private void addGraphicsSettings(ArrayList<SettingsItem> sl) { private void addGraphicsSettings(ArrayList<SettingsItem> sl) {
mView.getActivity().setTitle(R.string.preferences_graphics); mView.getActivity().setTitle(R.string.preferences_graphics);
SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER); SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER);
Setting resolutionFactor = rendererSection.getSetting(SettingsFile.KEY_RESOLUTION_FACTOR); Setting rendererBackend = rendererSection.getSetting(SettingsFile.KEY_RENDERER_BACKEND);
Setting filterMode = rendererSection.getSetting(SettingsFile.KEY_FILTER_MODE); Setting rendererAccuracy = rendererSection.getSetting(SettingsFile.KEY_RENDERER_ACCURACY);
Setting shadersAccurateMul = rendererSection.getSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL); Setting rendererReolution = rendererSection.getSetting(SettingsFile.KEY_RENDERER_RESOLUTION);
Setting render3dMode = rendererSection.getSetting(SettingsFile.KEY_RENDER_3D); Setting rendererAsynchronousShaders = rendererSection.getSetting(SettingsFile.KEY_RENDERER_ASYNCHRONOUS_SHADERS);
Setting factor3d = rendererSection.getSetting(SettingsFile.KEY_FACTOR_3D);
Setting useDiskShaderCache = rendererSection.getSetting(SettingsFile.KEY_USE_DISK_SHADER_CACHE);
SettingSection layoutSection = mSettings.getSection(Settings.SECTION_LAYOUT);
Setting cardboardScreenSize = layoutSection.getSetting(SettingsFile.KEY_CARDBOARD_SCREEN_SIZE);
Setting cardboardXShift = layoutSection.getSetting(SettingsFile.KEY_CARDBOARD_X_SHIFT);
Setting cardboardYShift = layoutSection.getSetting(SettingsFile.KEY_CARDBOARD_Y_SHIFT);
SettingSection utilitySection = mSettings.getSection(Settings.SECTION_UTILITY);
Setting dumpTextures = utilitySection.getSetting(SettingsFile.KEY_DUMP_TEXTURES);
Setting customTextures = utilitySection.getSetting(SettingsFile.KEY_CUSTOM_TEXTURES);
//Setting preloadTextures = utilitySection.getSetting(SettingsFile.KEY_PRELOAD_TEXTURES);
sl.add(new HeaderSetting(null, null, R.string.renderer, 0)); sl.add(new SingleChoiceSetting(SettingsFile.KEY_RENDERER_BACKEND, Settings.SECTION_RENDERER, R.string.renderer_api, 0, R.array.rendererApiNames, R.array.rendererApiValues, 0, rendererBackend));
sl.add(new SliderSetting(SettingsFile.KEY_RESOLUTION_FACTOR, Settings.SECTION_RENDERER, R.string.internal_resolution, R.string.internal_resolution_description, 1, 4, "x", 1, resolutionFactor)); sl.add(new SingleChoiceSetting(SettingsFile.KEY_RENDERER_ACCURACY, Settings.SECTION_RENDERER, R.string.renderer_accuracy, 0, R.array.rendererAccuracyNames, R.array.rendererAccuracyValues, 1, rendererAccuracy));
sl.add(new CheckBoxSetting(SettingsFile.KEY_FILTER_MODE, Settings.SECTION_RENDERER, R.string.linear_filtering, R.string.linear_filtering_description, true, filterMode)); sl.add(new SingleChoiceSetting(SettingsFile.KEY_RENDERER_RESOLUTION, Settings.SECTION_RENDERER, R.string.renderer_resolution, 0, R.array.rendererResolutionNames, R.array.rendererResolutionValues, 2, rendererReolution));
sl.add(new CheckBoxSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL, Settings.SECTION_RENDERER, R.string.shaders_accurate_mul, R.string.shaders_accurate_mul_description, false, shadersAccurateMul)); sl.add(new CheckBoxSetting(SettingsFile.KEY_RENDERER_ASYNCHRONOUS_SHADERS, Settings.SECTION_RENDERER, R.string.renderer_asynchronous_shaders, R.string.renderer_asynchronous_shaders_description, false, rendererAsynchronousShaders));
sl.add(new CheckBoxSetting(SettingsFile.KEY_USE_DISK_SHADER_CACHE, Settings.SECTION_RENDERER, R.string.use_disk_shader_cache, R.string.use_disk_shader_cache_description, true, useDiskShaderCache));
sl.add(new HeaderSetting(null, null, R.string.stereoscopy, 0));
sl.add(new SingleChoiceSetting(SettingsFile.KEY_RENDER_3D, Settings.SECTION_RENDERER, R.string.render3d, 0, R.array.render3dModes, R.array.render3dValues, 0, render3dMode));
sl.add(new SliderSetting(SettingsFile.KEY_FACTOR_3D, Settings.SECTION_RENDERER, R.string.factor3d, R.string.factor3d_description, 0, 100, "%", 0, factor3d));
sl.add(new HeaderSetting(null, null, R.string.cardboard_vr, 0));
sl.add(new SliderSetting(SettingsFile.KEY_CARDBOARD_SCREEN_SIZE, Settings.SECTION_LAYOUT, R.string.cardboard_screen_size, R.string.cardboard_screen_size_description, 30, 100, "%", 85, cardboardScreenSize));
sl.add(new SliderSetting(SettingsFile.KEY_CARDBOARD_X_SHIFT, Settings.SECTION_LAYOUT, R.string.cardboard_x_shift, R.string.cardboard_x_shift_description, -100, 100, "%", 0, cardboardXShift));
sl.add(new SliderSetting(SettingsFile.KEY_CARDBOARD_Y_SHIFT, Settings.SECTION_LAYOUT, R.string.cardboard_y_shift, R.string.cardboard_y_shift_description, -100, 100, "%", 0, cardboardYShift));
sl.add(new HeaderSetting(null, null, R.string.utility, 0));
sl.add(new CheckBoxSetting(SettingsFile.KEY_DUMP_TEXTURES, Settings.SECTION_UTILITY, R.string.dump_textures, R.string.dump_textures_description, false, dumpTextures));
sl.add(new CheckBoxSetting(SettingsFile.KEY_CUSTOM_TEXTURES, Settings.SECTION_UTILITY, R.string.custom_textures, R.string.custom_textures_description, false, customTextures));
//Disabled until custom texture implementation gets rewrite, current one overloads RAM and crashes yuzu.
//sl.add(new CheckBoxSetting(SettingsFile.KEY_PRELOAD_TEXTURES, Settings.SECTION_UTILITY, R.string.preload_textures, R.string.preload_textures_description, false, preloadTextures));
} }
private void addAudioSettings(ArrayList<SettingsItem> sl) { private void addAudioSettings(ArrayList<SettingsItem> sl) {
mView.getActivity().setTitle(R.string.preferences_audio); mView.getActivity().setTitle(R.string.preferences_audio);
SettingSection audioSection = mSettings.getSection(Settings.SECTION_AUDIO); SettingSection audioSection = mSettings.getSection(Settings.SECTION_AUDIO);
Setting audioStretch = audioSection.getSetting(SettingsFile.KEY_ENABLE_AUDIO_STRETCHING); Setting audioVolume = audioSection.getSetting(SettingsFile.KEY_AUDIO_VOLUME);
Setting micInputType = audioSection.getSetting(SettingsFile.KEY_MIC_INPUT_TYPE);
sl.add(new CheckBoxSetting(SettingsFile.KEY_ENABLE_AUDIO_STRETCHING, Settings.SECTION_AUDIO, R.string.audio_stretch, R.string.audio_stretch_description, true, audioStretch)); sl.add(new SliderSetting(SettingsFile.KEY_AUDIO_VOLUME, Settings.SECTION_AUDIO, R.string.audio_volume, R.string.audio_volume_description, 0, 100, "%", 100, audioVolume));
sl.add(new SingleChoiceSetting(SettingsFile.KEY_MIC_INPUT_TYPE, Settings.SECTION_AUDIO, R.string.audio_input_type, 0, R.array.audioInputTypeNames, R.array.audioInputTypeValues, 1, micInputType));
}
private void addDebugSettings(ArrayList<SettingsItem> sl) {
mView.getActivity().setTitle(R.string.preferences_debug);
SettingSection coreSection = mSettings.getSection(Settings.SECTION_CORE);
SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER);
Setting useCpuJit = coreSection.getSetting(SettingsFile.KEY_CPU_JIT);
Setting hardwareRenderer = rendererSection.getSetting(SettingsFile.KEY_HW_RENDERER);
Setting hardwareShader = rendererSection.getSetting(SettingsFile.KEY_HW_SHADER);
Setting vsyncEnable = rendererSection.getSetting(SettingsFile.KEY_USE_VSYNC);
sl.add(new HeaderSetting(null, null, R.string.debug_warning, 0));
sl.add(new CheckBoxSetting(SettingsFile.KEY_CPU_JIT, Settings.SECTION_CORE, R.string.cpu_jit, R.string.cpu_jit_description, true, useCpuJit, true, mView));
sl.add(new CheckBoxSetting(SettingsFile.KEY_HW_RENDERER, Settings.SECTION_RENDERER, R.string.hw_renderer, R.string.hw_renderer_description, true, hardwareRenderer, true, mView));
sl.add(new CheckBoxSetting(SettingsFile.KEY_HW_SHADER, Settings.SECTION_RENDERER, R.string.hw_shaders, R.string.hw_shaders_description, true, hardwareShader, true, mView));
sl.add(new CheckBoxSetting(SettingsFile.KEY_USE_VSYNC, Settings.SECTION_RENDERER, R.string.vsync, R.string.vsync_description, true, vsyncEnable));
} }
} }

View file

@ -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());
}
}

View file

@ -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);
}
}
}

View file

@ -5,7 +5,6 @@ import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import org.yuzu.yuzu_emu.R; import org.yuzu.yuzu_emu.R;
import org.yuzu.yuzu_emu.features.settings.model.view.PremiumSingleChoiceSetting;
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem; import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem;
import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting; import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting;
import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting; import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting;
@ -46,17 +45,6 @@ public final class SingleChoiceViewHolder extends SettingViewHolder {
mTextSettingDescription.setText(choices[i]); mTextSettingDescription.setText(choices[i]);
} }
} }
} else if (item instanceof PremiumSingleChoiceSetting) {
PremiumSingleChoiceSetting setting = (PremiumSingleChoiceSetting) item;
int selected = setting.getSelectedValue();
Resources resMgr = mTextSettingDescription.getContext().getResources();
String[] choices = resMgr.getStringArray(setting.getChoicesId());
int[] values = resMgr.getIntArray(setting.getValuesId());
for (int i = 0; i < values.length; ++i) {
if (values[i] == selected) {
mTextSettingDescription.setText(choices[i]);
}
}
} else { } else {
mTextSettingDescription.setVisibility(View.GONE); mTextSettingDescription.setVisibility(View.GONE);
} }
@ -67,8 +55,6 @@ public final class SingleChoiceViewHolder extends SettingViewHolder {
int position = getAdapterPosition(); int position = getAdapterPosition();
if (mItem instanceof SingleChoiceSetting) { if (mItem instanceof SingleChoiceSetting) {
getAdapter().onSingleChoiceClick((SingleChoiceSetting) mItem, position); getAdapter().onSingleChoiceClick((SingleChoiceSetting) mItem, position);
} else if (mItem instanceof PremiumSingleChoiceSetting) {
getAdapter().onSingleChoiceClick((PremiumSingleChoiceSetting) mItem, position);
} else if (mItem instanceof StringSingleChoiceSetting) { } else if (mItem instanceof StringSingleChoiceSetting) {
getAdapter().onStringSingleChoiceClick((StringSingleChoiceSetting) mItem, position); getAdapter().onStringSingleChoiceClick((StringSingleChoiceSetting) mItem, position);
} }

View file

@ -33,95 +33,23 @@ import java.util.TreeSet;
public final class SettingsFile { public final class SettingsFile {
public static final String FILE_NAME_CONFIG = "config"; public static final String FILE_NAME_CONFIG = "config";
public static final String KEY_CPU_JIT = "use_cpu_jit";
public static final String KEY_DESIGN = "design"; public static final String KEY_DESIGN = "design";
public static final String KEY_PREMIUM = "premium"; // CPU
public static final String KEY_CPU_ACCURACY = "cpu_accuracy";
public static final String KEY_HW_RENDERER = "use_hw_renderer"; // System
public static final String KEY_HW_SHADER = "use_hw_shader"; public static final String KEY_USE_DOCKED_MODE = "use_docked_mode";
public static final String KEY_SHADERS_ACCURATE_MUL = "shaders_accurate_mul"; public static final String KEY_REGION_INDEX = "region_index";
public static final String KEY_USE_SHADER_JIT = "use_shader_jit"; public static final String KEY_LANGUAGE_INDEX = "language_index";
public static final String KEY_USE_DISK_SHADER_CACHE = "use_disk_shader_cache"; public static final String KEY_RENDERER_BACKEND = "backend";
public static final String KEY_USE_VSYNC = "use_vsync_new"; // Renderer
public static final String KEY_RESOLUTION_FACTOR = "resolution_factor"; public static final String KEY_RENDERER_RESOLUTION = "resolution_setup";
public static final String KEY_FRAME_LIMIT_ENABLED = "use_frame_limit"; public static final String KEY_RENDERER_ACCURACY = "gpu_accuracy";
public static final String KEY_FRAME_LIMIT = "frame_limit"; public static final String KEY_RENDERER_ASYNCHRONOUS_SHADERS = "use_asynchronous_shaders";
public static final String KEY_BACKGROUND_RED = "bg_red"; public static final String KEY_RENDERER_USE_SPEED_LIMIT = "use_speed_limit";
public static final String KEY_BACKGROUND_BLUE = "bg_blue"; public static final String KEY_RENDERER_SPEED_LIMIT = "speed_limit";
public static final String KEY_BACKGROUND_GREEN = "bg_green"; // Audio
public static final String KEY_RENDER_3D = "render_3d"; public static final String KEY_AUDIO_VOLUME = "volume";
public static final String KEY_FACTOR_3D = "factor_3d";
public static final String KEY_PP_SHADER_NAME = "pp_shader_name";
public static final String KEY_FILTER_MODE = "filter_mode";
public static final String KEY_TEXTURE_FILTER_NAME = "texture_filter_name";
public static final String KEY_USE_ASYNCHRONOUS_GPU_EMULATION = "use_asynchronous_gpu_emulation";
public static final String KEY_LAYOUT_OPTION = "layout_option";
public static final String KEY_SWAP_SCREEN = "swap_screen";
public static final String KEY_CARDBOARD_SCREEN_SIZE = "cardboard_screen_size";
public static final String KEY_CARDBOARD_X_SHIFT = "cardboard_x_shift";
public static final String KEY_CARDBOARD_Y_SHIFT = "cardboard_y_shift";
public static final String KEY_DUMP_TEXTURES = "dump_textures";
public static final String KEY_CUSTOM_TEXTURES = "custom_textures";
public static final String KEY_PRELOAD_TEXTURES = "preload_textures";
public static final String KEY_AUDIO_OUTPUT_ENGINE = "output_engine";
public static final String KEY_ENABLE_AUDIO_STRETCHING = "enable_audio_stretching";
public static final String KEY_VOLUME = "volume";
public static final String KEY_MIC_INPUT_TYPE = "mic_input_type";
public static final String KEY_USE_VIRTUAL_SD = "use_virtual_sd";
public static final String KEY_IS_NEW_3DS = "is_new_3ds";
public static final String KEY_REGION_VALUE = "region_value";
public static final String KEY_LANGUAGE = "language";
public static final String KEY_INIT_CLOCK = "init_clock";
public static final String KEY_INIT_TIME = "init_time";
public static final String KEY_BUTTON_A = "button_a";
public static final String KEY_BUTTON_B = "button_b";
public static final String KEY_BUTTON_X = "button_x";
public static final String KEY_BUTTON_Y = "button_y";
public static final String KEY_BUTTON_SELECT = "button_select";
public static final String KEY_BUTTON_START = "button_start";
public static final String KEY_BUTTON_UP = "button_up";
public static final String KEY_BUTTON_DOWN = "button_down";
public static final String KEY_BUTTON_LEFT = "button_left";
public static final String KEY_BUTTON_RIGHT = "button_right";
public static final String KEY_BUTTON_L = "button_l";
public static final String KEY_BUTTON_R = "button_r";
public static final String KEY_BUTTON_ZL = "button_zl";
public static final String KEY_BUTTON_ZR = "button_zr";
public static final String KEY_CIRCLEPAD_AXIS_VERTICAL = "circlepad_axis_vertical";
public static final String KEY_CIRCLEPAD_AXIS_HORIZONTAL = "circlepad_axis_horizontal";
public static final String KEY_CSTICK_AXIS_VERTICAL = "cstick_axis_vertical";
public static final String KEY_CSTICK_AXIS_HORIZONTAL = "cstick_axis_horizontal";
public static final String KEY_DPAD_AXIS_VERTICAL = "dpad_axis_vertical";
public static final String KEY_DPAD_AXIS_HORIZONTAL = "dpad_axis_horizontal";
public static final String KEY_CIRCLEPAD_UP = "circlepad_up";
public static final String KEY_CIRCLEPAD_DOWN = "circlepad_down";
public static final String KEY_CIRCLEPAD_LEFT = "circlepad_left";
public static final String KEY_CIRCLEPAD_RIGHT = "circlepad_right";
public static final String KEY_CSTICK_UP = "cstick_up";
public static final String KEY_CSTICK_DOWN = "cstick_down";
public static final String KEY_CSTICK_LEFT = "cstick_left";
public static final String KEY_CSTICK_RIGHT = "cstick_right";
public static final String KEY_CAMERA_OUTER_RIGHT_NAME = "camera_outer_right_name";
public static final String KEY_CAMERA_OUTER_RIGHT_CONFIG = "camera_outer_right_config";
public static final String KEY_CAMERA_OUTER_RIGHT_FLIP = "camera_outer_right_flip";
public static final String KEY_CAMERA_OUTER_LEFT_NAME = "camera_outer_left_name";
public static final String KEY_CAMERA_OUTER_LEFT_CONFIG = "camera_outer_left_config";
public static final String KEY_CAMERA_OUTER_LEFT_FLIP = "camera_outer_left_flip";
public static final String KEY_CAMERA_INNER_NAME = "camera_inner_name";
public static final String KEY_CAMERA_INNER_CONFIG = "camera_inner_config";
public static final String KEY_CAMERA_INNER_FLIP = "camera_inner_flip";
public static final String KEY_LOG_FILTER = "log_filter";
private static BiMap<String, String> sectionsMap = new BiMap<>(); private static BiMap<String, String> sectionsMap = new BiMap<>();

View file

@ -29,7 +29,6 @@ import org.yuzu.yuzu_emu.overlay.InputOverlay;
import org.yuzu.yuzu_emu.utils.DirectoryInitialization; import org.yuzu.yuzu_emu.utils.DirectoryInitialization;
import org.yuzu.yuzu_emu.utils.DirectoryInitialization.DirectoryInitializationState; import org.yuzu.yuzu_emu.utils.DirectoryInitialization.DirectoryInitializationState;
import org.yuzu.yuzu_emu.utils.DirectoryStateReceiver; import org.yuzu.yuzu_emu.utils.DirectoryStateReceiver;
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings;
import org.yuzu.yuzu_emu.utils.Log; import org.yuzu.yuzu_emu.utils.Log;
public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback, Choreographer.FrameCallback { public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback, Choreographer.FrameCallback {

View file

@ -18,7 +18,6 @@ import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity;
import org.yuzu.yuzu_emu.model.GameProvider; import org.yuzu.yuzu_emu.model.GameProvider;
import org.yuzu.yuzu_emu.ui.platform.PlatformGamesFragment; import org.yuzu.yuzu_emu.ui.platform.PlatformGamesFragment;
import org.yuzu.yuzu_emu.utils.AddDirectoryHelper; import org.yuzu.yuzu_emu.utils.AddDirectoryHelper;
import org.yuzu.yuzu_emu.utils.BillingManager;
import org.yuzu.yuzu_emu.utils.DirectoryInitialization; import org.yuzu.yuzu_emu.utils.DirectoryInitialization;
import org.yuzu.yuzu_emu.utils.FileBrowserHelper; import org.yuzu.yuzu_emu.utils.FileBrowserHelper;
import org.yuzu.yuzu_emu.utils.PermissionsHandler; import org.yuzu.yuzu_emu.utils.PermissionsHandler;
@ -40,11 +39,6 @@ public final class MainActivity extends AppCompatActivity implements MainView {
private MainPresenter mPresenter = new MainPresenter(this); private MainPresenter mPresenter = new MainPresenter(this);
// Singleton to manage user billing state
private static BillingManager mBillingManager;
private static MenuItem mPremiumButton;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
ThemeUtil.applyTheme(); ThemeUtil.applyTheme();
@ -71,9 +65,6 @@ public final class MainActivity extends AppCompatActivity implements MainView {
} }
PicassoUtils.init(); PicassoUtils.init();
// Setup billing manager, so we can globally query for Premium status
mBillingManager = new BillingManager(this);
// Dismiss previous notifications (should not happen unless a crash occurred) // Dismiss previous notifications (should not happen unless a crash occurred)
EmulationActivity.tryDismissRunningNotification(this); EmulationActivity.tryDismissRunningNotification(this);
} }
@ -107,22 +98,10 @@ public final class MainActivity extends AppCompatActivity implements MainView {
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater(); MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_game_grid, menu); inflater.inflate(R.menu.menu_game_grid, menu);
mPremiumButton = menu.findItem(R.id.button_premium);
if (mBillingManager.isPremiumCached()) {
// User had premium in a previous session, hide upsell option
setPremiumButtonVisible(false);
}
return true; return true;
} }
static public void setPremiumButtonVisible(boolean isVisible) {
if (mPremiumButton != null) {
mPremiumButton.setVisible(isVisible);
}
}
/** /**
* MainView * MainView
*/ */
@ -155,15 +134,8 @@ public final class MainActivity extends AppCompatActivity implements MainView {
FileBrowserHelper.openDirectoryPicker(this, FileBrowserHelper.openDirectoryPicker(this,
MainPresenter.REQUEST_ADD_DIRECTORY, MainPresenter.REQUEST_ADD_DIRECTORY,
R.string.select_game_folder, R.string.select_game_folder,
Arrays.asList("xci", "nsp", "cci", "3ds", Arrays.asList("nso", "nro", "nca", "xci",
"cxi", "app", "3dsx", "cia", "nsp", "kip"));
"rar", "zip", "7z", "torrent",
"tar", "gz", "nro"));
break;
case MainPresenter.REQUEST_INSTALL_CIA:
FileBrowserHelper.openFilePicker(this, MainPresenter.REQUEST_INSTALL_CIA,
R.string.install_cia_title,
Collections.singletonList("cia"), true);
break; break;
} }
} else { } else {
@ -191,12 +163,6 @@ public final class MainActivity extends AppCompatActivity implements MainView {
mPresenter.onDirectorySelected(FileBrowserHelper.getSelectedDirectory(result)); mPresenter.onDirectorySelected(FileBrowserHelper.getSelectedDirectory(result));
} }
break; break;
case MainPresenter.REQUEST_INSTALL_CIA:
// If the user picked a file, as opposed to just backing out.
if (resultCode == MainActivity.RESULT_OK) {
mPresenter.refeshGameList();
}
break;
} }
} }
@ -248,20 +214,4 @@ public final class MainActivity extends AppCompatActivity implements MainView {
EmulationActivity.tryDismissRunningNotification(this); EmulationActivity.tryDismissRunningNotification(this);
super.onDestroy(); super.onDestroy();
} }
/**
* @return true if Premium subscription is currently active
*/
public static boolean isPremiumActive() {
return mBillingManager.isPremiumActive();
}
/**
* Invokes the billing flow for Premium
*
* @param callback Optional callback, called once, on completion of billing
*/
public static void invokePremiumBilling(Runnable callback) {
mBillingManager.invokePremiumBilling(callback);
}
} }

View file

@ -5,15 +5,12 @@ import android.os.SystemClock;
import org.yuzu.yuzu_emu.BuildConfig; import org.yuzu.yuzu_emu.BuildConfig;
import org.yuzu.yuzu_emu.YuzuApplication; import org.yuzu.yuzu_emu.YuzuApplication;
import org.yuzu.yuzu_emu.R; import org.yuzu.yuzu_emu.R;
import org.yuzu.yuzu_emu.features.settings.model.Settings;
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile; import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile;
import org.yuzu.yuzu_emu.model.GameDatabase; import org.yuzu.yuzu_emu.model.GameDatabase;
import org.yuzu.yuzu_emu.utils.AddDirectoryHelper; import org.yuzu.yuzu_emu.utils.AddDirectoryHelper;
public final class MainPresenter { public final class MainPresenter {
public static final int REQUEST_ADD_DIRECTORY = 1; public static final int REQUEST_ADD_DIRECTORY = 1;
public static final int REQUEST_INSTALL_CIA = 2;
private final MainView mView; private final MainView mView;
private String mDirToAdd; private String mDirToAdd;
private long mLastClickTime = 0; private long mLastClickTime = 0;
@ -49,14 +46,6 @@ public final class MainPresenter {
case R.id.button_add_directory: case R.id.button_add_directory:
launchFileListActivity(REQUEST_ADD_DIRECTORY); launchFileListActivity(REQUEST_ADD_DIRECTORY);
return true; return true;
case R.id.button_install_cia:
launchFileListActivity(REQUEST_INSTALL_CIA);
return true;
case R.id.button_premium:
mView.launchSettingsActivity(Settings.SECTION_PREMIUM);
return true;
} }
return false; return false;

View file

@ -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);
}
}
}

View file

@ -1,4 +1,7 @@
add_library(yuzu-android SHARED add_library(yuzu-android SHARED
config.cpp
config.h
default_ini.h
emu_window/emu_window.cpp emu_window/emu_window.cpp
emu_window/emu_window.h emu_window/emu_window.h
id_cache.cpp id_cache.cpp

View file

@ -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();
}

View file

@ -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);
};

View file

@ -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

View file

@ -19,6 +19,7 @@
#include "core/file_sys/vfs_real.h" #include "core/file_sys/vfs_real.h"
#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/filesystem.h"
#include "core/perf_stats.h" #include "core/perf_stats.h"
#include "jni/config.h"
#include "jni/emu_window/emu_window.h" #include "jni/emu_window/emu_window.h"
#include "jni/id_cache.h" #include "jni/id_cache.h"
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
@ -67,6 +68,9 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
return Core::SystemResultStatus::ErrorLoader; return Core::SystemResultStatus::ErrorLoader;
} }
// Loads the configuration.
Config{};
system_.Initialize(); system_.Initialize();
system_.ApplySettings(); system_.ApplySettings();
@ -245,7 +249,9 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGitRevision([[maybe_unused]] JN
} }
void Java_org_yuzu_yuzu_1emu_NativeLibrary_CreateConfigFile void Java_org_yuzu_yuzu_1emu_NativeLibrary_CreateConfigFile
[[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) {} [[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) {
Config{};
}
jint Java_org_yuzu_yuzu_1emu_NativeLibrary_DefaultCPUCore([[maybe_unused]] JNIEnv* env, jint Java_org_yuzu_yuzu_1emu_NativeLibrary_DefaultCPUCore([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) { [[maybe_unused]] jclass clazz) {
@ -257,7 +263,9 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_St
[[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {} [[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadSettings([[maybe_unused]] JNIEnv* env, void Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadSettings([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {} [[maybe_unused]] jclass clazz) {
Config{};
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_GetUserSetting([[maybe_unused]] JNIEnv* env, jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_GetUserSetting([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz, [[maybe_unused]] jclass clazz,

View file

@ -2,11 +2,6 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/button_premium"
android:icon="@drawable/ic_premium"
android:title="@string/premium_text"
app:showAsAction="ifRoom" />
<item <item
android:id="@+id/button_file_menu" android:id="@+id/button_file_menu"
android:icon="@drawable/ic_folder" android:icon="@drawable/ic_folder"

View file

@ -61,16 +61,22 @@
<string-array name="languageNames"> <string-array name="languageNames">
<item>Japanese (日本語)</item> <item>Japanese (日本語)</item>
<item>English</item> <item>English</item>
<item>French (français)</item> <item>French (Français)</item>
<item>German (Deutsch)</item> <item>German (Deutsch)</item>
<item>Italian (italiano)</item> <item>Italian (Italiano)</item>
<item>Spanish (español)</item> <item>Spanish (Español)</item>
<item>Simplified Chinese (简体中文)</item> <item>Chinese (简体中文)</item>
<item>Korean (한국어)</item> <item>Korean (한국어)</item>
<item>Dutch (Nederlands)</item> <item>Dutch (Nederlands)</item>
<item>Portuguese (português)</item> <item>Portuguese (Português)</item>
<item>Russian (Русский)</item> <item>Russian (Русский)</item>
<item>Traditional Chinese (正體中文)</item> <item>Taiwanese (台湾)</item>
<item>British English</item>
<item>Canadian French (Français canadien)</item>
<item>Latin American Spanish (Español latinoamericano)</item>
<item>Simplified Chinese (简体中文)</item>
<item>Traditional Chinese (正體中文))</item>
<item>Brazilian Portuguese (Portugues do Brasil)</item>
</string-array> </string-array>
<integer-array name="languageValues"> <integer-array name="languageValues">
@ -86,84 +92,46 @@
<item>9</item> <item>9</item>
<item>10</item> <item>10</item>
<item>11</item> <item>11</item>
<item>12</item>
<item>13</item>
<item>14</item>
<item>15</item>
<item>16</item>
<item>17</item>
</integer-array> </integer-array>
<string-array name="n3dsButtons"> <string-array name="rendererApiNames">
<item>a</item> <item>Vulkan</item>
<item>b</item>
<item>x</item>
<item>y</item>
<item>L</item>
<item>R</item>
<item>ZL</item>
<item>ZR</item>
<item>Start</item>
<item>Select</item>
<item>D-Pad</item>
<item>Circle Pad</item>
<item>C Stick</item>
</string-array>
<string-array name="cameraImageSourceNames">
<item>Blank</item>
<item>Still Image</item>
<item>Device Camera</item>
</string-array>
<string-array name="cameraImageSourceValues">
<item>blank</item>
<item>image</item>
<item>ndk</item>
</string-array>
<string-array name="cameraDeviceNames">
<item>Default</item>
<item>Any Front Camera</item>
<item>Any Back Camera</item>
</string-array>
<string-array name="cameraDeviceValues">
<item />
<item>_front</item>
<item>_back</item>
</string-array>
<string-array name="cameraFlipNames">
<item>None</item> <item>None</item>
<item>Horizontal</item>
<item>Vertical</item>
<item>Reverse</item>
</string-array> </string-array>
<integer-array name="cameraFlipValues"> <integer-array name="rendererApiValues">
<item>0</item>
<item>1</item> <item>1</item>
<item>2</item> <item>2</item>
<item>3</item>
</integer-array> </integer-array>
<string-array name="audioInputTypeNames"> <string-array name="rendererAccuracyNames">
<item>None</item> <item>Normal</item>
<item>Real Device</item> <item>High</item>
<item>Static Noise</item> <item>Extreme (Slow)</item>
</string-array> </string-array>
<integer-array name="audioInputTypeValues"> <integer-array name="rendererAccuracyValues">
<item>0</item> <item>0</item>
<item>1</item> <item>1</item>
<item>2</item> <item>2</item>
</integer-array> </integer-array>
<string-array name="render3dModes"> <string-array name="rendererResolutionNames">
<item>Off</item> <item>0.5X (360p/540p)</item>
<item>Side by Side</item> <item>0.75X (540p/810p)</item>
<item>Anaglyph</item> <item>1X (720p/1080p)</item>
<item>Interlaced</item> <item>2X (1440p/2160p) (Slow)</item>
<item>Reverse Interlaced</item> <item>3X (2160p/3240p) (Slow)</item>
<item>Cardboard VR</item> <item>4X (2880p/4320p) (Slow)</item>
</string-array> </string-array>
<integer-array name="render3dValues"> <integer-array name="rendererResolutionValues">
<item>0</item> <item>0</item>
<item>1</item> <item>1</item>
<item>2</item> <item>2</item>
@ -171,4 +139,18 @@
<item>4</item> <item>4</item>
<item>5</item> <item>5</item>
</integer-array> </integer-array>
<string-array name="cpuAccuracyNames">
<item>Auto</item>
<item>Accurate</item>
<item>Unsafe</item>
<item>Paranoid (Slow)</item>
</string-array>
<integer-array name="cpuAccuracyValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
</integer-array>
</resources> </resources>

View file

@ -9,105 +9,31 @@
<string name="app_notification_channel_description">yuzu Switch emulator notifications</string> <string name="app_notification_channel_description">yuzu Switch emulator notifications</string>
<string name="app_notification_running">yuzu is running</string> <string name="app_notification_running">yuzu is running</string>
<!-- Input related strings --> <!-- General settings strings -->
<string name="controller_circlepad">Circle Pad</string> <string name="frame_limit_enable">Enable limit speed</string>
<string name="controller_c">C-Stick</string> <string name="frame_limit_enable_description">When enabled, emulation speed will be limited to a specified percentage of normal speed.</string>
<string name="controller_triggers">Triggers</string> <string name="frame_limit_slider">Limit speed percent</string>
<string name="controller_dpad">D-Pad</string> <string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. With the default of 100% emulation will be limited to normal speed. Values higher or lower will increase or decrease the speed limit.</string>
<string name="controller_axis_vertical">Up/Down Axis</string> <string name="cpu_accuracy">CPU accuracy</string>
<string name="controller_axis_horizontal">Left/Right Axis</string>
<string name="input_binding">Input Binding</string>
<string name="input_binding_description">Press or move an input to bind it to %1$s.</string>
<string name="input_binding_description_vertical_axis">Move your joystick up or down.</string>
<string name="input_binding_description_horizontal_axis">Move your joystick left or right.</string>
<string name="button_a" translatable="false">A</string>
<string name="button_b" translatable="false">B</string>
<string name="button_select" translatable="false">SELECT</string>
<string name="button_start" translatable="false">START</string>
<string name="button_x" translatable="false">X</string>
<string name="button_y" translatable="false">Y</string>
<string name="button_l" translatable="false">L</string>
<string name="button_r" translatable="false">R</string>
<string name="button_zl" translatable="false">ZL</string>
<string name="button_zr" translatable="false">ZR</string>
<string name="input_message_analog_only">This control must be bound to a gamepad analog stick or D-pad axis!</string>
<string name="input_message_button_only">This control must be bound to a gamepad button!</string>
<!-- Generic buttons (Shared with lots of stuff) -->
<string name="generic_buttons">Buttons</string>
<!-- Premium settings strings -->
<string name="design">Change Theme (Light, Dark)</string>
<string name="design_updated">Theme will update when exiting Settings</string>
<!-- Core settings strings -->
<string name="cpu_jit">Enable CPU JIT</string>
<string name="cpu_jit_description">Uses the Just-in-Time (JIT) compiler for CPU emulation. When enabled, game performance will be significantly improved.</string>
<string name="init_clock">System clock type</string>
<string name="init_clock_description">Set the emulated Console clock to either reflect that of your device or start at a simulated date and time.</string>
<!-- System settings strings --> <!-- System settings strings -->
<string name="use_docked_mode">Docked mode</string>
<string name="use_docked_mode_description">Emulates in docked mode, which increases the resolution at the expense of performance.</string>
<string name="init_time">System clock starting time override</string> <string name="init_time">System clock starting time override</string>
<string name="init_time_description">If the \"System clock type\" setting is set to \"Simulated clock\", this changes the fixed date and time to start at.</string> <string name="init_time_description">If the \"System clock type\" setting is set to \"Simulated clock\", this changes the fixed date and time to start at.</string>
<string name="emulated_region">Emulated region</string> <string name="emulated_region">Emulated region</string>
<string name="emulated_language">Emulated language</string> <string name="emulated_language">Emulated language</string>
<!-- Graphics settings strings --> <!-- Graphics settings strings -->
<string name="renderer">Renderer</string> <string name="renderer_api">API</string>
<string name="vsync">Enable V-Sync</string> <string name="renderer_accuracy">Accuracy level</string>
<string name="vsync_description">Synchronizes the game frame rate to the refresh rate of your device.</string> <string name="renderer_resolution">Resolution</string>
<string name="linear_filtering">Enable linear filtering</string> <string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
<string name="linear_filtering_description">Enables linear filtering, which causes game visuals to appear smoother.</string> <string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously, which will reduce stutter but may introduce glitches.</string>
<string name="texture_filter_name">Texture Filter</string>
<string name="texture_filter_description">Enhances the visuals of games by applying a filter to textures. The supported filters are Anime4K Ultrafast, Bicubic, ScaleForce, and xBRZ freescale.</string>
<string name="hw_renderer">Enable hardware renderer</string>
<string name="hw_renderer_description">Uses hardware to emulate 3DS graphics. When enabled, game performance will be significantly improved.</string>
<string name="hw_shaders">Enable hardware shader</string>
<string name="hw_shaders_description">Uses hardware to emulate 3DS shaders. When enabled, game performance will be significantly improved.</string>
<string name="shaders_accurate_mul">Enable accurate shader multiplication</string>
<string name="shaders_accurate_mul_description">Uses more accurate multiplication in hardware shaders, which may fix some graphical bugs. When enabled, performance will be reduced.</string>
<string name="asynchronous_gpu">Enable asynchronous GPU emulation</string>
<string name="asynchronous_gpu_description">Uses a separate thread to emulate the GPU asynchronously. When enabled, performance will be improved.</string>
<string name="frame_limit_enable">Enable limit speed</string>
<string name="frame_limit_enable_description">When enabled, emulation speed will be limited to a specified percentage of normal speed.</string>
<string name="frame_limit_slider">Limit speed percent</string>
<string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. With the default of 100% emulation will be limited to normal speed. Values higher or lower will increase or decrease the speed limit.</string>
<string name="internal_resolution">Internal resolution</string>
<string name="internal_resolution_description">Specifies the resolution used to render at. A high resolution will improve visual quality a lot but is also quite heavy on performance and might cause glitches in certain games.</string>
<string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string>
<string name="debug_warning">Warning: Modifying these settings will slow emulation</string>
<string name="stereoscopy">Stereoscopy</string>
<string name="render3d">Stereoscopic 3D Mode</string>
<string name="factor3d">Depth</string>
<string name="factor3d_description">Specifies the value of the 3D slider. This should be set to higher than 0% when Stereoscopic 3D is enabled.</string>
<string name="cardboard_vr">Cardboard VR</string>
<string name="cardboard_screen_size">Cardboard Screen size</string>
<string name="cardboard_screen_size_description">Scales the screen to a percentage of its original size.</string>
<string name="cardboard_x_shift">Horizontal shift</string>
<string name="cardboard_x_shift_description">Specifies the percentage of empty space to shift the screens horizontally. Positive values move the two eyes closer to the middle, while negative values move them away.</string>
<string name="cardboard_y_shift">Vertical shift</string>
<string name="cardboard_y_shift_description">Specifies the percentage of empty space to shift the screens vertically. Positive values move the two eyes towards the bottom, while negative values move them towards the top.</string>
<string name="use_shader_jit">Use shader JIT</string>
<string name="use_disk_shader_cache">Use disk shader cache</string>
<string name="use_disk_shader_cache_description">Reduce stuttering by storing and loading generated shaders to disk. It cannot be used without Enabling Hardware Shader.</string>
<string name="utility">Utility</string>
<string name="dump_textures">Dump textures</string>
<string name="dump_textures_description">Dumps textures to dump/textures/[GAME ID]</string>
<string name="custom_textures">Use custom textures</string>
<string name="custom_textures_description">Uses custom textures found in load/textures/[GAME ID]</string>
<string name="preload_textures">Preload custom textures</string>
<string name="preload_textures_description">Loads all custom textures into memory. This feature can use a lot of memory.</string>
<!-- Premium strings -->
<string name="premium_text">Premium</string>
<string name="premium_settings_upsell">Upgrade to Premium and support yuzu!</string>
<string name="premium_settings_upsell_description">With Premium, you will support the developers to continue improving yuzu, and gain access to these exclusive features!</string>
<string name="premium_settings_welcome">Welcome to Premium.</string>
<string name="premium_settings_welcome_description">Thank you for your support!</string>
<!-- Audio settings strings --> <!-- Audio settings strings -->
<string name="audio_stretch">Enable audio stretching</string> <string name="audio_volume">Volume</string>
<string name="audio_stretch_description">Stretches audio to reduce stuttering. When enabled, increases audio latency and slightly reduces performance.</string> <string name="audio_volume_description">Specifies the volume of audio output.</string>
<string name="audio_input_type">Audio Input Device</string>
<!-- Miscellaneous --> <!-- Miscellaneous -->
<string name="clear">Clear</string> <string name="clear">Clear</string>
@ -126,14 +52,10 @@
<!-- Preferences Screen --> <!-- Preferences Screen -->
<string name="preferences_settings">Settings</string> <string name="preferences_settings">Settings</string>
<string name="preferences_premium">Premium</string>
<string name="preferences_general">General</string> <string name="preferences_general">General</string>
<string name="preferences_system">System</string> <string name="preferences_system">System</string>
<string name="preferences_camera">Camera</string>
<string name="preferences_controls">Gamepad</string>
<string name="preferences_graphics">Graphics</string> <string name="preferences_graphics">Graphics</string>
<string name="preferences_audio">Audio</string> <string name="preferences_audio">Audio</string>
<string name="preferences_debug">Debug</string>
<!-- ROM loading errors --> <!-- ROM loading errors -->
<string name="loader_error_encrypted">Your ROM is encrypted</string> <string name="loader_error_encrypted">Your ROM is encrypted</string>
@ -158,7 +80,7 @@
<string name="do_not_show_this_again">Do not show this again</string> <string name="do_not_show_this_again">Do not show this again</string>
<!-- Software Keyboard --> <!-- Software keyboard -->
<string name="software_keyboard">Software Keyboard</string> <string name="software_keyboard">Software Keyboard</string>
<string name="i_forgot">I Forgot</string> <string name="i_forgot">I Forgot</string>
<string name="fixed_length_required">Text length is not correct (should be %d characters)</string> <string name="fixed_length_required">Text length is not correct (should be %d characters)</string>
@ -166,7 +88,7 @@
<string name="blank_input_not_allowed">Blank input is not allowed</string> <string name="blank_input_not_allowed">Blank input is not allowed</string>
<string name="empty_input_not_allowed">Empty input is not allowed</string> <string name="empty_input_not_allowed">Empty input is not allowed</string>
<!-- Core Errors --> <!-- Errors and warnings -->
<string name="abort_button">Abort</string> <string name="abort_button">Abort</string>
<string name="continue_button">Continue</string> <string name="continue_button">Continue</string>
<string name="system_archive_not_found">System Archive Not Found</string> <string name="system_archive_not_found">System Archive Not Found</string>
@ -175,4 +97,5 @@
<string name="save_load_error">Save/Load Error</string> <string name="save_load_error">Save/Load Error</string>
<string name="fatal_error">Fatal Error</string> <string name="fatal_error">Fatal Error</string>
<string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string> <string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string>
<string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string>
</resources> </resources>