From ba2aeb99d67bd31725139a8a09c6f69299d8932a Mon Sep 17 00:00:00 2001
From: JosJuice <josjuice@gmail.com>
Date: Tue, 20 Oct 2020 19:40:04 +0200
Subject: [PATCH] Android: Remove hacks for Wii Remote extension setting, round
 2

It's still not exactly pretty, but now all the mess
is contained in one place and has a proper interface.
Fixes https://bugs.dolphin-emu.org/issues/12218.
---
 .../features/settings/model/Settings.java     |   5 -
 .../settings/model/WiimoteProfileSetting.java | 118 ++++++++++++++++++
 .../ui/SettingsFragmentPresenter.java         |  32 ++---
 .../features/settings/utils/SettingsFile.java |  72 +----------
 .../utils/DirectoryInitialization.java        |  13 --
 5 files changed, 128 insertions(+), 112 deletions(-)
 create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/WiimoteProfileSetting.java

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 422b97acae..79d29c644d 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
@@ -134,11 +134,6 @@ public class Settings implements Closeable
     mIniFiles.put(GAME_SETTINGS_PLACEHOLDER_FILE_NAME, ini);
   }
 
-  public void loadWiimoteProfile(String gameId, int padId)
-  {
-    SettingsFile.readWiimoteProfile(gameId, getGameSpecificFile(), padId);
-  }
-
   public void loadSettings(String gameId, int revision, SettingsActivityView view)
   {
     mGameId = gameId;
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/WiimoteProfileSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/WiimoteProfileSetting.java
new file mode 100644
index 0000000000..2e6d2c2eaf
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/WiimoteProfileSetting.java
@@ -0,0 +1,118 @@
+package org.dolphinemu.dolphinemu.features.settings.model;
+
+import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
+import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
+import org.dolphinemu.dolphinemu.utils.IniFile;
+
+import java.io.File;
+
+// This stuff is pretty ugly. It's a kind of workaround for certain controller settings
+// not actually being available as game-specific settings.
+//
+// Warning: The current implementation of this should only be used for one setting at a time.
+// Because each instance of this class keeps an IniFile around in memory, saving one profile
+// setting to disk might overwrite changes that were made to other profile settings earlier.
+// This system shouldn't be used for new settings anyway since the approach is fundamentally messy.
+public class WiimoteProfileSetting implements AbstractStringSetting
+{
+  private final int mPadID;
+
+  private final String mSection;
+  private final String mKey;
+  private final String mDefaultValue;
+
+  private final String mProfileKey;
+
+  private final String mProfile;
+  private final File mProfileFile;
+  private final IniFile mProfileIni;
+
+  public WiimoteProfileSetting(String gameID, int padID, String section, String key,
+          String defaultValue)
+  {
+    mPadID = padID;
+    mSection = section;
+    mKey = key;
+    mDefaultValue = defaultValue;
+
+    mProfileKey = SettingsFile.KEY_WIIMOTE_PROFILE + (mPadID + 1);
+
+    mProfile = gameID + "_Wii" + padID;
+    mProfileFile = SettingsFile.getWiiProfile(mProfile);
+    mProfileIni = loadProfile();
+  }
+
+  @Override
+  public boolean isOverridden(Settings settings)
+  {
+    return isProfileEnabled(settings);
+  }
+
+  @Override
+  public boolean isRuntimeEditable()
+  {
+    return false;
+  }
+
+  @Override
+  public boolean delete(Settings settings)
+  {
+    return disableProfile(settings);
+  }
+
+  @Override
+  public String getString(Settings settings)
+  {
+    if (isProfileEnabled(settings))
+      return mProfileIni.getString(mSection, mKey, mDefaultValue);
+    else
+      return mDefaultValue;
+  }
+
+  @Override
+  public void setString(Settings settings, String newValue)
+  {
+    mProfileIni.setString(mSection, mKey, newValue);
+
+    mProfileIni.save(mProfileFile);
+    enableProfile(settings);
+  }
+
+  private IniFile loadProfile()
+  {
+    IniFile profileIni = new IniFile();
+
+    if (!profileIni.load(mProfileFile, false))
+    {
+      String defaultWiiProfilePath = DirectoryInitialization.getUserDirectory() +
+              "/Config/Profiles/Wiimote/WiimoteProfile.ini";
+
+      profileIni.load(defaultWiiProfilePath, false);
+
+      profileIni.setString(Settings.SECTION_PROFILE, "Device",
+              "Android/" + (mPadID + 4) + "/Touchscreen");
+    }
+
+    return profileIni;
+  }
+
+  private void enableProfile(Settings settings)
+  {
+    getControlsSection(settings).setString(mProfileKey, mProfile);
+  }
+
+  private boolean disableProfile(Settings settings)
+  {
+    return getControlsSection(settings).delete(mProfileKey);
+  }
+
+  private boolean isProfileEnabled(Settings settings)
+  {
+    return mProfile.equals(getControlsSection(settings).getString(mProfileKey, ""));
+  }
+
+  private IniFile.Section getControlsSection(Settings s)
+  {
+    return s.getSection(Settings.GAME_SETTINGS_PLACEHOLDER_FILE_NAME, Settings.SECTION_CONTROLS);
+  }
+}
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 711afb82a0..893c0241d0 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
@@ -6,6 +6,7 @@ import android.text.TextUtils;
 import org.dolphinemu.dolphinemu.NativeLibrary;
 import org.dolphinemu.dolphinemu.R;
 import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting;
+import org.dolphinemu.dolphinemu.features.settings.model.AbstractStringSetting;
 import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
 import org.dolphinemu.dolphinemu.features.settings.model.FloatSetting;
 import org.dolphinemu.dolphinemu.features.settings.model.IntSetting;
@@ -14,6 +15,7 @@ import org.dolphinemu.dolphinemu.features.settings.model.LegacyIntSetting;
 import org.dolphinemu.dolphinemu.features.settings.model.LegacyStringSetting;
 import org.dolphinemu.dolphinemu.features.settings.model.Settings;
 import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
+import org.dolphinemu.dolphinemu.features.settings.model.WiimoteProfileSetting;
 import org.dolphinemu.dolphinemu.features.settings.model.view.CheckBoxSetting;
 import org.dolphinemu.dolphinemu.features.settings.model.view.RunRunnable;
 import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker;
@@ -33,7 +35,6 @@ import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
 import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
 import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
 import org.dolphinemu.dolphinemu.utils.EGLHelper;
-import org.dolphinemu.dolphinemu.utils.IniFile;
 import org.dolphinemu.dolphinemu.utils.Log;
 
 import java.io.File;
@@ -755,22 +756,21 @@ public final class SettingsFragmentPresenter
   private void addWiimoteSubSettings(ArrayList<SettingsItem> sl, int wiimoteNumber)
   {
     // Bindings use controller numbers 4-7 (0-3 are GameCube), but the extension setting uses 1-4.
-    // But game game specific extension settings are saved in their own profile. These profiles
+    // But game specific extension settings are saved in their own profile. These profiles
     // do not have any way to specify the controller that is loaded outside of knowing the filename
     // of the profile that was loaded.
-    LegacyStringSetting extension;
+    AbstractStringSetting extension;
+    final String defaultExtension = "None";
     if (mGameID.isEmpty())
     {
       extension = new LegacyStringSetting(Settings.FILE_WIIMOTE,
               Settings.SECTION_WIIMOTE + (wiimoteNumber - 3), SettingsFile.KEY_WIIMOTE_EXTENSION,
-              getExtensionValue(wiimoteNumber - 3));
+              defaultExtension);
     }
     else
     {
-      mSettings.loadWiimoteProfile(mGameID, wiimoteNumber - 4);
-      extension = new LegacyStringSetting(Settings.GAME_SETTINGS_PLACEHOLDER_FILE_NAME,
-              Settings.SECTION_CONTROLS, SettingsFile.KEY_WIIMOTE_EXTENSION + (wiimoteNumber - 4),
-              getExtensionValue(wiimoteNumber - 4));
+      extension = new WiimoteProfileSetting(mGameID, wiimoteNumber - 4, Settings.SECTION_PROFILE,
+              SettingsFile.KEY_WIIMOTE_EXTENSION, defaultExtension);
     }
 
     sl.add(new StringSingleChoiceSetting(extension, R.string.wiimote_extensions, 0,
@@ -1189,22 +1189,6 @@ public final class SettingsFragmentPresenter
     }
   }
 
-  private String getExtensionValue(int wiimoteNumber)
-  {
-    IniFile.Section section;
-    if (mGameID.equals("")) // Main settings
-    {
-      section = mSettings.getSection(Settings.FILE_WIIMOTE,
-              Settings.SECTION_WIIMOTE + wiimoteNumber);
-    }
-    else // Game settings
-    {
-      section = mSettings.getSection(Settings.GAME_SETTINGS_PLACEHOLDER_FILE_NAME,
-              Settings.SECTION_PROFILE);
-    }
-    return section.getString(SettingsFile.KEY_WIIMOTE_EXTENSION, "None");
-  }
-
   private static int getLogVerbosityEntries()
   {
     // Value obtained from LOG_LEVELS in Common/Logging/Log.h
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 e5238c460c..b058c6cd3f 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
@@ -248,12 +248,6 @@ public final class SettingsFile
     readFile(getGenericGameSettingsForAllRegions(gameId), ini, view);
   }
 
-  public static void readWiimoteProfile(final String gameId, IniFile ini, final int padId)
-  {
-    String profile = gameId + "_Wii" + padId;
-    readFile(getWiiProfile(profile), ini, null);
-  }
-
   /**
    * Saves a given .ini file on disk.
    * If unsuccessful, outputs an error telling why it failed.
@@ -274,69 +268,7 @@ public final class SettingsFile
 
   public static void saveCustomGameSettings(final String gameId, IniFile ini)
   {
-    IniFile iniCopy = new IniFile(ini);
-
-    // Profile options(wii extension) are not saved, only used to properly display values
-    iniCopy.deleteSection(Settings.SECTION_PROFILE);
-
-    for (int i = 0; i < 3; i++)
-    {
-      String key = SettingsFile.KEY_WIIMOTE_EXTENSION + i;
-      if (iniCopy.exists(Settings.SECTION_CONTROLS, key))
-      {
-        // Special case. Extension gets saved into a controller profile
-        String value = iniCopy.getString(Settings.SECTION_CONTROLS, key, "");
-        saveCustomWiimoteSetting(gameId, KEY_WIIMOTE_EXTENSION, value, i);
-        iniCopy.deleteKey(Settings.SECTION_CONTROLS, key);
-      }
-    }
-
-    iniCopy.save(getCustomGameSettingsFile(gameId));
-  }
-
-  /**
-   * Saves the wiimote setting in a profile and enables that profile.
-   *
-   * @param gameId
-   * @param key
-   * @param value
-   * @param padId
-   */
-  private static void saveCustomWiimoteSetting(final String gameId, final String key,
-          final String value, final int padId)
-  {
-    String profile = gameId + "_Wii" + padId;
-    String wiiConfigPath =
-            DirectoryInitialization.getUserDirectory() + "/Config/Profiles/Wiimote/" +
-                    profile + ".ini";
-    File wiiProfile = getWiiProfile(profile);
-    // If it doesn't exist, create it
-    boolean wiiProfileExists = wiiProfile.exists();
-    if (!wiiProfileExists)
-    {
-      String defautlWiiProfilePath =
-              DirectoryInitialization.getUserDirectory() +
-                      "/Config/Profiles/Wiimote/WiimoteProfile.ini";
-      DirectoryInitialization.copyFile(defautlWiiProfilePath, wiiConfigPath);
-    }
-
-    IniFile wiiProfileIni = new IniFile(wiiConfigPath);
-
-    if (!wiiProfileExists)
-    {
-      wiiProfileIni.setString(Settings.SECTION_PROFILE, "Device",
-              "Android/" + (padId + 4) + "/Touchscreen");
-    }
-
-    wiiProfileIni.setString(Settings.SECTION_PROFILE, key, value);
-    wiiProfileIni.save(wiiConfigPath);
-
-    // Enable the profile
-    File gameSettingsFile = SettingsFile.getCustomGameSettingsFile(gameId);
-    IniFile gameSettingsIni = new IniFile(gameSettingsFile);
-    gameSettingsIni.setString(Settings.SECTION_CONTROLS, KEY_WIIMOTE_PROFILE + (padId + 1),
-            profile);
-    gameSettingsIni.save(gameSettingsFile);
+    ini.save(getCustomGameSettingsFile(gameId));
   }
 
   public static String mapSectionNameFromIni(String generalSectionName)
@@ -388,7 +320,7 @@ public final class SettingsFile
             DirectoryInitialization.getUserDirectory() + "/GameSettings/" + gameId + ".ini");
   }
 
-  private static File getWiiProfile(String profile)
+  public static File getWiiProfile(String profile)
   {
     String wiiConfigPath =
             DirectoryInitialization.getUserDirectory() + "/Config/Profiles/Wiimote/" +
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 4e293dfc4a..d94a2d63e2 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
@@ -320,19 +320,6 @@ 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 ignored)
-    {
-    }
-  }
-
   private static void copyFile(InputStream in, OutputStream out) throws IOException
   {
     byte[] buffer = new byte[1024];