From 74f197caedf00df80c411f6cfb299fae5822adba Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 6 Jul 2020 16:57:49 +0200 Subject: [PATCH] Android: Expose a proper interface for C++ IniFile class Replaces the inflexible INI functions in NativeLibrary. --- .../dolphinemu/dolphinemu/NativeLibrary.java | 37 ----- .../activities/EmulationActivity.java | 35 +++-- .../dialogs/GamePropertiesDialog.java | 9 +- .../features/settings/model/Settings.java | 42 +++--- .../settings/model/view/FilePicker.java | 7 +- .../features/settings/ui/SettingsAdapter.java | 7 +- .../ui/viewholder/FilePickerViewHolder.java | 9 +- .../features/settings/utils/SettingsFile.java | 37 +++-- .../dolphinemu/model/GameFileCache.java | 8 +- .../dolphinemu/overlay/InputOverlay.java | 15 ++- .../dolphinemu/ui/main/MainActivity.java | 28 ++-- .../dolphinemu/dolphinemu/utils/IniFile.java | 69 ++++++++++ Source/Android/jni/AndroidCommon/IDCache.cpp | 51 +++++++ Source/Android/jni/AndroidCommon/IDCache.h | 6 + Source/Android/jni/CMakeLists.txt | 1 + Source/Android/jni/IniFile.cpp | 121 +++++++++++++++++ Source/Android/jni/MainAndroid.cpp | 127 ------------------ 17 files changed, 360 insertions(+), 249 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/IniFile.java create mode 100644 Source/Android/jni/IniFile.cpp 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 58c5f0e488..8f3a10b8af 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 @@ -274,43 +274,6 @@ public final class NativeLibrary // Angle is in radians and should be non-negative public static native double GetInputRadiusAtAngle(int emu_pad_id, int stick, double angle); - public static native void NewGameIniFile(); - - 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); - - public static native void SetProfileSetting(String profile, String Section, String Key, - String Value); - - public static native void InitGameIni(String gameID); - - /** - * Gets a value from a key in the given ini-based config file. - * - * @param configFile The ini-based config file to get the value from. - * @param Section The section key that the actual key is in. - * @param Key The key to get the value from. - * @param Default The value to return in the event the given key doesn't exist. - * @return the value stored at the key, or a default value if it doesn't exist. - */ - public static native String GetConfig(String configFile, String Section, String Key, - String Default); - - /** - * Sets a value to a key in the given ini config file. - * - * @param configFile The ini-based config file to add the value to. - * @param Section The section key for the ini key - * @param Key The actual ini key to set. - * @param Value The string to set the ini key to. - */ - public static native void SetConfig(String configFile, String Section, String Key, String Value); - /** * Gets the Dolphin version string. * diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index 1faa1bff43..272ec2d233 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -47,10 +47,12 @@ import org.dolphinemu.dolphinemu.ui.main.TvMainActivity; import org.dolphinemu.dolphinemu.ui.platform.Platform; import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper; import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; +import org.dolphinemu.dolphinemu.utils.IniFile; import org.dolphinemu.dolphinemu.utils.MotionListener; import org.dolphinemu.dolphinemu.utils.Rumble; import org.dolphinemu.dolphinemu.utils.TvUtil; +import java.io.File; import java.lang.annotation.Retention; import java.util.List; @@ -962,10 +964,14 @@ public final class EmulationActivity extends AppCompatActivity (dialog, indexSelected) -> { editor.putInt("wiiController", indexSelected); - NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote1", "Extension", + + File wiimoteNewFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_WIIMOTE); + IniFile wiimoteNewIni = new IniFile(wiimoteNewFile); + wiimoteNewIni.setString("Wiimote1", "Extension", getResources().getStringArray(R.array.controllersValues)[indexSelected]); - NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote1", - "Options/Sideways Wiimote", indexSelected == 2 ? "True" : "False"); + wiimoteNewIni.setBoolean("Wiimote1", "Options/Sideways Wiimote", indexSelected == 2); + wiimoteNewIni.save(wiimoteNewFile); + NativeLibrary.ReloadWiimoteConfig(); }); builder.setPositiveButton(getString(R.string.ok), (dialogInterface, i) -> @@ -994,8 +1000,11 @@ public final class EmulationActivity extends AppCompatActivity else mMotionListener.disable(); - NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote1", "IMUIR/Enabled", - indexSelected != 1 ? "True" : "False"); + File wiimoteNewFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_WIIMOTE); + IniFile wiimoteNewIni = new IniFile(wiimoteNewFile); + wiimoteNewIni.setBoolean("Wiimote1", "IMUIR/Enabled", indexSelected != 1); + wiimoteNewIni.save(wiimoteNewFile); + NativeLibrary.ReloadWiimoteConfig(); }); builder.setPositiveButton(getString(R.string.ok), (dialogInterface, i) -> editor.apply()); @@ -1137,15 +1146,15 @@ public final class EmulationActivity extends AppCompatActivity builder.setView(view); builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> { - NativeLibrary.LoadGameIniFile(mSelectedGameId); - NativeLibrary.SetUserSetting(mSelectedGameId, Settings.SECTION_CONTROLS, - SettingsFile.KEY_WIIBIND_IR_PITCH, text_slider_value_pitch.getText().toString()); - NativeLibrary.SetUserSetting(mSelectedGameId, Settings.SECTION_CONTROLS, - SettingsFile.KEY_WIIBIND_IR_YAW, text_slider_value_yaw.getText().toString()); - NativeLibrary.SetUserSetting(mSelectedGameId, Settings.SECTION_CONTROLS, - SettingsFile.KEY_WIIBIND_IR_VERTICAL_OFFSET, + File file = SettingsFile.getCustomGameSettingsFile(mSelectedGameId); + IniFile ini = new IniFile(file); + ini.setString(Settings.SECTION_CONTROLS, SettingsFile.KEY_WIIBIND_IR_PITCH, + text_slider_value_pitch.getText().toString()); + ini.setString(Settings.SECTION_CONTROLS, SettingsFile.KEY_WIIBIND_IR_YAW, + text_slider_value_yaw.getText().toString()); + ini.setString(Settings.SECTION_CONTROLS, SettingsFile.KEY_WIIBIND_IR_VERTICAL_OFFSET, text_slider_value_vertical_offset.getText().toString()); - NativeLibrary.SaveGameIniFile(mSelectedGameId); + ini.save(file); NativeLibrary.ReloadWiimoteConfig(); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GamePropertiesDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GamePropertiesDialog.java index e720c20ccd..335fb7aba7 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GamePropertiesDialog.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GamePropertiesDialog.java @@ -16,6 +16,7 @@ import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.ui.platform.Platform; +import org.dolphinemu.dolphinemu.utils.IniFile; import org.dolphinemu.dolphinemu.utils.DirectoryInitialization; import org.dolphinemu.dolphinemu.utils.Log; @@ -65,8 +66,12 @@ public class GamePropertiesDialog extends DialogFragment .getSupportFragmentManager(), "game_details"); break; case 1: - NativeLibrary.SetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", - Settings.SECTION_INI_CORE, SettingsFile.KEY_DEFAULT_ISO, path); + File dolphinFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_DOLPHIN); + IniFile dolphinIni = new IniFile(dolphinFile); + dolphinIni.setString(Settings.SECTION_INI_CORE, SettingsFile.KEY_DEFAULT_ISO, + path); + dolphinIni.save(dolphinFile); + NativeLibrary.ReloadConfig(); Toast.makeText(getContext(), "Default ISO set", Toast.LENGTH_SHORT).show(); break; 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 322b52f894..a2eb5501e6 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 @@ -7,7 +7,9 @@ import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivityView; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.services.GameFileCacheService; +import org.dolphinemu.dolphinemu.utils.IniFile; +import java.io.File; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -40,9 +42,9 @@ public class Settings public static final String SECTION_CONTROLS = "Controls"; public static final String SECTION_PROFILE = "Profile"; - private static final String DSP_HLE = "0"; - private static final String DSP_LLE_RECOMPILER = "1"; - private static final String DSP_LLE_INTERPRETER = "2"; + private static final int DSP_HLE = 0; + private static final int DSP_LLE_RECOMPILER = 1; + private static final int DSP_LLE_INTERPRETER = 2; public static final String SECTION_ANALYTICS = "Analytics"; @@ -195,37 +197,29 @@ public class Settings if (modifiedSettings.contains(SettingsFile.KEY_DSP_ENGINE)) { - switch (NativeLibrary - .GetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_ANDROID, - SettingsFile.KEY_DSP_ENGINE, DSP_HLE)) + File dolphinFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_DOLPHIN); + IniFile dolphinIni = new IniFile(dolphinFile); + + switch (dolphinIni.getInt(Settings.SECTION_INI_ANDROID, SettingsFile.KEY_DSP_ENGINE, + DSP_HLE)) { case DSP_HLE: - NativeLibrary - .SetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_CORE, - SettingsFile.KEY_DSP_HLE, "True"); - NativeLibrary - .SetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_DSP, - SettingsFile.KEY_DSP_ENABLE_JIT, "True"); + dolphinIni.setBoolean(Settings.SECTION_INI_CORE, SettingsFile.KEY_DSP_HLE, true); + dolphinIni.setBoolean(Settings.SECTION_INI_DSP, SettingsFile.KEY_DSP_ENABLE_JIT, true); break; case DSP_LLE_RECOMPILER: - NativeLibrary - .SetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_CORE, - SettingsFile.KEY_DSP_HLE, "False"); - NativeLibrary - .SetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_DSP, - SettingsFile.KEY_DSP_ENABLE_JIT, "True"); + dolphinIni.setBoolean(Settings.SECTION_INI_CORE, SettingsFile.KEY_DSP_HLE, false); + dolphinIni.setBoolean(Settings.SECTION_INI_DSP, SettingsFile.KEY_DSP_ENABLE_JIT, true); break; case DSP_LLE_INTERPRETER: - NativeLibrary - .SetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_CORE, - SettingsFile.KEY_DSP_HLE, "False"); - NativeLibrary - .SetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_DSP, - SettingsFile.KEY_DSP_ENABLE_JIT, "False"); + dolphinIni.setBoolean(Settings.SECTION_INI_CORE, SettingsFile.KEY_DSP_HLE, false); + dolphinIni.setBoolean(Settings.SECTION_INI_DSP, SettingsFile.KEY_DSP_ENABLE_JIT, false); break; } + + dolphinIni.save(dolphinFile); } // Notify the native code of the changes diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FilePicker.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FilePicker.java index 4901795e88..6f9d99b108 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FilePicker.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/FilePicker.java @@ -2,6 +2,9 @@ package org.dolphinemu.dolphinemu.features.settings.model.view; import org.dolphinemu.dolphinemu.features.settings.model.Setting; import org.dolphinemu.dolphinemu.features.settings.model.StringSetting; +import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; + +import java.io.File; public final class FilePicker extends SettingsItem { @@ -18,9 +21,9 @@ public final class FilePicker extends SettingsItem mRequestType = requestType; } - public String getFile() + public File getFile() { - return mFile + ".ini"; + return SettingsFile.getSettingsFile(mFile); } public String getSelectedValue() 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 701cb7219a..bbdeff6227 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 @@ -43,6 +43,7 @@ import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SubmenuViewHold import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.ui.main.MainPresenter; import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; +import org.dolphinemu.dolphinemu.utils.IniFile; import java.security.InvalidParameterException; import java.util.ArrayList; @@ -319,8 +320,10 @@ public final class SettingsAdapter extends RecyclerView.Adapter sortedSections = new TreeSet<>(sections.keySet()); - NativeLibrary.NewGameIniFile(); + IniFile ini = new IniFile(); for (String sectionKey : sortedSections) { SettingSection section = sections.get(sectionKey); @@ -491,12 +492,12 @@ public final class SettingsFile } else { - NativeLibrary.SetUserSetting(gameId, mapSectionNameFromIni(section.getName()), - setting.getKey(), setting.getValueAsString()); + ini.setString(mapSectionNameFromIni(section.getName()), setting.getKey(), + setting.getValueAsString()); } } } - NativeLibrary.SaveGameIniFile(gameId); + ini.save(getCustomGameSettingsFile(gameId)); } /** @@ -508,31 +509,40 @@ public final class SettingsFile * @param padId */ private static void saveCustomWiimoteSetting(final String gameId, final String key, - final String value, - final String padId) + final String value, final String padId) { String profile = gameId + "_Wii" + padId; String wiiConfigPath = DirectoryInitialization.getUserDirectory() + "/Config/Profiles/Wiimote/" + profile + ".ini"; - File wiiProfile = new File(wiiConfigPath); + File wiiProfile = getWiiProfile(profile, padId); // If it doesn't exist, create it - if (!wiiProfile.exists()) + boolean wiiProfileExists = wiiProfile.exists(); + if (!wiiProfileExists) { String defautlWiiProfilePath = DirectoryInitialization.getUserDirectory() + "/Config/Profiles/Wiimote/WiimoteProfile.ini"; DirectoryInitialization.copyFile(defautlWiiProfilePath, wiiConfigPath); + } - NativeLibrary.SetProfileSetting(profile, Settings.SECTION_PROFILE, "Device", + IniFile wiiProfileIni = new IniFile(wiiConfigPath); + + if (!wiiProfileExists) + { + wiiProfileIni.setString(Settings.SECTION_PROFILE, "Device", "Android/" + (Integer.parseInt(padId) + 4) + "/Touchscreen"); } - NativeLibrary.SetProfileSetting(profile, Settings.SECTION_PROFILE, key, value); + wiiProfileIni.setString(Settings.SECTION_PROFILE, key, value); + wiiProfileIni.save(wiiConfigPath); // Enable the profile - NativeLibrary.SetUserSetting(gameId, Settings.SECTION_CONTROLS, + File gameSettingsFile = SettingsFile.getCustomGameSettingsFile(gameId); + IniFile gameSettingsIni = new IniFile(gameSettingsFile); + gameSettingsIni.setString(Settings.SECTION_CONTROLS, KEY_WIIMOTE_PROFILE + (Integer.parseInt(padId) + 1), profile); + gameSettingsIni.save(gameSettingsFile); } private static String mapSectionNameFromIni(String generalSectionName) @@ -556,7 +566,7 @@ public final class SettingsFile } @NonNull - private static File getSettingsFile(String fileName) + public static File getSettingsFile(String fileName) { return new File( DirectoryInitialization.getUserDirectory() + "/Config/" + fileName + ".ini"); @@ -578,9 +588,8 @@ public final class SettingsFile gameId + ".ini"); } - private static File getCustomGameSettingsFile(String gameId) + public static File getCustomGameSettingsFile(String gameId) { - return new File( DirectoryInitialization.getUserDirectory() + "/GameSettings/" + gameId + ".ini"); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameFileCache.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameFileCache.java index 4e38297efa..698f2171cf 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameFileCache.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameFileCache.java @@ -7,6 +7,7 @@ import android.preference.PreferenceManager; import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; +import org.dolphinemu.dolphinemu.utils.IniFile; import java.io.File; import java.util.HashSet; @@ -82,9 +83,10 @@ public class GameFileCache */ public boolean scanLibrary(Context context) { - boolean recursiveScan = NativeLibrary - .GetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_GENERAL, - SettingsFile.KEY_RECURSIVE_ISO_PATHS, "False").equals("True"); + IniFile dolphinIni = + new IniFile(SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_DOLPHIN)); + boolean recursiveScan = dolphinIni.getBoolean(Settings.SECTION_INI_GENERAL, + SettingsFile.KEY_RECURSIVE_ISO_PATHS, false); removeNonExistentGameFolders(context); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlay.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlay.java index dfe431cae7..2093c45bcb 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlay.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlay.java @@ -33,6 +33,7 @@ import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.activities.EmulationActivity; import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; +import org.dolphinemu.dolphinemu.utils.IniFile; import java.util.ArrayList; import java.util.HashSet; @@ -51,9 +52,9 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener public static final int OVERLAY_WIIMOTE_CLASSIC = 4; public static final int OVERLAY_NONE = 5; - private static final String DISABLED_GAMECUBE_CONTROLLER = "0"; - private static final String EMULATED_GAMECUBE_CONTROLLER = "6"; - private static final String GAMECUBE_ADAPTER = "12"; + private static final int DISABLED_GAMECUBE_CONTROLLER = 0; + private static final int EMULATED_GAMECUBE_CONTROLLER = 6; + private static final int GAMECUBE_ADAPTER = 12; private final Set overlayButtons = new HashSet<>(); private final Set overlayDpads = new HashSet<>(); @@ -703,9 +704,11 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener // Add all the enabled overlay items back to the HashSet. if (EmulationActivity.isGameCubeGame()) { - switch (NativeLibrary - .GetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_CORE, - SettingsFile.KEY_GCPAD_PLAYER_1, EMULATED_GAMECUBE_CONTROLLER)) + IniFile dolphinIni = + new IniFile(SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_DOLPHIN)); + + switch (dolphinIni.getInt(Settings.SECTION_INI_CORE, SettingsFile.KEY_GCPAD_PLAYER_1, + EMULATED_GAMECUBE_CONTROLLER)) { case DISABLED_GAMECUBE_CONTROLLER: if (mIsFirstRun) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java index 5081e58cb1..b6456f41d3 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java @@ -33,9 +33,12 @@ import org.dolphinemu.dolphinemu.services.GameFileCacheService; import org.dolphinemu.dolphinemu.ui.platform.Platform; import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView; import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; +import org.dolphinemu.dolphinemu.utils.IniFile; import org.dolphinemu.dolphinemu.utils.PermissionsHandler; import org.dolphinemu.dolphinemu.utils.StartupHandler; +import java.io.File; + /** * The main Activity of the Lollipop style UI. Manages several PlatformGamesFragments, which * individually display a grid of available games for each Fragment, in a tabbed layout. @@ -272,24 +275,19 @@ public final class MainActivity extends AppCompatActivity implements MainView public void onTabSelected(@NonNull TabLayout.Tab tab) { super.onTabSelected(tab); - NativeLibrary - .SetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_ANDROID, - SettingsFile.KEY_LAST_PLATFORM_TAB, Integer.toString(tab.getPosition())); + + File dolphinFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_DOLPHIN); + IniFile dolphinIni = new IniFile(dolphinFile); + dolphinIni.setInt(Settings.SECTION_INI_ANDROID, SettingsFile.KEY_LAST_PLATFORM_TAB, + tab.getPosition()); + dolphinIni.save(dolphinFile); } }); - String platformTab = NativeLibrary - .GetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_ANDROID, - SettingsFile.KEY_LAST_PLATFORM_TAB, "0"); - - try - { - mViewPager.setCurrentItem(Integer.parseInt(platformTab)); - } - catch (NumberFormatException ex) - { - mViewPager.setCurrentItem(0); - } + IniFile dolphinIni = + new IniFile(SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_DOLPHIN)); + mViewPager.setCurrentItem(dolphinIni.getInt(Settings.SECTION_INI_ANDROID, + SettingsFile.KEY_LAST_PLATFORM_TAB, 0)); showGames(); GameFileCacheService.startLoad(this); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/IniFile.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/IniFile.java new file mode 100644 index 0000000000..ecbe7b3122 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/IniFile.java @@ -0,0 +1,69 @@ +package org.dolphinemu.dolphinemu.utils; + +import java.io.File; + +// An in-memory copy of an INI file +public class IniFile +{ + // This class is non-static to ensure that the IniFile parent does not get garbage collected + // while a section still is accessible. (The finalizer of IniFile deletes the native sections.) + public class Section + { + private long mPointer; // Do not rename or move without editing the native code + + private Section(long pointer) + { + mPointer = pointer; + } + } + + private long mPointer; // Do not rename or move without editing the native code + + public IniFile() + { + mPointer = newIniFile(); + } + + public IniFile(String path) + { + this(); + load(path, false); + } + + public IniFile(File file) + { + this(); + load(file, false); + } + + public native boolean load(String path, boolean keepCurrentData); + + public boolean load(File file, boolean keepCurrentData) + { + return load(file.getPath(), keepCurrentData); + } + + public native boolean save(String path); + + public boolean save(File file) + { + return save(file.getPath()); + } + + public native String getString(String sectionName, String key, String defaultValue); + + public native boolean getBoolean(String sectionName, String key, boolean defaultValue); + + public native int getInt(String sectionName, String key, int defaultValue); + + public native void setString(String sectionName, String key, String newValue); + + public native void setBoolean(String sectionName, String key, boolean newValue); + + public native void setInt(String sectionName, String key, int newValue); + + @Override + public native void finalize(); + + private native long newIniFile(); +} diff --git a/Source/Android/jni/AndroidCommon/IDCache.cpp b/Source/Android/jni/AndroidCommon/IDCache.cpp index 32160edaee..8383f8ed68 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.cpp +++ b/Source/Android/jni/AndroidCommon/IDCache.cpp @@ -30,6 +30,12 @@ static jclass s_linked_hash_map_class; static jmethodID s_linked_hash_map_init; static jmethodID s_linked_hash_map_put; +static jclass s_ini_file_class; +static jfieldID s_ini_file_pointer; +static jclass s_ini_file_section_class; +static jfieldID s_ini_file_section_pointer; +static jmethodID s_ini_file_section_constructor; + namespace IDCache { JNIEnv* GetEnvForThread() @@ -89,6 +95,7 @@ jmethodID GetAnalyticsValue() { return s_get_analytics_value; } + jclass GetGameFileClass() { return s_game_file_class; @@ -129,6 +136,31 @@ jmethodID GetLinkedHashMapPut() return s_linked_hash_map_put; } +jclass GetIniFileClass() +{ + return s_ini_file_class; +} + +jfieldID GetIniFilePointer() +{ + return s_ini_file_pointer; +} + +jclass GetIniFileSectionClass() +{ + return s_ini_file_section_class; +} + +jfieldID GetIniFileSectionPointer() +{ + return s_ini_file_section_pointer; +} + +jmethodID GetIniFileSectionConstructor() +{ + return s_ini_file_section_constructor; +} + } // namespace IDCache #ifdef __cplusplus @@ -150,16 +182,19 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) s_do_rumble = env->GetStaticMethodID(s_native_library_class, "rumble", "(ID)V"); s_get_update_touch_pointer = env->GetStaticMethodID(s_native_library_class, "updateTouchPointer", "()V"); + env->DeleteLocalRef(native_library_class); const jclass game_file_class = env->FindClass("org/dolphinemu/dolphinemu/model/GameFile"); s_game_file_class = reinterpret_cast(env->NewGlobalRef(game_file_class)); s_game_file_pointer = env->GetFieldID(game_file_class, "mPointer", "J"); s_game_file_constructor = env->GetMethodID(game_file_class, "", "(J)V"); + env->DeleteLocalRef(game_file_class); const jclass game_file_cache_class = env->FindClass("org/dolphinemu/dolphinemu/model/GameFileCache"); s_game_file_cache_class = reinterpret_cast(env->NewGlobalRef(game_file_cache_class)); s_game_file_cache_pointer = env->GetFieldID(game_file_cache_class, "mPointer", "J"); + env->DeleteLocalRef(game_file_cache_class); const jclass analytics_class = env->FindClass("org/dolphinemu/dolphinemu/utils/Analytics"); s_analytics_class = reinterpret_cast(env->NewGlobalRef(analytics_class)); @@ -167,6 +202,20 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) env->GetStaticMethodID(s_analytics_class, "sendReport", "(Ljava/lang/String;[B)V"); s_get_analytics_value = env->GetStaticMethodID(s_analytics_class, "getValue", "(Ljava/lang/String;)Ljava/lang/String;"); + env->DeleteLocalRef(analytics_class); + + const jclass ini_file_class = env->FindClass("org/dolphinemu/dolphinemu/utils/IniFile"); + s_ini_file_class = reinterpret_cast(env->NewGlobalRef(ini_file_class)); + s_ini_file_pointer = env->GetFieldID(ini_file_class, "mPointer", "J"); + env->DeleteLocalRef(ini_file_class); + + const jclass ini_file_section_class = + env->FindClass("org/dolphinemu/dolphinemu/utils/IniFile$Section"); + s_ini_file_section_class = reinterpret_cast(env->NewGlobalRef(ini_file_section_class)); + s_ini_file_section_pointer = env->GetFieldID(ini_file_section_class, "mPointer", "J"); + s_ini_file_section_constructor = env->GetMethodID( + ini_file_section_class, "", "(Lorg/dolphinemu/dolphinemu/utils/IniFile;J)V"); + env->DeleteLocalRef(ini_file_section_class); const jclass map_class = env->FindClass("java/util/LinkedHashMap"); s_linked_hash_map_class = reinterpret_cast(env->NewGlobalRef(map_class)); @@ -188,6 +237,8 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) env->DeleteGlobalRef(s_game_file_cache_class); env->DeleteGlobalRef(s_analytics_class); env->DeleteGlobalRef(s_linked_hash_map_class); + env->DeleteGlobalRef(s_ini_file_class); + env->DeleteGlobalRef(s_ini_file_section_class); } #ifdef __cplusplus diff --git a/Source/Android/jni/AndroidCommon/IDCache.h b/Source/Android/jni/AndroidCommon/IDCache.h index 286009239f..77f49658b8 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.h +++ b/Source/Android/jni/AndroidCommon/IDCache.h @@ -32,4 +32,10 @@ jclass GetLinkedHashMapClass(); jmethodID GetLinkedHashMapInit(); jmethodID GetLinkedHashMapPut(); +jclass GetIniFileClass(); +jfieldID GetIniFilePointer(); +jclass GetIniFileSectionClass(); +jfieldID GetIniFileSectionPointer(); +jmethodID GetIniFileSectionConstructor(); + } // namespace IDCache diff --git a/Source/Android/jni/CMakeLists.txt b/Source/Android/jni/CMakeLists.txt index 6279c9e94b..4e806c455b 100644 --- a/Source/Android/jni/CMakeLists.txt +++ b/Source/Android/jni/CMakeLists.txt @@ -3,6 +3,7 @@ add_library(main SHARED AndroidCommon/IDCache.cpp GameList/GameFile.cpp GameList/GameFileCache.cpp + IniFile.cpp MainAndroid.cpp ) diff --git a/Source/Android/jni/IniFile.cpp b/Source/Android/jni/IniFile.cpp new file mode 100644 index 0000000000..0237fec4dc --- /dev/null +++ b/Source/Android/jni/IniFile.cpp @@ -0,0 +1,121 @@ +// Copyright 2020 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include "Common/IniFile.h" +#include "jni/AndroidCommon/AndroidCommon.h" +#include "jni/AndroidCommon/IDCache.h" + +static IniFile::Section* GetSectionPointer(JNIEnv* env, jobject obj) +{ + return reinterpret_cast( + env->GetLongField(obj, IDCache::GetIniFileSectionPointer())); +} + +static IniFile* GetIniFilePointer(JNIEnv* env, jobject obj) +{ + return reinterpret_cast(env->GetLongField(obj, IDCache::GetIniFilePointer())); +} + +static jobject SectionToJava(JNIEnv* env, jobject ini_file, IniFile::Section* section) +{ + if (!section) + return nullptr; + + return env->NewObject(IDCache::GetIniFileSectionClass(), IDCache::GetIniFileSectionConstructor(), + ini_file, reinterpret_cast(section)); +} + +template +static T Get(JNIEnv* env, jobject obj, jstring section_name, jstring key, T default_value) +{ + T result; + GetIniFilePointer(env, obj) + ->GetOrCreateSection(GetJString(env, section_name)) + ->Get(GetJString(env, key), &result, default_value); + return result; +} + +template +static void Set(JNIEnv* env, jobject obj, jstring section_name, jstring key, T new_value) +{ + GetIniFilePointer(env, obj) + ->GetOrCreateSection(GetJString(env, section_name)) + ->Set(GetJString(env, key), new_value); +} + +#ifdef __cplusplus +extern "C" { +#endif + +JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_load( + JNIEnv* env, jobject obj, jstring path, jboolean keep_current_data) +{ + return static_cast( + GetIniFilePointer(env, obj)->Load(GetJString(env, path), keep_current_data)); +} + +JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_save(JNIEnv* env, + jobject obj, + jstring path) +{ + return static_cast(GetIniFilePointer(env, obj)->Save(GetJString(env, path))); +} + +JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_getString( + JNIEnv* env, jobject obj, jstring section_name, jstring key, jstring default_value) +{ + return ToJString(env, Get(env, obj, section_name, key, GetJString(env, default_value))); +} + +JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_getBoolean( + JNIEnv* env, jobject obj, jstring section_name, jstring key, jboolean default_value) +{ + return static_cast(Get(env, obj, section_name, key, static_cast(default_value))); +} + +JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_getInt(JNIEnv* env, jobject obj, + jstring section_name, + jstring key, + jint default_value) +{ + return Get(env, obj, section_name, key, default_value); +} + +JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_setString( + JNIEnv* env, jobject obj, jstring section_name, jstring key, jstring new_value) +{ + Set(env, obj, section_name, key, GetJString(env, new_value)); +} + +JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_setBoolean( + JNIEnv* env, jobject obj, jstring section_name, jstring key, jboolean new_value) +{ + Set(env, obj, section_name, key, static_cast(new_value)); +} + +JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_setInt(JNIEnv* env, jobject obj, + jstring section_name, + jstring key, + jint new_value) +{ + Set(env, obj, section_name, key, new_value); +} + +JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_finalize(JNIEnv* env, + jobject obj) +{ + delete GetIniFilePointer(env, obj); +} + +JNIEXPORT jlong JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_newIniFile(JNIEnv* env, + jobject obj) +{ + return reinterpret_cast(new IniFile); +} + +#ifdef __cplusplus +} +#endif diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index 8393fe53d8..0732987a9d 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -213,10 +213,6 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveScreenSh JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_eglBindAPI(JNIEnv* env, jobject obj, jint api); -JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetConfig( - JNIEnv* env, jobject obj, jstring jFile, jstring jSection, jstring jKey, jstring jDefault); -JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetConfig( - JNIEnv* env, jobject obj, jstring jFile, jstring jSection, jstring jKey, jstring jValue); JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetFilename(JNIEnv* env, jobject obj, jstring jFile); @@ -355,129 +351,6 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_eglBindAPI(J eglBindAPI(api); } -JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_InitGameIni(JNIEnv* env, - jobject obj, - jstring jGameID) -{ - // Initialize an empty INI file - IniFile ini; - std::string gameid = GetJString(env, jGameID); - - __android_log_print(ANDROID_LOG_DEBUG, "InitGameIni", "Initializing base game config file"); - ini.Save(File::GetUserPath(D_GAMESETTINGS_IDX) + gameid + ".ini"); -} - -JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetUserSetting( - JNIEnv* env, jobject obj, jstring jGameID, jstring jSection, jstring jKey) -{ - IniFile ini; - std::string gameid = GetJString(env, jGameID); - std::string section = GetJString(env, jSection); - std::string key = GetJString(env, jKey); - - ini = SConfig::LoadGameIni(gameid, 0); - std::string value; - - ini.GetOrCreateSection(section)->Get(key, &value, "-1"); - - return ToJString(env, value); -} - -JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_NewGameIniFile(JNIEnv* env, - jobject obj) -{ - s_ini = IniFile(); -} - -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) -{ - std::string gameid = GetJString(env, jGameID); - std::string section = GetJString(env, jSection); - std::string key = GetJString(env, jKey); - std::string val = GetJString(env, jValue); - - if (val != "-1") - { - s_ini.GetOrCreateSection(section)->Set(key, val); - } - else - { - s_ini.GetOrCreateSection(section)->Delete(key); - } -} - -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) -{ - IniFile ini; - std::string file = GetJString(env, jFile); - std::string section = GetJString(env, jSection); - std::string key = GetJString(env, jKey); - std::string defaultValue = GetJString(env, jDefault); - - ini.Load(File::GetUserPath(D_CONFIG_IDX) + std::string(file)); - std::string value; - - ini.GetOrCreateSection(section)->Get(key, &value, defaultValue); - - return ToJString(env, value); -} - -JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetConfig( - JNIEnv* env, jobject obj, jstring jFile, jstring jSection, jstring jKey, jstring jValue) -{ - IniFile ini; - std::string file = GetJString(env, jFile); - std::string section = GetJString(env, jSection); - std::string key = GetJString(env, jKey); - std::string value = GetJString(env, jValue); - - ini.Load(File::GetUserPath(D_CONFIG_IDX) + std::string(file)); - - ini.GetOrCreateSection(section)->Set(key, value); - ini.Save(File::GetUserPath(D_CONFIG_IDX) + std::string(file)); -} - JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveState(JNIEnv* env, jobject obj, jint slot,