diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.java index cd64a3298..f4fca40e4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.java @@ -3,17 +3,13 @@ package org.yuzu.yuzu_emu.activities; import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.PackageManager; import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; -import android.util.SparseIntArray; import android.view.InputDevice; import android.view.KeyEvent; import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.widget.SeekBar; @@ -31,20 +27,14 @@ import androidx.fragment.app.FragmentManager; import org.yuzu.yuzu_emu.NativeLibrary; 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.MenuFragment; import org.yuzu.yuzu_emu.utils.ControllerMappingHelper; -import org.yuzu.yuzu_emu.utils.EmulationMenuSettings; import org.yuzu.yuzu_emu.utils.ForegroundService; import java.lang.annotation.Retention; import java.util.List; -import static android.Manifest.permission.CAMERA; -import static android.Manifest.permission.RECORD_AUDIO; import static java.lang.annotation.RetentionPolicy.SOURCE; 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() { LayoutInflater inflater = LayoutInflater.from(this); View view = inflater.inflate(R.layout.dialog_seekbar, null); @@ -377,122 +296,6 @@ public final class EmulationActivity extends AppCompatActivity { 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 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() { return activityRecreated; } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/MotionAlertDialog.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/MotionAlertDialog.java deleted file mode 100644 index 874c1acbc..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/MotionAlertDialog.java +++ /dev/null @@ -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 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 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; - } -} \ No newline at end of file diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.java index efde45ee9..3126eba73 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.java @@ -14,22 +14,18 @@ import java.util.Map; import java.util.TreeMap; public class Settings { - public static final String SECTION_PREMIUM = "Premium"; - public static final String SECTION_CORE = "Core"; + public static final String SECTION_GENERAL = "General"; 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_LAYOUT = "Layout"; - public static final String SECTION_UTILITY = "Utility"; 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 static final Map> configFileSectionsMap = new HashMap<>(); 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)); } /** diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputBindingSetting.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputBindingSetting.java deleted file mode 100644 index 4ad54421e..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputBindingSetting.java +++ /dev/null @@ -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; - } -} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/PremiumHeader.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/PremiumHeader.java deleted file mode 100644 index 9bf95ce51..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/PremiumHeader.java +++ /dev/null @@ -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; - } -} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/PremiumSingleChoiceSetting.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/PremiumSingleChoiceSetting.java deleted file mode 100644 index 0c4570c8d..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/PremiumSingleChoiceSetting.java +++ /dev/null @@ -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; - } -} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.java index db7fb791a..e2ba9014f 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.java @@ -17,10 +17,8 @@ public abstract class SettingsItem { public static final int TYPE_SINGLE_CHOICE = 2; public static final int TYPE_SLIDER = 3; public static final int TYPE_SUBMENU = 4; - public static final int TYPE_INPUT_BINDING = 5; - public static final int TYPE_STRING_SINGLE_CHOICE = 6; - public static final int TYPE_DATETIME_SETTING = 7; - public static final int TYPE_PREMIUM = 8; + public static final int TYPE_STRING_SINGLE_CHOICE = 5; + public static final int TYPE_DATETIME_SETTING = 6; private String mKey; private String mSection; @@ -48,7 +46,6 @@ public abstract class SettingsItem { mSetting = setting; mNameId = nameId; mDescriptionId = descriptionId; - mIsPremium = (section == Settings.SECTION_PREMIUM); } /** @@ -93,10 +90,6 @@ public abstract class SettingsItem { return mDescriptionId; } - public boolean isPremium() { - return mIsPremium; - } - /** * Used by {@link SettingsAdapter}'s onCreateViewHolder() * method to determine which type of ViewHolder should be created. diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.java index 1102d6af1..47e73bfe2 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.java @@ -14,14 +14,11 @@ import androidx.appcompat.app.AlertDialog; import androidx.recyclerview.widget.RecyclerView; 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.IntSetting; 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.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.SingleChoiceSetting; 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.DateTimeViewHolder; 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.SingleChoiceViewHolder; 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.ui.main.MainActivity; import org.yuzu.yuzu_emu.utils.Log; import java.util.ArrayList; @@ -87,18 +81,10 @@ public final class SettingsAdapter extends RecyclerView.Adapter 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)); + onSingleChoiceClick(item); } public void onStringSingleChoiceClick(StringSingleChoiceSetting item) { @@ -209,15 +161,7 @@ public final class SettingsAdapter extends RecyclerView.Adapter onStringSingleChoiceClick(item)); + onStringSingleChoiceClick(item); } DialogInterface.OnClickListener defaultCancelListener = (dialog, which) -> closeDialog(); @@ -309,37 +253,6 @@ public final class SettingsAdapter extends RecyclerView.Adapter - 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 public void onClick(DialogInterface dialog, int which) { if (mClickedItem instanceof SingleChoiceSetting) { @@ -356,10 +269,6 @@ public final class SettingsAdapter extends RecyclerView.Adapter 0) { - int[] valuesArray = mContext.getResources().getIntArray(valuesId); - return valuesArray[which]; - } else { - return which; - } - } - private int getSelectionForSingleChoiceValue(SingleChoiceSetting item) { int value = item.getSelectedValue(); int valuesId = item.getValuesId(); @@ -465,23 +363,4 @@ public final class SettingsAdapter extends RecyclerView.Adapter 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; - } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.java index 27f0adf29..c84467c16 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.java @@ -1,10 +1,5 @@ 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 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.DateTimeSetting; 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.SingleChoiceSetting; 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.utils.SettingsFile; -import org.yuzu.yuzu_emu.utils.Log; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Objects; public final class SettingsFragmentPresenter { private SettingsFragmentView mView; @@ -106,27 +94,18 @@ public final class SettingsFragmentPresenter { case SettingsFile.FILE_NAME_CONFIG: addConfigSettings(sl); break; - case Settings.SECTION_PREMIUM: - addPremiumSettings(sl); - break; - case Settings.SECTION_CORE: + case Settings.SECTION_GENERAL: addGeneralSettings(sl); break; case Settings.SECTION_SYSTEM: addSystemSettings(sl); break; - case Settings.SECTION_CONTROLS: - addInputSettings(sl); - break; case Settings.SECTION_RENDERER: addGraphicsSettings(sl); break; case Settings.SECTION_AUDIO: addAudioSettings(sl); break; - case Settings.SECTION_DEBUG: - addDebugSettings(sl); - break; default: mView.showToastMessage("Unimplemented menu", false); return; @@ -139,184 +118,61 @@ public final class SettingsFragmentPresenter { private void addConfigSettings(ArrayList sl) { 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_CORE)); + sl.add(new SubmenuSetting(null, null, R.string.preferences_general, 0, Settings.SECTION_GENERAL)); 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_audio, 0, Settings.SECTION_AUDIO)); - sl.add(new SubmenuSetting(null, null, R.string.preferences_debug, 0, Settings.SECTION_DEBUG)); - } - - private void addPremiumSettings(ArrayList 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 sl) { mView.getActivity().setTitle(R.string.preferences_general); SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER); - Setting frameLimitEnable = rendererSection.getSetting(SettingsFile.KEY_FRAME_LIMIT_ENABLED); - Setting frameLimitValue = rendererSection.getSetting(SettingsFile.KEY_FRAME_LIMIT); + Setting frameLimitEnable = rendererSection.getSetting(SettingsFile.KEY_RENDERER_USE_SPEED_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 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 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_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 sl) { mView.getActivity().setTitle(R.string.preferences_system); SettingSection systemSection = mSettings.getSection(Settings.SECTION_SYSTEM); - Setting region = systemSection.getSetting(SettingsFile.KEY_REGION_VALUE); - Setting language = systemSection.getSetting(SettingsFile.KEY_LANGUAGE); - Setting systemClock = systemSection.getSetting(SettingsFile.KEY_INIT_CLOCK); - Setting dateTime = systemSection.getSetting(SettingsFile.KEY_INIT_TIME); + Setting dockedMode = systemSection.getSetting(SettingsFile.KEY_USE_DOCKED_MODE); + Setting region = systemSection.getSetting(SettingsFile.KEY_REGION_INDEX); + Setting language = systemSection.getSetting(SettingsFile.KEY_LANGUAGE_INDEX); - 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 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_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 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 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)); + 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_REGION_INDEX, Settings.SECTION_SYSTEM, R.string.emulated_region, 0, R.array.regionNames, R.array.regionValues, -1, region)); + sl.add(new SingleChoiceSetting(SettingsFile.KEY_LANGUAGE_INDEX, Settings.SECTION_SYSTEM, R.string.emulated_language, 0, R.array.languageNames, R.array.languageValues, 1, language)); } private void addGraphicsSettings(ArrayList sl) { mView.getActivity().setTitle(R.string.preferences_graphics); SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER); - Setting resolutionFactor = rendererSection.getSetting(SettingsFile.KEY_RESOLUTION_FACTOR); - Setting filterMode = rendererSection.getSetting(SettingsFile.KEY_FILTER_MODE); - Setting shadersAccurateMul = rendererSection.getSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL); - Setting render3dMode = rendererSection.getSetting(SettingsFile.KEY_RENDER_3D); - 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); + Setting rendererBackend = rendererSection.getSetting(SettingsFile.KEY_RENDERER_BACKEND); + Setting rendererAccuracy = rendererSection.getSetting(SettingsFile.KEY_RENDERER_ACCURACY); + Setting rendererReolution = rendererSection.getSetting(SettingsFile.KEY_RENDERER_RESOLUTION); + Setting rendererAsynchronousShaders = rendererSection.getSetting(SettingsFile.KEY_RENDERER_ASYNCHRONOUS_SHADERS); - sl.add(new HeaderSetting(null, null, R.string.renderer, 0)); - 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 CheckBoxSetting(SettingsFile.KEY_FILTER_MODE, Settings.SECTION_RENDERER, R.string.linear_filtering, R.string.linear_filtering_description, true, filterMode)); - 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_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)); + 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 SingleChoiceSetting(SettingsFile.KEY_RENDERER_ACCURACY, Settings.SECTION_RENDERER, R.string.renderer_accuracy, 0, R.array.rendererAccuracyNames, R.array.rendererAccuracyValues, 1, rendererAccuracy)); + 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_RENDERER_ASYNCHRONOUS_SHADERS, Settings.SECTION_RENDERER, R.string.renderer_asynchronous_shaders, R.string.renderer_asynchronous_shaders_description, false, rendererAsynchronousShaders)); } private void addAudioSettings(ArrayList sl) { mView.getActivity().setTitle(R.string.preferences_audio); SettingSection audioSection = mSettings.getSection(Settings.SECTION_AUDIO); - Setting audioStretch = audioSection.getSetting(SettingsFile.KEY_ENABLE_AUDIO_STRETCHING); - Setting micInputType = audioSection.getSetting(SettingsFile.KEY_MIC_INPUT_TYPE); + Setting audioVolume = audioSection.getSetting(SettingsFile.KEY_AUDIO_VOLUME); - 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 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 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)); + sl.add(new SliderSetting(SettingsFile.KEY_AUDIO_VOLUME, Settings.SECTION_AUDIO, R.string.audio_volume, R.string.audio_volume_description, 0, 100, "%", 100, audioVolume)); } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputBindingSettingViewHolder.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputBindingSettingViewHolder.java deleted file mode 100644 index 6f8bef7d7..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputBindingSettingViewHolder.java +++ /dev/null @@ -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()); - } -} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/PremiumViewHolder.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/PremiumViewHolder.java deleted file mode 100644 index 1f862b281..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/PremiumViewHolder.java +++ /dev/null @@ -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); - } - } -} diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.java index e3766f55e..539710395 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.java @@ -5,7 +5,6 @@ import android.view.View; import android.widget.TextView; 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.SingleChoiceSetting; import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting; @@ -46,17 +45,6 @@ public final class SingleChoiceViewHolder extends SettingViewHolder { 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 { mTextSettingDescription.setVisibility(View.GONE); } @@ -67,8 +55,6 @@ public final class SingleChoiceViewHolder extends SettingViewHolder { int position = getAdapterPosition(); if (mItem instanceof SingleChoiceSetting) { getAdapter().onSingleChoiceClick((SingleChoiceSetting) mItem, position); - } else if (mItem instanceof PremiumSingleChoiceSetting) { - getAdapter().onSingleChoiceClick((PremiumSingleChoiceSetting) mItem, position); } else if (mItem instanceof StringSingleChoiceSetting) { getAdapter().onStringSingleChoiceClick((StringSingleChoiceSetting) mItem, position); } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.java index 9e58dedc2..6526f9139 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.java @@ -33,95 +33,23 @@ import java.util.TreeSet; public final class SettingsFile { 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_PREMIUM = "premium"; - - public static final String KEY_HW_RENDERER = "use_hw_renderer"; - public static final String KEY_HW_SHADER = "use_hw_shader"; - public static final String KEY_SHADERS_ACCURATE_MUL = "shaders_accurate_mul"; - public static final String KEY_USE_SHADER_JIT = "use_shader_jit"; - public static final String KEY_USE_DISK_SHADER_CACHE = "use_disk_shader_cache"; - public static final String KEY_USE_VSYNC = "use_vsync_new"; - public static final String KEY_RESOLUTION_FACTOR = "resolution_factor"; - public static final String KEY_FRAME_LIMIT_ENABLED = "use_frame_limit"; - public static final String KEY_FRAME_LIMIT = "frame_limit"; - public static final String KEY_BACKGROUND_RED = "bg_red"; - public static final String KEY_BACKGROUND_BLUE = "bg_blue"; - public static final String KEY_BACKGROUND_GREEN = "bg_green"; - public static final String KEY_RENDER_3D = "render_3d"; - 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"; + // CPU + public static final String KEY_CPU_ACCURACY = "cpu_accuracy"; + // System + public static final String KEY_USE_DOCKED_MODE = "use_docked_mode"; + public static final String KEY_REGION_INDEX = "region_index"; + public static final String KEY_LANGUAGE_INDEX = "language_index"; + public static final String KEY_RENDERER_BACKEND = "backend"; + // Renderer + public static final String KEY_RENDERER_RESOLUTION = "resolution_setup"; + public static final String KEY_RENDERER_ACCURACY = "gpu_accuracy"; + public static final String KEY_RENDERER_ASYNCHRONOUS_SHADERS = "use_asynchronous_shaders"; + public static final String KEY_RENDERER_USE_SPEED_LIMIT = "use_speed_limit"; + public static final String KEY_RENDERER_SPEED_LIMIT = "speed_limit"; + // Audio + public static final String KEY_AUDIO_VOLUME = "volume"; private static BiMap sectionsMap = new BiMap<>(); diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.java index b2083f858..f7a242171 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.java @@ -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.DirectoryInitializationState; import org.yuzu.yuzu_emu.utils.DirectoryStateReceiver; -import org.yuzu.yuzu_emu.utils.EmulationMenuSettings; import org.yuzu.yuzu_emu.utils.Log; public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback, Choreographer.FrameCallback { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java index 6558a05c9..d419750a3 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java @@ -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.ui.platform.PlatformGamesFragment; 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.FileBrowserHelper; 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); - // Singleton to manage user billing state - private static BillingManager mBillingManager; - - private static MenuItem mPremiumButton; - @Override protected void onCreate(Bundle savedInstanceState) { ThemeUtil.applyTheme(); @@ -71,9 +65,6 @@ public final class MainActivity extends AppCompatActivity implements MainView { } 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) EmulationActivity.tryDismissRunningNotification(this); } @@ -107,22 +98,10 @@ public final class MainActivity extends AppCompatActivity implements MainView { public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); 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; } - static public void setPremiumButtonVisible(boolean isVisible) { - if (mPremiumButton != null) { - mPremiumButton.setVisible(isVisible); - } - } - /** * MainView */ @@ -155,15 +134,8 @@ public final class MainActivity extends AppCompatActivity implements MainView { FileBrowserHelper.openDirectoryPicker(this, MainPresenter.REQUEST_ADD_DIRECTORY, R.string.select_game_folder, - Arrays.asList("xci", "nsp", "cci", "3ds", - "cxi", "app", "3dsx", "cia", - "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); + Arrays.asList("nso", "nro", "nca", "xci", + "nsp", "kip")); break; } } else { @@ -191,12 +163,6 @@ public final class MainActivity extends AppCompatActivity implements MainView { mPresenter.onDirectorySelected(FileBrowserHelper.getSelectedDirectory(result)); } 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); 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); - } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java index 2608df2c2..4cf643552 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java @@ -5,15 +5,12 @@ import android.os.SystemClock; import org.yuzu.yuzu_emu.BuildConfig; import org.yuzu.yuzu_emu.YuzuApplication; 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.model.GameDatabase; import org.yuzu.yuzu_emu.utils.AddDirectoryHelper; public final class MainPresenter { public static final int REQUEST_ADD_DIRECTORY = 1; - public static final int REQUEST_INSTALL_CIA = 2; - private final MainView mView; private String mDirToAdd; private long mLastClickTime = 0; @@ -49,14 +46,6 @@ public final class MainPresenter { case R.id.button_add_directory: launchFileListActivity(REQUEST_ADD_DIRECTORY); 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; diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BillingManager.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BillingManager.java deleted file mode 100644 index 3d6dd1481..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BillingManager.java +++ /dev/null @@ -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 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 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 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); - } - } -} diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index 373c0e8bd..e5c9d57f2 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt @@ -1,4 +1,7 @@ add_library(yuzu-android SHARED + config.cpp + config.h + default_ini.h emu_window/emu_window.cpp emu_window/emu_window.h id_cache.cpp diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp new file mode 100644 index 000000000..326dab5fc --- /dev/null +++ b/src/android/app/src/main/jni/config.cpp @@ -0,0 +1,284 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include + +#include +#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 config_path) + : config_loc{config_path.value_or(default_config_path)}, + config{std::make_unique(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(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& 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& setting) { + setting = config->GetBoolean(group, setting.GetLabel(), setting.GetDefault()); +} + +template +void Config::ReadSetting(const std::string& group, Settings::Setting& setting) { + setting = static_cast(config->GetInteger(group, setting.GetLabel(), + static_cast(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(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 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(); +} diff --git a/src/android/app/src/main/jni/config.h b/src/android/app/src/main/jni/config.h new file mode 100644 index 000000000..0d7d6e94d --- /dev/null +++ b/src/android/app/src/main/jni/config.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include + +#include "common/settings.h" + +class INIReader; + +class Config { + std::filesystem::path config_loc; + std::unique_ptr config; + + bool LoadINI(const std::string& default_contents = "", bool retry = true); + void ReadValues(); + +public: + explicit Config(std::optional 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 + void ReadSetting(const std::string& group, Settings::Setting& setting); +}; diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h new file mode 100644 index 000000000..60db951c8 --- /dev/null +++ b/src/android/app/src/main/jni/default_ini.h @@ -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_` 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 diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index f548931f1..5d93da237 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -19,6 +19,7 @@ #include "core/file_sys/vfs_real.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/perf_stats.h" +#include "jni/config.h" #include "jni/emu_window/emu_window.h" #include "jni/id_cache.h" #include "video_core/rasterizer_interface.h" @@ -67,6 +68,9 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) { return Core::SystemResultStatus::ErrorLoader; } + // Loads the configuration. + Config{}; + system_.Initialize(); 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 - [[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, [[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) {} 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, [[maybe_unused]] jclass clazz, diff --git a/src/android/app/src/main/res/menu/menu_game_grid.xml b/src/android/app/src/main/res/menu/menu_game_grid.xml index 9cdcc7f08..cd515afbf 100644 --- a/src/android/app/src/main/res/menu/menu_game_grid.xml +++ b/src/android/app/src/main/res/menu/menu_game_grid.xml @@ -2,11 +2,6 @@ - Japanese (日本語) English - French (français) + French (Français) German (Deutsch) - Italian (italiano) - Spanish (español) - Simplified Chinese (简体中文) + Italian (Italiano) + Spanish (Español) + Chinese (简体中文) Korean (한국어) Dutch (Nederlands) - Portuguese (português) + Portuguese (Português) Russian (Русский) - Traditional Chinese (正體中文) + Taiwanese (台湾) + British English + Canadian French (Français canadien) + Latin American Spanish (Español latinoamericano) + Simplified Chinese (简体中文) + Traditional Chinese (正體中文)) + Brazilian Portuguese (Portugues do Brasil) @@ -86,84 +92,46 @@ 9 10 11 + 12 + 13 + 14 + 15 + 16 + 17 - - a - b - x - y - L - R - ZL - ZR - Start - Select - D-Pad - Circle Pad - C Stick - - - - Blank - Still Image - Device Camera - - - - blank - image - ndk - - - - Default - Any Front Camera - Any Back Camera - - - - - _front - _back - - - + + Vulkan None - Horizontal - Vertical - Reverse - - 0 + 1 2 - 3 - - None - Real Device - Static Noise + + Normal + High + Extreme (Slow) - + 0 1 2 - - Off - Side by Side - Anaglyph - Interlaced - Reverse Interlaced - Cardboard VR + + 0.5X (360p/540p) + 0.75X (540p/810p) + 1X (720p/1080p) + 2X (1440p/2160p) (Slow) + 3X (2160p/3240p) (Slow) + 4X (2880p/4320p) (Slow) - + 0 1 2 @@ -171,4 +139,18 @@ 4 5 + + + Auto + Accurate + Unsafe + Paranoid (Slow) + + + + 0 + 1 + 2 + 3 + diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 58d80398f..239fde48d 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -9,105 +9,31 @@ yuzu Switch emulator notifications yuzu is running - - Circle Pad - C-Stick - Triggers - D-Pad - Up/Down Axis - Left/Right Axis - Input Binding - Press or move an input to bind it to %1$s. - Move your joystick up or down. - Move your joystick left or right. - A - B - SELECT - START - X - Y - L - R - ZL - ZR - This control must be bound to a gamepad analog stick or D-pad axis! - This control must be bound to a gamepad button! - - - Buttons - - - Change Theme (Light, Dark) - Theme will update when exiting Settings - - - Enable CPU JIT - Uses the Just-in-Time (JIT) compiler for CPU emulation. When enabled, game performance will be significantly improved. - System clock type - Set the emulated Console clock to either reflect that of your device or start at a simulated date and time. + + Enable limit speed + When enabled, emulation speed will be limited to a specified percentage of normal speed. + Limit speed percent + 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. + CPU accuracy + Docked mode + Emulates in docked mode, which increases the resolution at the expense of performance. System clock starting time override If the \"System clock type\" setting is set to \"Simulated clock\", this changes the fixed date and time to start at. Emulated region Emulated language - Renderer - Enable V-Sync - Synchronizes the game frame rate to the refresh rate of your device. - Enable linear filtering - Enables linear filtering, which causes game visuals to appear smoother. - Texture Filter - Enhances the visuals of games by applying a filter to textures. The supported filters are Anime4K Ultrafast, Bicubic, ScaleForce, and xBRZ freescale. - Enable hardware renderer - Uses hardware to emulate 3DS graphics. When enabled, game performance will be significantly improved. - Enable hardware shader - Uses hardware to emulate 3DS shaders. When enabled, game performance will be significantly improved. - Enable accurate shader multiplication - Uses more accurate multiplication in hardware shaders, which may fix some graphical bugs. When enabled, performance will be reduced. - Enable asynchronous GPU emulation - Uses a separate thread to emulate the GPU asynchronously. When enabled, performance will be improved. - Enable limit speed - When enabled, emulation speed will be limited to a specified percentage of normal speed. - Limit speed percent - 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. - Internal resolution - 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. - Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled. - Warning: Modifying these settings will slow emulation - Stereoscopy - Stereoscopic 3D Mode - Depth - Specifies the value of the 3D slider. This should be set to higher than 0% when Stereoscopic 3D is enabled. - Cardboard VR - Cardboard Screen size - Scales the screen to a percentage of its original size. - Horizontal shift - 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. - Vertical shift - 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. - Use shader JIT - Use disk shader cache - Reduce stuttering by storing and loading generated shaders to disk. It cannot be used without Enabling Hardware Shader. - Utility - Dump textures - Dumps textures to dump/textures/[GAME ID] - Use custom textures - Uses custom textures found in load/textures/[GAME ID] - Preload custom textures - Loads all custom textures into memory. This feature can use a lot of memory. - - Premium - Upgrade to Premium and support yuzu! - With Premium, you will support the developers to continue improving yuzu, and gain access to these exclusive features! - Welcome to Premium. - Thank you for your support! + API + Accuracy level + Resolution + Use asynchronous shaders + Compiles shaders asynchronously, which will reduce stutter but may introduce glitches. - Enable audio stretching - Stretches audio to reduce stuttering. When enabled, increases audio latency and slightly reduces performance. - Audio Input Device + Volume + Specifies the volume of audio output. Clear @@ -126,14 +52,10 @@ Settings - Premium General System - Camera - Gamepad Graphics Audio - Debug Your ROM is encrypted @@ -158,7 +80,7 @@ Do not show this again - + Software Keyboard I Forgot Text length is not correct (should be %d characters) @@ -166,7 +88,7 @@ Blank input is not allowed Empty input is not allowed - + Abort Continue System Archive Not Found @@ -175,4 +97,5 @@ Save/Load Error Fatal Error A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs. + Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.