From 5d93a10c60f25acd4f9045234156fd68720b1a87 Mon Sep 17 00:00:00 2001 From: zackhow Date: Sun, 26 Aug 2018 18:27:27 -0400 Subject: [PATCH 1/4] Android: Add game specific gc/wii controller settings --- .../app/src/main/assets/WiimoteProfile.ini | 135 ++++++ .../dolphinemu/dolphinemu/NativeLibrary.java | 3 + .../dolphinemu/adapters/GameAdapter.java | 49 +-- .../dolphinemu/adapters/GameRowPresenter.java | 72 +--- .../dialogs/GameSettingsDialog.java | 98 +++++ .../features/settings/model/Settings.java | 8 +- .../model/view/InputBindingSetting.java | 11 +- .../settings/model/view/SettingsItem.java | 3 +- .../features/settings/ui/SettingsAdapter.java | 30 +- .../ui/SettingsFragmentPresenter.java | 391 ++++++++++-------- .../InputBindingSettingViewHolder.java | 5 +- .../features/settings/utils/SettingsFile.java | 75 +++- .../utils/DirectoryInitialization.java | 30 ++ .../app/src/main/res/values/arrays.xml | 10 +- Source/Android/jni/ButtonManager.cpp | 36 +- Source/Android/jni/ButtonManager.h | 2 +- Source/Android/jni/MainAndroid.cpp | 28 +- 17 files changed, 704 insertions(+), 282 deletions(-) create mode 100644 Source/Android/app/src/main/assets/WiimoteProfile.ini create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameSettingsDialog.java diff --git a/Source/Android/app/src/main/assets/WiimoteProfile.ini b/Source/Android/app/src/main/assets/WiimoteProfile.ini new file mode 100644 index 0000000000..6c036c1a37 --- /dev/null +++ b/Source/Android/app/src/main/assets/WiimoteProfile.ini @@ -0,0 +1,135 @@ +[Profile] +Device = Android/4/Touchscreen +Buttons/A = `Button 100` +Buttons/B = `Button 101` +Buttons/- = `Button 102` +Buttons/+ = `Button 103` +Buttons/Home = `Button 104` +Buttons/1 = `Button 105` +Buttons/2 = `Button 106` +D-Pad/Up = `Button 107` +D-Pad/Down = `Button 108` +D-Pad/Left = `Button 109` +D-Pad/Right = `Button 110` +IR/Up = `Axis 112` +IR/Down = `Axis 113` +IR/Left = `Axis 114` +IR/Right = `Axis 115` +IR/Forward = `Axis 116` +IR/Backward = `Axis 117` +IR/Hide = `Button 118` +Swing/Up = `Axis 120` +Swing/Down = `Axis 121` +Swing/Left = `Axis 122` +Swing/Right = `Axis 123` +Swing/Forward = `Axis 124` +Swing/Backward = `Axis 125` +Tilt/Forward = `Axis 127` +Tilt/Backward = `Axis 128` +Tilt/Left = `Axis 129` +Tilt/Right = `Axis 130` +Tilt/Modifier = `Button 131` +Tilt/Modifier/Range = 50,000000 +Shake/X = `Button 132` +Shake/Y = `Button 133` +Shake/Z = `Button 134` +Extension = Nunchuk +Nunchuk/Buttons/C = `Button 200` +Nunchuk/Buttons/Z = `Button 201` +Nunchuk/Stick/Up = `Axis 203` +Nunchuk/Stick/Down = `Axis 204` +Nunchuk/Stick/Left = `Axis 205` +Nunchuk/Stick/Right = `Axis 206` +Nunchuk/Stick/Radius = 100,000000 +Nunchuk/Swing/Up = `Axis 208` +Nunchuk/Swing/Down = `Axis 209` +Nunchuk/Swing/Left = `Axis 210` +Nunchuk/Swing/Right = `Axis 211` +Nunchuk/Swing/Forward = `Axis 212` +Nunchuk/Swing/Backward = `Axis 213` +Nunchuk/Tilt/Forward = `Axis 215` +Nunchuk/Tilt/Backward = `Axis 216` +Nunchuk/Tilt/Left = `Axis 217` +Nunchuk/Tilt/Right = `Axis 218` +Nunchuk/Tilt/Modifier = `Button 219` +Nunchuk/Tilt/Modifier/Range = 50,000000 +Nunchuk/Shake/X = `Button 220` +Nunchuk/Shake/Y = `Button 221` +Nunchuk/Shake/Z = `Button 222` +Classic/Buttons/A = `Button 300` +Classic/Buttons/B = `Button 301` +Classic/Buttons/X = `Button 302` +Classic/Buttons/Y = `Button 303` +Classic/Buttons/- = `Button 304` +Classic/Buttons/+ = `Button 305` +Classic/Buttons/Home = `Button 306` +Classic/Buttons/ZL = `Button 307` +Classic/Buttons/ZR = `Button 308` +Classic/D-Pad/Up = `Button 309` +Classic/D-Pad/Down = `Button 310` +Classic/D-Pad/Left = `Button 311` +Classic/D-Pad/Right = `Button 312` +Classic/Left Stick/Up = `Axis 314` +Classic/Left Stick/Down = `Axis 315` +Classic/Left Stick/Left = `Axis 316` +Classic/Left Stick/Right = `Axis 317` +Classic/Left Stick/Radius = 100,000000 +Classic/Right Stick/Up = `Axis 319` +Classic/Right Stick/Down = `Axis 320` +Classic/Right Stick/Left = `Axis 321` +Classic/Right Stick/Right = `Axis 322` +Classic/Right Stick/Radius = 100,000000 +Classic/Triggers/L = `Axis 323` +Classic/Triggers/R = `Axis 324` +Classic/Triggers/Threshold = 90,000000 +Guitar/Buttons/- = `Button 400` +Guitar/Buttons/+ = `Button 401` +Guitar/Frets/Green = `Button 402` +Guitar/Frets/Red = `Button 403` +Guitar/Frets/Yellow = `Button 404` +Guitar/Frets/Blue = `Button 405` +Guitar/Frets/Orange = `Button 406` +Guitar/Strum/Up = `Button 407` +Guitar/Strum/Down = `Button 408` +Guitar/Stick/Up = `Axis 410` +Guitar/Stick/Down = `Axis 411` +Guitar/Stick/Left = `Axis 412` +Guitar/Stick/Right = `Axis 413` +Guitar/Stick/Radius = 100,000000 +Guitar/Whammy/Bar = `Axis = 414` +Drums/Buttons/- = `Button 500` +Drums/Buttons/+ = `Button 501` +Drums/Pads/Red = `Button 502` +Drums/Pads/Yellow = `Button 503` +Drums/Pads/Blue = `Button 504` +Drums/Pads/Green = `Button 505` +Drums/Pads/Orange = `Button 506` +Drums/Pads/Bass = `Button 507` +Drums/Stick/Up = `Axis 509` +Drums/Stick/Down = `Axis 510` +Drums/Stick/Left = `Axis 511` +Drums/Stick/Right = `Axis 512` +Drums/Stick/Radius = 100,000000 +Turntable/Buttons/Green Left = `Button 600` +Turntable/Buttons/Red Left = `Button 601` +Turntable/Buttons/Blue Left = `Button 602` +Turntable/Buttons/Green Right = `Button 603` +Turntable/Buttons/Red Right = `Button 604` +Turntable/Buttons/Blue Right = `Button 605` +Turntable/Buttons/- = `Button 606` +Turntable/Buttons/+ = `Button 607` +Turntable/Buttons/Home = `Button 608` +Turntable/Buttons/Euphoria = `Button 609` +Turntable/Table Left/Left = `Axis 611` +Turntable/Table Left/Right = `Axis 612` +Turntable/Table Right/Left = `Axis 614` +Turntable/Table Right/Right = `Axis 615` +Turntable/Stick/Up = `Axis 617` +Turntable/Stick/Down = `Axis 618` +Turntable/Stick/Left = `Axis 619` +Turntable/Stick/Right = `Axis 620` +Turntable/Stick/Radius = 100,000000 +Turntable/Effect/Dial = `Axis 621` +Turntable/Crossfade/Left = `Axis 623` +Turntable/Crossfade/Right = `Axis 624` +Rumble/Motor = `Rumble 700` \ No newline at end of file diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java index 3536a17776..933ba04285 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java @@ -248,6 +248,9 @@ public final class NativeLibrary public static native void SetUserSetting(String gameID, String Section, String Key, String Value); + public static native void SetProfileSetting(String profile, String Section, String Key, + String Value); + public static native void InitGameIni(String gameID); /** diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java index 67b6b77e7a..52312b29f9 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java @@ -12,10 +12,12 @@ import android.widget.Toast; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.activities.EmulationActivity; +import org.dolphinemu.dolphinemu.dialogs.GameSettingsDialog; import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity; import org.dolphinemu.dolphinemu.model.GameFile; import org.dolphinemu.dolphinemu.utils.DirectoryInitialization; +import org.dolphinemu.dolphinemu.ui.platform.Platform; import org.dolphinemu.dolphinemu.utils.PicassoUtils; import org.dolphinemu.dolphinemu.viewholders.GameViewHolder; @@ -147,49 +149,10 @@ public final class GameAdapter extends RecyclerView.Adapter impl return true; } - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle("Game Settings") - .setItems(R.array.gameSettingsMenus, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int which) - { - switch (which) - { - case 0: - SettingsActivity.launch(activity, MenuTag.CONFIG, gameId); - break; - case 1: - SettingsActivity.launch(activity, MenuTag.GRAPHICS, gameId); - break; - case 2: - String path = - DirectoryInitialization.getUserDirectory() + "/GameSettings/" + - gameId + ".ini"; - File gameSettingsFile = new File(path); - if (gameSettingsFile.exists()) - { - if (gameSettingsFile.delete()) - { - Toast.makeText(view.getContext(), "Cleared settings for " + gameId, - Toast.LENGTH_SHORT).show(); - } - else - { - Toast.makeText(view.getContext(), "Unable to clear settings for " + gameId, - Toast.LENGTH_SHORT).show(); - } - } - else - { - Toast.makeText(view.getContext(), "No game settings to delete", - Toast.LENGTH_SHORT).show(); - } - break; - } - } - }); - - builder.show(); + GameSettingsDialog fragment = + GameSettingsDialog.newInstance(gameId, holder.gameFile.getPlatform()); + ((FragmentActivity) view.getContext()).getSupportFragmentManager().beginTransaction() + .add(fragment, GameSettingsDialog.TAG).commit(); return true; } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameRowPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameRowPresenter.java index 2cf23080b1..8047a438e8 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameRowPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameRowPresenter.java @@ -14,6 +14,7 @@ import android.widget.ImageView; import android.widget.Toast; import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.dialogs.GameSettingsDialog; import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity; import org.dolphinemu.dolphinemu.model.GameFile; @@ -80,73 +81,30 @@ public final class GameRowPresenter extends Presenter Context context = holder.cardParent.getContext(); Drawable background = ContextCompat.getDrawable(context, backgroundId); holder.cardParent.setInfoAreaBackground(background); - holder.cardParent.setOnLongClickListener(new View.OnLongClickListener() + holder.cardParent.setOnLongClickListener((view) -> { - @Override - public boolean onLongClick(View view) + FragmentActivity activity = (FragmentActivity) view.getContext(); + String gameId = gameFile.getGameId(); + + if (gameId.isEmpty()) { - FragmentActivity activity = (FragmentActivity) view.getContext(); - String gameId = gameFile.getGameId(); - - if (gameId.isEmpty()) - { - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle("Game Settings"); - builder.setMessage("Files without game IDs don't support game-specific settings."); - - builder.show(); - return true; - } - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle("Game Settings") - .setItems(R.array.gameSettingsMenus, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int which) - { - switch (which) - { - case 0: - SettingsActivity.launch(activity, MenuTag.CONFIG, gameId); - break; - case 1: - SettingsActivity.launch(activity, MenuTag.GRAPHICS, gameId); - break; - case 2: - String path = DirectoryInitialization.getUserDirectory() + - "/GameSettings/" + gameId + ".ini"; - File gameSettingsFile = new File(path); - if (gameSettingsFile.exists()) - { - if (gameSettingsFile.delete()) - { - Toast.makeText(view.getContext(), "Cleared settings for " + gameId, - Toast.LENGTH_SHORT).show(); - } - else - { - Toast.makeText(view.getContext(), - "Unable to clear settings for " + gameId, Toast.LENGTH_SHORT) - .show(); - } - } - else - { - Toast.makeText(view.getContext(), "No game settings to delete", - Toast.LENGTH_SHORT).show(); - } - break; - } - } - }); + builder.setTitle("Game Settings"); + builder.setMessage("Files without game IDs don't support game-specific settings."); builder.show(); return true; } + + GameSettingsDialog fragment = + GameSettingsDialog.newInstance(gameId, holder.gameFile.getPlatform()); + ((FragmentActivity) view.getContext()).getSupportFragmentManager().beginTransaction() + .add(fragment, GameSettingsDialog.TAG).commit(); + + return true; }); } - @Override public void onUnbindViewHolder(ViewHolder viewHolder) { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameSettingsDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameSettingsDialog.java new file mode 100644 index 0000000000..5639897d62 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameSettingsDialog.java @@ -0,0 +1,98 @@ +package org.dolphinemu.dolphinemu.dialogs; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.widget.Toast; + +import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; +import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity; +import org.dolphinemu.dolphinemu.ui.platform.Platform; +import org.dolphinemu.dolphinemu.utils.DirectoryInitialization; + +import java.io.File; + +public class GameSettingsDialog extends DialogFragment +{ + public static final String TAG = "GameSettingsDialog"; + public static final String ARG_GAMEID = "game_id"; + public static final String ARG_PLATFORM = "platform"; + + public static GameSettingsDialog newInstance(String gameId, int platform) + { + GameSettingsDialog fragment = new GameSettingsDialog(); + + Bundle arguments = new Bundle(); + arguments.putString(ARG_GAMEID, gameId); + arguments.putInt(ARG_PLATFORM, platform); + fragment.setArguments(arguments); + + return fragment; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) + { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + + String gameId = getArguments().getString(ARG_GAMEID); + int platform = getArguments().getInt(ARG_PLATFORM); + + builder.setTitle(getActivity().getString(R.string.preferences_game_settings)) + .setItems(platform == Platform.GAMECUBE.toInt() ? + R.array.gameSettingsMenusGC : + R.array.gameSettingsMenusWii, (dialog, which) -> + { + switch (which) + { + case 0: + SettingsActivity.launch(getContext(), MenuTag.CONFIG, gameId); + break; + case 1: + SettingsActivity.launch(getContext(), MenuTag.GRAPHICS, gameId); + break; + case 2: + SettingsActivity.launch(getContext(), MenuTag.GCPAD_TYPE, gameId); + break; + case 3: + // Clear option for GC, Wii controls for else + if (platform == Platform.GAMECUBE.toInt()) + clearGameSettings(gameId); + else + SettingsActivity.launch(getActivity(), MenuTag.WIIMOTE, gameId); + break; + case 4: + clearGameSettings(gameId); + break; + } + }); + return builder.create(); + } + + + private void clearGameSettings(String gameId) + { + String path = + DirectoryInitialization.getUserDirectory() + "/GameSettings/" + gameId + ".ini"; + File gameSettingsFile = new File(path); + if (gameSettingsFile.exists()) + { + if (gameSettingsFile.delete()) + { + Toast.makeText(getContext(), "Cleared settings for " + gameId, Toast.LENGTH_SHORT) + .show(); + } + else + { + Toast.makeText(getContext(), "Unable to clear settings for " + gameId, + Toast.LENGTH_SHORT).show(); + } + } + else + { + Toast.makeText(getContext(), "No game settings to delete", Toast.LENGTH_SHORT).show(); + } + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java index d945512069..0f3ff9d717 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java @@ -26,6 +26,8 @@ public class Settings public static final String SECTION_WIIMOTE = "Wiimote"; public static final String SECTION_BINDINGS = "Android"; + public static final String SECTION_CONTROLS = "Controls"; + public static final String SECTION_PROFILE = "Profile"; public static final String SECTION_ANALYTICS = "Analytics"; @@ -134,6 +136,11 @@ public class Settings mergeSections(SettingsFile.readCustomGameSettings(gameId, view)); } + public void loadWiimoteProfile(String gameId, String padId) + { + mergeSections(SettingsFile.readWiimoteProfile(gameId, padId)); + } + private void mergeSections(HashMap updatedSections) { for (Map.Entry entry : updatedSections.entrySet()) @@ -182,6 +189,5 @@ public class Settings view.showToastMessage("Saved settings for " + gameId); SettingsFile.saveCustomGameSettings(gameId, sections); } - } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputBindingSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputBindingSetting.java index e3850cb447..0c6a3bc314 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputBindingSetting.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputBindingSetting.java @@ -11,9 +11,13 @@ import org.dolphinemu.dolphinemu.features.settings.model.StringSetting; public class InputBindingSetting extends SettingsItem { - public InputBindingSetting(String key, String section, int titleId, Setting setting) + private String gameId; + + public InputBindingSetting(String key, String section, int titleId, Setting setting, + String gameId) { super(key, section, setting, titleId, 0); + this.gameId = gameId; } public String getValue() @@ -98,4 +102,9 @@ public class InputBindingSetting extends SettingsItem { return TYPE_INPUT_BINDING; } + + public String getGameId() + { + return gameId; + } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java index e3c798521f..48df22fc45 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java @@ -1,7 +1,7 @@ package org.dolphinemu.dolphinemu.features.settings.model.view; -import org.dolphinemu.dolphinemu.features.settings.model.Setting; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; +import org.dolphinemu.dolphinemu.features.settings.model.Setting; /** * ViewModel abstraction for an Item in the RecyclerView powering SettingsFragments. @@ -39,6 +39,7 @@ public abstract class SettingsItem * @param nameId Resource ID for a text string to be displayed as this setting's name. * @param descriptionId Resource ID for a text string to be displayed as this setting's description. */ + public SettingsItem(String key, String section, Setting setting, int nameId, int descriptionId) { mKey = key; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java index a2392caefc..7d903ddd0a 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java @@ -292,7 +292,14 @@ public final class SettingsAdapter extends RecyclerView.Adapter readWiimoteProfile(final String gameId, + final String padId) + { + String profile = gameId + "_Wii" + padId; + return readFile(getWiiProfile(profile, padId), true, null); + } /** * Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error @@ -435,13 +446,43 @@ public final class SettingsFile for (String settingKey : sortedKeySet) { Setting setting = settings.get(settingKey); - NativeLibrary - .SetUserSetting(gameId, mapSectionNameFromIni(section.getName()), setting.getKey(), - setting.getValueAsString()); + // Special case. Extension gets saved into a controller profile + if (settingKey.contains(SettingsFile.KEY_WIIMOTE_EXTENSION)) + { + saveCustomWiimoteSetting(gameId, setting); + } + else + { + NativeLibrary.SetUserSetting(gameId, mapSectionNameFromIni(section.getName()), + setting.getKey(), setting.getValueAsString()); + } } } } + /** + * Saves the extension value in a profile and enables that profile. Extension is the only + * controller setting that is not saved in the main config. + * + * @param gameId + * @param setting + */ + public static void saveCustomWiimoteSetting(final String gameId, final Setting setting) + { + if (setting.getSection().equals(Settings.SECTION_PROFILE)) + return; + String padId = + setting.getKey().substring(setting.getKey().length() - 1, setting.getKey().length()); + String profile = gameId + "_Wii" + padId; + + NativeLibrary.SetProfileSetting(profile, Settings.SECTION_PROFILE, KEY_WIIMOTE_EXTENSION, + setting.getValueAsString()); + + // Enable the profile + NativeLibrary.SetUserSetting(gameId, Settings.SECTION_CONTROLS, + KEY_WIIMOTE_PROFILE + (Integer.valueOf(padId) + 1), profile); + } + private static String mapSectionNameFromIni(String generalSectionName) { if (sectionsMap.getForward(generalSectionName) != null) @@ -487,10 +528,32 @@ public final class SettingsFile private static File getCustomGameSettingsFile(String gameId) { + return new File( DirectoryInitialization.getUserDirectory() + "/GameSettings/" + gameId + ".ini"); } + private static File getWiiProfile(String profile, String padId) + { + String wiiConfigPath = + DirectoryInitialization.getUserDirectory() + "/Config/Profiles/Wiimote/" + + profile + ".ini"; + + File wiiProfile = new File(wiiConfigPath); + // If it doesn't exist, create it + if (!wiiProfile.exists()) + { + String defautlWiiProfilePath = + DirectoryInitialization.getUserDirectory() + + "/Config/Profiles/Wiimote/WiimoteProfile.ini"; + DirectoryInitialization.copyFile(defautlWiiProfilePath, wiiConfigPath); + + NativeLibrary.SetProfileSetting(profile, Settings.SECTION_PROFILE, "Device", + "Android/" + (Integer.valueOf(padId) + 4) + "/Touchscreen"); + } + return wiiProfile; + } + private static SettingSection sectionFromLine(String line, boolean isCustomGame) { String sectionName = line.substring(1, line.length() - 1); @@ -626,4 +689,10 @@ public final class SettingsFile { return setting.getKey() + " = " + setting.getValueAsString(); } + + private static String customWiimoteExtSettingAsString(Setting setting) + { + return setting.getKey().substring(0, setting.getKey().length() - 1) + " = " + + setting.getValueAsString(); + } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java index b0132c215f..c8b54b36f0 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/DirectoryInitialization.java @@ -16,6 +16,7 @@ import android.support.v4.content.LocalBroadcastManager; import org.dolphinemu.dolphinemu.NativeLibrary; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -139,8 +140,14 @@ public final class DirectoryInitialization // // TODO: Redo the Android controller system so that we don't have to extract these INIs. String configDirectory = NativeLibrary.GetUserDirectory() + File.separator + "Config"; + String profileDirectory = + NativeLibrary.GetUserDirectory() + File.separator + "Config/Profiles/Wiimote/"; + createWiimoteProfileDirectory(profileDirectory); + copyAsset("GCPadNew.ini", new File(configDirectory, "GCPadNew.ini"), true, context); copyAsset("WiimoteNew.ini", new File(configDirectory, "WiimoteNew.ini"), false, context); + copyAsset("WiimoteProfile.ini", new File(profileDirectory, "WiimoteProfile.ini"), true, + context); } private static void deleteDirectoryRecursively(File file) @@ -247,6 +254,20 @@ public final class DirectoryInitialization } } + public static void copyFile(String from, String to) + { + try + { + InputStream in = new FileInputStream(from); + OutputStream out = new FileOutputStream(to); + copyFile(in, out); + } + catch (IOException e) + { + + } + } + private static void copyFile(InputStream in, OutputStream out) throws IOException { byte[] buffer = new byte[1024]; @@ -258,6 +279,15 @@ public final class DirectoryInitialization } } + private static void createWiimoteProfileDirectory(String directory) + { + File wiiPath = new File(directory); + if (!wiiPath.isDirectory()) + { + wiiPath.mkdirs(); + } + } + private static native void CreateUserDirectories(); private static native void SetSysDirectory(String path); diff --git a/Source/Android/app/src/main/res/values/arrays.xml b/Source/Android/app/src/main/res/values/arrays.xml index 21a5c4f7b3..403e27b2b1 100644 --- a/Source/Android/app/src/main/res/values/arrays.xml +++ b/Source/Android/app/src/main/res/values/arrays.xml @@ -306,9 +306,17 @@ Right Stick - + Core Settings GFX Settings + GameCube Controller Settings + Clear Game Settings + + + Core Settings + GFX Settings + GameCube Controller Settings + Wii Controller Settings Clear Game Settings diff --git a/Source/Android/jni/ButtonManager.cpp b/Source/Android/jni/ButtonManager.cpp index 637b23b3da..251c754968 100644 --- a/Source/Android/jni/ButtonManager.cpp +++ b/Source/Android/jni/ButtonManager.cpp @@ -335,7 +335,7 @@ static void AddBind(const std::string& dev, sBind* bind) m_controllers[dev]->AddBind(bind); } -void Init() +void Init(const std::string& gameId) { // Initialize our touchScreenKey buttons for (int a = 0; a < 8; ++a) @@ -592,6 +592,40 @@ void Init() new sBind(padID, configTypes[a], type, bindnum, modifier == '-' ? -1.0f : 1.0f)); } } + + ini.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + std::string(gameId + ".ini")); + for (u32 a = 0; a < configStrings.size(); ++a) + { + for (int padID = 0; padID < 8; ++padID) + { + std::ostringstream config; + config << configStrings[a] << "_" << padID; + BindType type; + int bindnum; + char dev[128]; + bool hasbind = false; + char modifier = '+'; + std::string value; + ini.GetOrCreateSection("Android")->Get(config.str(), &value, "None"); + if (value == "None") + continue; + if (std::string::npos != value.find("Axis")) + { + hasbind = true; + type = BIND_AXIS; + sscanf(value.c_str(), "Device '%127[^\']'-Axis %d%c", dev, &bindnum, &modifier); + } + else if (std::string::npos != value.find("Button")) + { + hasbind = true; + type = BIND_BUTTON; + sscanf(value.c_str(), "Device '%127[^\']'-Button %d", dev, &bindnum); + } + if (hasbind) + AddBind(std::string(dev), + new sBind(padID, configTypes[a], type, bindnum, modifier == '-' ? -1.0f : 1.0f)); + } + } } bool GetButtonPressed(int padID, ButtonType button) diff --git a/Source/Android/jni/ButtonManager.h b/Source/Android/jni/ButtonManager.h index b9744f516c..82f64e6d4d 100644 --- a/Source/Android/jni/ButtonManager.h +++ b/Source/Android/jni/ButtonManager.h @@ -250,7 +250,7 @@ public: float AxisValue(int padID, ButtonType axis); }; -void Init(); +void Init(const std::string&); bool GetButtonPressed(int padID, ButtonType button); float GetAxisValue(int padID, ButtonType axis); bool GamepadEvent(const std::string& dev, int button, int action); diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index 34e2b06c5e..f74ae67788 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include "Common/AndroidAnalytics.h" #include "Common/CPUDetect.h" @@ -250,8 +251,10 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetProfiling jboolean enable); JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteProfileResults(JNIEnv* env, jobject obj); + JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run__Ljava_lang_String_2Z( JNIEnv* env, jobject obj, jstring jFile, jboolean jfirstOpen); + JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_String_2Z( JNIEnv* env, jobject obj, jstring jFile, jstring jSavestate, jboolean jDeleteSavestate); @@ -381,6 +384,29 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetUserSetti ini.Save(File::GetUserPath(D_GAMESETTINGS_IDX) + gameid + ".ini"); } +JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetProfileSetting( + JNIEnv* env, jobject obj, jstring jProfile, jstring jSection, jstring jKey, jstring jValue) +{ + IniFile ini; + std::string profile = GetJString(env, jProfile); + std::string section = GetJString(env, jSection); + std::string key = GetJString(env, jKey); + std::string val = GetJString(env, jValue); + + ini.Load(File::GetUserPath(D_CONFIG_IDX) + "Profiles/Wiimote/" + profile + ".ini"); + + if (val != "-1") + { + ini.GetOrCreateSection(section)->Set(key, val); + } + else + { + ini.GetOrCreateSection(section)->Delete(key); + } + + ini.Save(File::GetUserPath(D_CONFIG_IDX) + "Profiles/Wiimote/" + profile + ".ini"); +} + JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetConfig( JNIEnv* env, jobject obj, jstring jFile, jstring jSection, jstring jKey, jstring jDefault) { @@ -539,7 +565,6 @@ static void Run(const std::string& path, bool first_open, __android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Running : %s", path.c_str()); // Install our callbacks - OSD::AddCallback(OSD::CallbackType::Initialization, ButtonManager::Init); OSD::AddCallback(OSD::CallbackType::Shutdown, ButtonManager::Shutdown); RegisterMsgAlertHandler(&MsgAlert); @@ -563,6 +588,7 @@ static void Run(const std::string& path, bool first_open, WindowSystemInfo wsi(WindowSystemType::Android, nullptr, s_surf); if (BootManager::BootCore(std::move(boot), wsi)) { + ButtonManager::Init(SConfig::GetInstance().GetGameID()); static constexpr int TIMEOUT = 10000; static constexpr int WAIT_STEP = 25; int time_waited = 0; From 405b37deaa4b59659906d709dbe7a220cc875b74 Mon Sep 17 00:00:00 2001 From: zackhow Date: Mon, 24 Sep 2018 21:07:56 -0400 Subject: [PATCH 2/4] Android: Speed up game settings saving Previously would take several seconds to save, sometimes causing ANRs, which was made worse when adding all the controller values. Now we only load/save each section instead of doing it for each setting. Also added a method to save an individual setting. --- .../dolphinemu/dolphinemu/NativeLibrary.java | 4 ++ .../features/settings/utils/SettingsFile.java | 48 ++++++++++++++----- Source/Android/jni/MainAndroid.cpp | 30 ++++++++---- 3 files changed, 62 insertions(+), 20 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java index 933ba04285..425a749d75 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java @@ -244,6 +244,10 @@ public final class NativeLibrary Rumble.checkRumble(padID, state); } + public static native void LoadGameIniFile(String gameId); + + public static native void SaveGameIniFile(String gameId); + public static native String GetUserSetting(String gameID, String Section, String Key); public static native void SetUserSetting(String gameID, String Section, String Key, String Value); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java index 4592011443..04b36cb84f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java @@ -443,13 +443,27 @@ public final class SettingsFile HashMap settings = section.getSettings(); Set sortedKeySet = new TreeSet<>(settings.keySet()); + // Profile options(wii extension) are not saved, only used to properly display values + if (sectionKey.contains(Settings.SECTION_PROFILE)) + { + continue; + } + else + { + NativeLibrary.LoadGameIniFile(gameId); + } for (String settingKey : sortedKeySet) { Setting setting = settings.get(settingKey); // Special case. Extension gets saved into a controller profile if (settingKey.contains(SettingsFile.KEY_WIIMOTE_EXTENSION)) { - saveCustomWiimoteSetting(gameId, setting); + String padId = + setting.getKey() + .substring(setting.getKey().length() - 1, setting.getKey().length()); + + saveCustomWiimoteSetting(gameId, KEY_WIIMOTE_EXTENSION, setting.getValueAsString(), + padId); } else { @@ -457,30 +471,42 @@ public final class SettingsFile setting.getKey(), setting.getValueAsString()); } } + NativeLibrary.SaveGameIniFile(gameId); } } + public static void saveSingleCustomSetting(final String gameId, final String section, + final String key, + final String value) + { + NativeLibrary.LoadGameIniFile(gameId); + NativeLibrary.SetUserSetting(gameId, section, + key, value); + NativeLibrary.SaveGameIniFile(gameId); + } + /** - * Saves the extension value in a profile and enables that profile. Extension is the only - * controller setting that is not saved in the main config. + * Saves the wiimote setting in a profile and enables that profile. * * @param gameId - * @param setting + * @param key + * @param value + * @param padId */ - public static void saveCustomWiimoteSetting(final String gameId, final Setting setting) + public static void saveCustomWiimoteSetting(final String gameId, final String key, + final String value, + final String padId) { - if (setting.getSection().equals(Settings.SECTION_PROFILE)) - return; - String padId = - setting.getKey().substring(setting.getKey().length() - 1, setting.getKey().length()); String profile = gameId + "_Wii" + padId; - NativeLibrary.SetProfileSetting(profile, Settings.SECTION_PROFILE, KEY_WIIMOTE_EXTENSION, - setting.getValueAsString()); + NativeLibrary.SetProfileSetting(profile, Settings.SECTION_PROFILE, key, + value); // Enable the profile + NativeLibrary.LoadGameIniFile(gameId); NativeLibrary.SetUserSetting(gameId, Settings.SECTION_CONTROLS, KEY_WIIMOTE_PROFILE + (Integer.valueOf(padId) + 1), profile); + NativeLibrary.SaveGameIniFile(gameId); } private static String mapSectionNameFromIni(String generalSectionName) diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index f74ae67788..f4692af117 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #include #include @@ -15,7 +16,6 @@ #include #include #include -#include #include "Common/AndroidAnalytics.h" #include "Common/CPUDetect.h" @@ -61,6 +61,7 @@ namespace static constexpr char DOLPHIN_TAG[] = "DolphinEmuNative"; ANativeWindow* s_surf; +IniFile s_ini; // The Core only supports using a single Host thread. // If multiple threads want to call host functions then they need to queue @@ -361,27 +362,38 @@ JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetUserSe return ToJString(env, value.c_str()); } +JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_LoadGameIniFile(JNIEnv* env, + jobject obj, + jstring jGameID) +{ + std::string gameid = GetJString(env, jGameID); + s_ini.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + gameid + ".ini"); +} + +JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveGameIniFile(JNIEnv* env, + jobject obj, + jstring jGameID) +{ + std::string gameid = GetJString(env, jGameID); + s_ini.Save(File::GetUserPath(D_GAMESETTINGS_IDX) + gameid + ".ini"); +} + JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetUserSetting( JNIEnv* env, jobject obj, jstring jGameID, jstring jSection, jstring jKey, jstring jValue) { - IniFile ini; std::string gameid = GetJString(env, jGameID); std::string section = GetJString(env, jSection); std::string key = GetJString(env, jKey); std::string val = GetJString(env, jValue); - ini.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + gameid + ".ini"); - if (val != "-1") { - ini.GetOrCreateSection(section)->Set(key, val); + s_ini.GetOrCreateSection(section)->Set(key, val); } else { - ini.GetOrCreateSection(section)->Delete(key); + s_ini.GetOrCreateSection(section)->Delete(key); } - - ini.Save(File::GetUserPath(D_GAMESETTINGS_IDX) + gameid + ".ini"); } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetProfileSetting( @@ -404,7 +416,7 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetProfileSe ini.GetOrCreateSection(section)->Delete(key); } - ini.Save(File::GetUserPath(D_CONFIG_IDX) + "Profiles/Wiimote/" + profile + ".ini"); + ini.Save(File::GetUserPath(D_CONFIG_IDX) + "Profiles/Wiimote/" + profile + ".ini"); } JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetConfig( From 471485071d7265ad57bbddd6fa4b052d463ffb4e Mon Sep 17 00:00:00 2001 From: zackhow Date: Sun, 14 Oct 2018 15:09:21 -0400 Subject: [PATCH 3/4] Android: Move profile check to when it is saving --- .../features/settings/utils/SettingsFile.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java index 04b36cb84f..79315dbe90 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java @@ -499,6 +499,22 @@ public final class SettingsFile { String profile = gameId + "_Wii" + padId; + String wiiConfigPath = + DirectoryInitialization.getUserDirectory() + "/Config/Profiles/Wiimote/" + + profile + ".ini"; + File wiiProfile = new File(wiiConfigPath); + // If it doesn't exist, create it + if (!wiiProfile.exists()) + { + String defautlWiiProfilePath = + DirectoryInitialization.getUserDirectory() + + "/Config/Profiles/Wiimote/WiimoteProfile.ini"; + DirectoryInitialization.copyFile(defautlWiiProfilePath, wiiConfigPath); + + NativeLibrary.SetProfileSetting(profile, Settings.SECTION_PROFILE, "Device", + "Android/" + (Integer.valueOf(padId) + 4) + "/Touchscreen"); + } + NativeLibrary.SetProfileSetting(profile, Settings.SECTION_PROFILE, key, value); @@ -565,19 +581,7 @@ public final class SettingsFile DirectoryInitialization.getUserDirectory() + "/Config/Profiles/Wiimote/" + profile + ".ini"; - File wiiProfile = new File(wiiConfigPath); - // If it doesn't exist, create it - if (!wiiProfile.exists()) - { - String defautlWiiProfilePath = - DirectoryInitialization.getUserDirectory() + - "/Config/Profiles/Wiimote/WiimoteProfile.ini"; - DirectoryInitialization.copyFile(defautlWiiProfilePath, wiiConfigPath); - - NativeLibrary.SetProfileSetting(profile, Settings.SECTION_PROFILE, "Device", - "Android/" + (Integer.valueOf(padId) + 4) + "/Touchscreen"); - } - return wiiProfile; + return new File(wiiConfigPath); } private static SettingSection sectionFromLine(String line, boolean isCustomGame) From f46157793faa9c1352b9df08c3667bbafdf2f71e Mon Sep 17 00:00:00 2001 From: zackhow Date: Sun, 28 Oct 2018 20:30:32 -0400 Subject: [PATCH 4/4] Android: Update rumble settings for use in game profiles --- .../features/settings/model/view/RumbleBindingSetting.java | 5 +++-- .../features/settings/ui/SettingsFragmentPresenter.java | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RumbleBindingSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RumbleBindingSetting.java index 8dde9825d5..175d444d5f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RumbleBindingSetting.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RumbleBindingSetting.java @@ -15,9 +15,10 @@ import org.dolphinemu.dolphinemu.utils.Rumble; public class RumbleBindingSetting extends InputBindingSetting { - public RumbleBindingSetting(String key, String section, int titleId, Setting setting) + public RumbleBindingSetting(String key, String section, int titleId, Setting setting, + String gameId) { - super(key, section, titleId, setting); + super(key, section, titleId, setting, gameId); } @Override diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index 6359c340e2..f27596228f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -712,7 +712,7 @@ public final class SettingsFragmentPresenter sl.add(new HeaderSetting(null, null, R.string.emulation_control_rumble, 0)); sl.add(new RumbleBindingSetting(SettingsFile.KEY_EMU_RUMBLE + gcPadNumber, - Settings.SECTION_BINDINGS, R.string.emulation_control_rumble, gcEmuRumble)); + Settings.SECTION_BINDINGS, R.string.emulation_control_rumble, gcEmuRumble, mGameID)); } else // Adapter { @@ -900,7 +900,7 @@ public final class SettingsFragmentPresenter sl.add(new HeaderSetting(null, null, R.string.emulation_control_rumble, 0)); sl.add(new RumbleBindingSetting(SettingsFile.KEY_EMU_RUMBLE + wiimoteNumber, - Settings.SECTION_BINDINGS, R.string.emulation_control_rumble, wiiEmuRumble)); + Settings.SECTION_BINDINGS, R.string.emulation_control_rumble, wiiEmuRumble, mGameID)); } private void addExtensionTypeSettings(ArrayList sl, int wiimoteNumber,