Merge pull request #8941 from JosJuice/android-ini

Android: Replace Java INI parser with C++ INI parser
This commit is contained in:
JMC47 2020-09-06 07:51:01 -04:00 committed by GitHub
commit a390640550
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1610 additions and 2488 deletions

View File

@ -274,43 +274,6 @@ public final class NativeLibrary
// Angle is in radians and should be non-negative // 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 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. * Gets the Dolphin version string.
* *

View File

@ -32,7 +32,6 @@ import android.widget.Toast;
import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
import org.dolphinemu.dolphinemu.fragments.EmulationFragment; import org.dolphinemu.dolphinemu.fragments.EmulationFragment;
@ -47,10 +46,12 @@ import org.dolphinemu.dolphinemu.ui.main.TvMainActivity;
import org.dolphinemu.dolphinemu.ui.platform.Platform; import org.dolphinemu.dolphinemu.ui.platform.Platform;
import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper; import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper;
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
import org.dolphinemu.dolphinemu.utils.IniFile;
import org.dolphinemu.dolphinemu.utils.MotionListener; import org.dolphinemu.dolphinemu.utils.MotionListener;
import org.dolphinemu.dolphinemu.utils.Rumble; import org.dolphinemu.dolphinemu.utils.Rumble;
import org.dolphinemu.dolphinemu.utils.TvUtil; import org.dolphinemu.dolphinemu.utils.TvUtil;
import java.io.File;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.util.List; import java.util.List;
@ -519,10 +520,8 @@ public final class EmulationActivity extends AppCompatActivity
showUnpauseEmulationButton(); showUnpauseEmulationButton();
} }
BooleanSetting enableSaveStates = if (mSettings.getSection(SettingsFile.FILE_NAME_DOLPHIN, Settings.SECTION_INI_CORE)
(BooleanSetting) mSettings.getSection(Settings.SECTION_INI_CORE) .getBoolean(SettingsFile.KEY_ENABLE_SAVE_STATES, false))
.getSetting(SettingsFile.KEY_ENABLE_SAVE_STATES);
if (enableSaveStates != null && enableSaveStates.getValue())
{ {
menu.findItem(R.id.menu_quicksave).setVisible(true); menu.findItem(R.id.menu_quicksave).setVisible(true);
menu.findItem(R.id.menu_quickload).setVisible(true); menu.findItem(R.id.menu_quickload).setVisible(true);
@ -962,10 +961,14 @@ public final class EmulationActivity extends AppCompatActivity
(dialog, indexSelected) -> (dialog, indexSelected) ->
{ {
editor.putInt("wiiController", 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]); getResources().getStringArray(R.array.controllersValues)[indexSelected]);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote1", wiimoteNewIni.setBoolean("Wiimote1", "Options/Sideways Wiimote", indexSelected == 2);
"Options/Sideways Wiimote", indexSelected == 2 ? "True" : "False"); wiimoteNewIni.save(wiimoteNewFile);
NativeLibrary.ReloadWiimoteConfig(); NativeLibrary.ReloadWiimoteConfig();
}); });
builder.setPositiveButton(getString(R.string.ok), (dialogInterface, i) -> builder.setPositiveButton(getString(R.string.ok), (dialogInterface, i) ->
@ -994,8 +997,11 @@ public final class EmulationActivity extends AppCompatActivity
else else
mMotionListener.disable(); mMotionListener.disable();
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote1", "IMUIR/Enabled", File wiimoteNewFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_WIIMOTE);
indexSelected != 1 ? "True" : "False"); IniFile wiimoteNewIni = new IniFile(wiimoteNewFile);
wiimoteNewIni.setBoolean("Wiimote1", "IMUIR/Enabled", indexSelected != 1);
wiimoteNewIni.save(wiimoteNewFile);
NativeLibrary.ReloadWiimoteConfig(); NativeLibrary.ReloadWiimoteConfig();
}); });
builder.setPositiveButton(getString(R.string.ok), (dialogInterface, i) -> editor.apply()); builder.setPositiveButton(getString(R.string.ok), (dialogInterface, i) -> editor.apply());
@ -1137,15 +1143,15 @@ public final class EmulationActivity extends AppCompatActivity
builder.setView(view); builder.setView(view);
builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> builder.setPositiveButton(R.string.ok, (dialogInterface, i) ->
{ {
NativeLibrary.LoadGameIniFile(mSelectedGameId); File file = SettingsFile.getCustomGameSettingsFile(mSelectedGameId);
NativeLibrary.SetUserSetting(mSelectedGameId, Settings.SECTION_CONTROLS, IniFile ini = new IniFile(file);
SettingsFile.KEY_WIIBIND_IR_PITCH, text_slider_value_pitch.getText().toString()); ini.setString(Settings.SECTION_CONTROLS, SettingsFile.KEY_WIIBIND_IR_PITCH,
NativeLibrary.SetUserSetting(mSelectedGameId, Settings.SECTION_CONTROLS, text_slider_value_pitch.getText().toString());
SettingsFile.KEY_WIIBIND_IR_YAW, text_slider_value_yaw.getText().toString()); ini.setString(Settings.SECTION_CONTROLS, SettingsFile.KEY_WIIBIND_IR_YAW,
NativeLibrary.SetUserSetting(mSelectedGameId, Settings.SECTION_CONTROLS, text_slider_value_yaw.getText().toString());
SettingsFile.KEY_WIIBIND_IR_VERTICAL_OFFSET, ini.setString(Settings.SECTION_CONTROLS, SettingsFile.KEY_WIIBIND_IR_VERTICAL_OFFSET,
text_slider_value_vertical_offset.getText().toString()); text_slider_value_vertical_offset.getText().toString());
NativeLibrary.SaveGameIniFile(mSelectedGameId); ini.save(file);
NativeLibrary.ReloadWiimoteConfig(); NativeLibrary.ReloadWiimoteConfig();

View File

@ -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.ui.SettingsActivity;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
import org.dolphinemu.dolphinemu.ui.platform.Platform; import org.dolphinemu.dolphinemu.ui.platform.Platform;
import org.dolphinemu.dolphinemu.utils.IniFile;
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization; import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
import org.dolphinemu.dolphinemu.utils.Log; import org.dolphinemu.dolphinemu.utils.Log;
@ -65,8 +66,12 @@ public class GamePropertiesDialog extends DialogFragment
.getSupportFragmentManager(), "game_details"); .getSupportFragmentManager(), "game_details");
break; break;
case 1: case 1:
NativeLibrary.SetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", File dolphinFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_DOLPHIN);
Settings.SECTION_INI_CORE, SettingsFile.KEY_DEFAULT_ISO, path); IniFile dolphinIni = new IniFile(dolphinFile);
dolphinIni.setString(Settings.SECTION_INI_CORE, SettingsFile.KEY_DEFAULT_ISO,
path);
dolphinIni.save(dolphinFile);
NativeLibrary.ReloadConfig(); NativeLibrary.ReloadConfig();
Toast.makeText(getContext(), "Default ISO set", Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), "Default ISO set", Toast.LENGTH_SHORT).show();
break; break;

View File

@ -9,6 +9,7 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper; import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper;
import org.dolphinemu.dolphinemu.utils.Log; import org.dolphinemu.dolphinemu.utils.Log;
import org.dolphinemu.dolphinemu.utils.TvUtil; import org.dolphinemu.dolphinemu.utils.TvUtil;
@ -27,6 +28,7 @@ public final class MotionAlertDialog extends AlertDialog
private final ArrayList<Float> mPreviousValues = new ArrayList<>(); private final ArrayList<Float> mPreviousValues = new ArrayList<>();
private int mPrevDeviceId = 0; private int mPrevDeviceId = 0;
private boolean mWaitingForEvent = true; private boolean mWaitingForEvent = true;
private SettingsAdapter mAdapter;
/** /**
* Constructor * Constructor
@ -34,11 +36,12 @@ public final class MotionAlertDialog extends AlertDialog
* @param context The current {@link Context}. * @param context The current {@link Context}.
* @param setting The Preference to show this dialog for. * @param setting The Preference to show this dialog for.
*/ */
public MotionAlertDialog(Context context, InputBindingSetting setting) public MotionAlertDialog(Context context, InputBindingSetting setting, SettingsAdapter adapter)
{ {
super(context); super(context);
this.setting = setting; this.setting = setting;
mAdapter = adapter;
} }
public boolean onKeyEvent(int keyCode, KeyEvent event) public boolean onKeyEvent(int keyCode, KeyEvent event)
@ -48,7 +51,7 @@ public final class MotionAlertDialog extends AlertDialog
{ {
if (!ControllerMappingHelper.shouldKeyBeIgnored(event.getDevice(), keyCode)) if (!ControllerMappingHelper.shouldKeyBeIgnored(event.getDevice(), keyCode))
{ {
setting.onKeyInput(event); setting.onKeyInput(mAdapter.getSettings(), event);
dismiss(); dismiss();
} }
// Even if we ignore the key, we still consume it. Thus return true regardless. // Even if we ignore the key, we still consume it. Thus return true regardless.
@ -63,7 +66,7 @@ public final class MotionAlertDialog extends AlertDialog
// Option to clear by long back is only needed on the TV interface // Option to clear by long back is only needed on the TV interface
if (TvUtil.isLeanback(getContext()) && keyCode == KeyEvent.KEYCODE_BACK) if (TvUtil.isLeanback(getContext()) && keyCode == KeyEvent.KEYCODE_BACK)
{ {
setting.clearValue(); setting.clearValue(mAdapter.getSettings());
dismiss(); dismiss();
return true; return true;
} }
@ -158,7 +161,7 @@ public final class MotionAlertDialog extends AlertDialog
if (numMovedAxis == 1) if (numMovedAxis == 1)
{ {
mWaitingForEvent = false; mWaitingForEvent = false;
setting.onMotionInput(input, lastMovedRange, lastMovedDir); setting.onMotionInput(mAdapter.getSettings(), input, lastMovedRange, lastMovedDir);
dismiss(); dismiss();
} }
} }

View File

@ -1,28 +0,0 @@
package org.dolphinemu.dolphinemu.features.settings.model;
public final class BooleanSetting extends Setting
{
private boolean mValue;
public BooleanSetting(String key, String section, boolean value)
{
super(key, section);
mValue = value;
}
public boolean getValue()
{
return mValue;
}
public void setValue(boolean value)
{
mValue = value;
}
@Override
public String getValueAsString()
{
return mValue ? "True" : "False";
}
}

View File

@ -1,28 +0,0 @@
package org.dolphinemu.dolphinemu.features.settings.model;
public final class FloatSetting extends Setting
{
private float mValue;
public FloatSetting(String key, String section, float value)
{
super(key, section);
mValue = value;
}
public float getValue()
{
return mValue;
}
public void setValue(float value)
{
mValue = value;
}
@Override
public String getValueAsString()
{
return Float.toString(mValue);
}
}

View File

@ -1,44 +0,0 @@
package org.dolphinemu.dolphinemu.features.settings.model;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
public final class IntSetting extends Setting
{
private int mValue;
private MenuTag menuTag;
public IntSetting(String key, String section, int value)
{
super(key, section);
mValue = value;
}
public IntSetting(String key, String section, int value, MenuTag menuTag)
{
super(key, section);
mValue = value;
this.menuTag = menuTag;
}
public int getValue()
{
return mValue;
}
public void setValue(int value)
{
mValue = value;
}
@Override
public String getValueAsString()
{
return Integer.toString(mValue);
}
public MenuTag getMenuTag()
{
return menuTag;
}
}

View File

@ -1,46 +0,0 @@
package org.dolphinemu.dolphinemu.features.settings.model;
/**
* Abstraction for a setting item as read from / written to Dolphin's configuration ini files.
* These files generally consist of a key/value pair, though the type of value is ambiguous and
* must be inferred at read-time. The type of value determines which child of this class is used
* to represent the Setting.
*/
public abstract class Setting
{
private String mKey;
private String mSection;
/**
* Base constructor.
*
* @param key Everything to the left of the = in a line from the ini file.
* @param section The corresponding recent section header; e.g. [Core] or [Enhancements] without the brackets.
*/
public Setting(String key, String section)
{
mKey = key;
mSection = section;
}
/**
* @return The identifier used to write this setting to the ini file.
*/
public String getKey()
{
return mKey;
}
/**
* @return The name of the header under which this Setting should be written in the ini file.
*/
public String getSection()
{
return mSection;
}
/**
* @return A representation of this Setting's backing value converted to a String (e.g. for serialization).
*/
public abstract String getValueAsString();
}

View File

@ -1,63 +0,0 @@
package org.dolphinemu.dolphinemu.features.settings.model;
import java.util.HashMap;
/**
* A semantically-related group of Settings objects. These Settings are
* internally stored as a HashMap.
*/
public final class SettingSection
{
private String mName;
private HashMap<String, Setting> mSettings = new HashMap<>();
/**
* Create a new SettingSection with no Settings in it.
*
* @param name The header of this section; e.g. [Core] or [Enhancements] without the brackets.
*/
public SettingSection(String name)
{
mName = name;
}
public String getName()
{
return mName;
}
/**
* Convenience method; inserts a value directly into the backing HashMap.
*
* @param setting The Setting to be inserted.
*/
public void putSetting(Setting setting)
{
mSettings.put(setting.getKey(), setting);
}
/**
* Convenience method; gets a value directly from the backing HashMap.
*
* @param key Used to retrieve the Setting.
* @return A Setting object (you should probably cast this before using)
*/
public Setting getSetting(String key)
{
return mSettings.get(key);
}
public HashMap<String, Setting> getSettings()
{
return mSettings;
}
public void mergeSection(SettingSection settingSection)
{
for (Setting setting : settingSection.mSettings.values())
{
putSetting(setting);
}
}
}

View File

@ -7,7 +7,9 @@ import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivityView; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivityView;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
import org.dolphinemu.dolphinemu.services.GameFileCacheService; import org.dolphinemu.dolphinemu.services.GameFileCacheService;
import org.dolphinemu.dolphinemu.utils.IniFile;
import java.io.File;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -40,79 +42,51 @@ public class Settings
public static final String SECTION_CONTROLS = "Controls"; public static final String SECTION_CONTROLS = "Controls";
public static final String SECTION_PROFILE = "Profile"; public static final String SECTION_PROFILE = "Profile";
private static final String DSP_HLE = "0"; private static final int DSP_HLE = 0;
private static final String DSP_LLE_RECOMPILER = "1"; private static final int DSP_LLE_RECOMPILER = 1;
private static final String DSP_LLE_INTERPRETER = "2"; private static final int DSP_LLE_INTERPRETER = 2;
public static final String SECTION_ANALYTICS = "Analytics"; public static final String SECTION_ANALYTICS = "Analytics";
public static final String GAME_SETTINGS_PLACEHOLDER_FILE_NAME = "";
private String gameId; private String gameId;
private static final Map<String, List<String>> configFileSectionsMap = new HashMap<>(); private static final String[] configFiles = new String[]{SettingsFile.FILE_NAME_DOLPHIN,
SettingsFile.FILE_NAME_GFX, SettingsFile.FILE_NAME_LOGGER,
SettingsFile.FILE_NAME_WIIMOTE};
static private HashMap<String, IniFile> mIniFiles = new HashMap<>();
private IniFile getGameSpecificFile()
{ {
configFileSectionsMap.put(SettingsFile.FILE_NAME_DOLPHIN, if (TextUtils.isEmpty(gameId) || mIniFiles.size() != 1)
Arrays throw new IllegalStateException();
.asList(SECTION_INI_ANDROID, SECTION_INI_GENERAL, SECTION_INI_CORE,
SECTION_INI_INTERFACE, return mIniFiles.get(GAME_SETTINGS_PLACEHOLDER_FILE_NAME);
SECTION_INI_DSP, SECTION_BINDINGS, SECTION_ANALYTICS, SECTION_DEBUG));
configFileSectionsMap.put(SettingsFile.FILE_NAME_GFX,
Arrays.asList(SECTION_GFX_SETTINGS, SECTION_GFX_ENHANCEMENTS, SECTION_GFX_HACKS,
SECTION_STEREOSCOPY));
configFileSectionsMap.put(SettingsFile.FILE_NAME_LOGGER,
Arrays.asList(SECTION_LOGGER_LOGS, SECTION_LOGGER_OPTIONS));
configFileSectionsMap.put(SettingsFile.FILE_NAME_WIIMOTE,
Arrays.asList(SECTION_WIIMOTE + 1, SECTION_WIIMOTE + 2, SECTION_WIIMOTE + 3,
SECTION_WIIMOTE + 4));
} }
/** public IniFile.Section getSection(String fileName, String sectionName)
* A HashMap<String, SettingSection> that constructs a new SettingSection instead of returning null
* when getting a key not already in the map
*/
public static final class SettingsSectionMap extends HashMap<String, SettingSection>
{ {
@Override if (TextUtils.isEmpty(gameId))
public SettingSection get(Object key)
{ {
if (!(key instanceof String)) return mIniFiles.get(fileName).getOrCreateSection(sectionName);
{
return null;
} }
else
String stringKey = (String) key;
if (!super.containsKey(stringKey))
{ {
SettingSection section = new SettingSection(stringKey); return getGameSpecificFile()
super.put(stringKey, section); .getOrCreateSection(SettingsFile.mapSectionNameFromIni(sectionName));
return section;
} }
return super.get(key);
}
}
private HashMap<String, SettingSection> sections = new Settings.SettingsSectionMap();
public SettingSection getSection(String sectionName)
{
return sections.get(sectionName);
} }
public boolean isEmpty() public boolean isEmpty()
{ {
return sections.isEmpty(); return mIniFiles.isEmpty();
}
public HashMap<String, SettingSection> getSections()
{
return sections;
} }
public void loadSettings(SettingsActivityView view) public void loadSettings(SettingsActivityView view)
{ {
sections = new Settings.SettingsSectionMap(); mIniFiles = new HashMap<>();
if (TextUtils.isEmpty(gameId)) if (TextUtils.isEmpty(gameId))
{ {
@ -126,46 +100,24 @@ public class Settings
private void loadDolphinSettings(SettingsActivityView view) private void loadDolphinSettings(SettingsActivityView view)
{ {
for (Map.Entry<String, List<String>> entry : configFileSectionsMap.entrySet()) for (String fileName : configFiles)
{ {
String fileName = entry.getKey(); IniFile ini = new IniFile();
sections.putAll(SettingsFile.readFile(fileName, view)); SettingsFile.readFile(fileName, ini, view);
mIniFiles.put(fileName, ini);
} }
} }
private void loadGenericGameSettings(String gameId, SettingsActivityView view)
{
// generic game settings
mergeSections(SettingsFile.readGenericGameSettings(gameId, view));
mergeSections(SettingsFile.readGenericGameSettingsForAllRegions(gameId, view));
}
private void loadCustomGameSettings(String gameId, SettingsActivityView view) private void loadCustomGameSettings(String gameId, SettingsActivityView view)
{ {
// custom game settings IniFile ini = new IniFile();
mergeSections(SettingsFile.readCustomGameSettings(gameId, view)); SettingsFile.readCustomGameSettings(gameId, ini, view);
mIniFiles.put(GAME_SETTINGS_PLACEHOLDER_FILE_NAME, ini);
} }
public void loadWiimoteProfile(String gameId, String padId) public void loadWiimoteProfile(String gameId, int padId)
{ {
mergeSections(SettingsFile.readWiimoteProfile(gameId, padId)); SettingsFile.readWiimoteProfile(gameId, getGameSpecificFile(), padId);
}
private void mergeSections(HashMap<String, SettingSection> updatedSections)
{
for (Map.Entry<String, SettingSection> entry : updatedSections.entrySet())
{
if (sections.containsKey(entry.getKey()))
{
SettingSection originalSection = sections.get(entry.getKey());
SettingSection updatedSection = entry.getValue();
originalSection.mergeSection(updatedSection);
}
else
{
sections.put(entry.getKey(), entry.getValue());
}
}
} }
public void loadSettings(String gameId, SettingsActivityView view) public void loadSettings(String gameId, SettingsActivityView view)
@ -180,52 +132,36 @@ public class Settings
{ {
view.showToastMessage("Saved settings to INI files"); view.showToastMessage("Saved settings to INI files");
for (Map.Entry<String, List<String>> entry : configFileSectionsMap.entrySet()) for (Map.Entry<String, IniFile> entry : mIniFiles.entrySet())
{ {
String fileName = entry.getKey(); SettingsFile.saveFile(entry.getKey(), entry.getValue(), view);
List<String> sectionNames = entry.getValue();
TreeMap<String, SettingSection> iniSections = new TreeMap<>();
for (String section : sectionNames)
{
iniSections.put(section, sections.get(section));
}
SettingsFile.saveFile(fileName, iniSections, view);
} }
if (modifiedSettings.contains(SettingsFile.KEY_DSP_ENGINE)) if (modifiedSettings.contains(SettingsFile.KEY_DSP_ENGINE))
{ {
switch (NativeLibrary File dolphinFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_DOLPHIN);
.GetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_ANDROID, IniFile dolphinIni = new IniFile(dolphinFile);
SettingsFile.KEY_DSP_ENGINE, DSP_HLE))
switch (dolphinIni.getInt(Settings.SECTION_INI_ANDROID, SettingsFile.KEY_DSP_ENGINE,
DSP_HLE))
{ {
case DSP_HLE: case DSP_HLE:
NativeLibrary dolphinIni.setBoolean(Settings.SECTION_INI_CORE, SettingsFile.KEY_DSP_HLE, true);
.SetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_CORE, dolphinIni.setBoolean(Settings.SECTION_INI_DSP, SettingsFile.KEY_DSP_ENABLE_JIT, true);
SettingsFile.KEY_DSP_HLE, "True");
NativeLibrary
.SetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_DSP,
SettingsFile.KEY_DSP_ENABLE_JIT, "True");
break; break;
case DSP_LLE_RECOMPILER: case DSP_LLE_RECOMPILER:
NativeLibrary dolphinIni.setBoolean(Settings.SECTION_INI_CORE, SettingsFile.KEY_DSP_HLE, false);
.SetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_CORE, dolphinIni.setBoolean(Settings.SECTION_INI_DSP, SettingsFile.KEY_DSP_ENABLE_JIT, true);
SettingsFile.KEY_DSP_HLE, "False");
NativeLibrary
.SetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_DSP,
SettingsFile.KEY_DSP_ENABLE_JIT, "True");
break; break;
case DSP_LLE_INTERPRETER: case DSP_LLE_INTERPRETER:
NativeLibrary dolphinIni.setBoolean(Settings.SECTION_INI_CORE, SettingsFile.KEY_DSP_HLE, false);
.SetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_CORE, dolphinIni.setBoolean(Settings.SECTION_INI_DSP, SettingsFile.KEY_DSP_ENABLE_JIT, false);
SettingsFile.KEY_DSP_HLE, "False");
NativeLibrary
.SetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_DSP,
SettingsFile.KEY_DSP_ENABLE_JIT, "False");
break; break;
} }
dolphinIni.save(dolphinFile);
} }
// Notify the native code of the changes // Notify the native code of the changes
@ -246,13 +182,16 @@ public class Settings
{ {
// custom game settings // custom game settings
view.showToastMessage("Saved settings for " + gameId); view.showToastMessage("Saved settings for " + gameId);
SettingsFile.saveCustomGameSettings(gameId, sections); SettingsFile.saveCustomGameSettings(gameId, getGameSpecificFile());
} }
} }
public void clearSettings() public void clearSettings()
{ {
sections.clear(); for (String fileName : mIniFiles.keySet())
{
mIniFiles.put(fileName, new IniFile());
}
} }
public boolean gameIniContainsJunk() public boolean gameIniContainsJunk()
@ -278,7 +217,6 @@ public class Settings
if (TextUtils.isEmpty(gameId)) if (TextUtils.isEmpty(gameId))
return false; return false;
SettingSection interfaceSection = sections.get("Interface"); return getSection(SettingsFile.FILE_NAME_DOLPHIN, SECTION_INI_INTERFACE).exists("ThemeName");
return interfaceSection != null && interfaceSection.getSetting("ThemeName") != null;
} }
} }

View File

@ -1,28 +0,0 @@
package org.dolphinemu.dolphinemu.features.settings.model;
public final class StringSetting extends Setting
{
private String mValue;
public StringSetting(String key, String section, String value)
{
super(key, section);
mValue = value;
}
public String getValue()
{
return mValue;
}
public void setValue(String value)
{
mValue = value;
}
@Override
public String getValueAsString()
{
return mValue;
}
}

View File

@ -1,51 +1,26 @@
package org.dolphinemu.dolphinemu.features.settings.model.view; package org.dolphinemu.dolphinemu.features.settings.model.view;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.Setting;
public final class CheckBoxSetting extends SettingsItem public class CheckBoxSetting extends SettingsItem
{ {
private boolean mDefaultValue; protected boolean mDefaultValue;
public CheckBoxSetting(String key, String section, int titleId, int descriptionId, public CheckBoxSetting(String file, String section, String key, int titleId, int descriptionId,
boolean defaultValue, Setting setting) boolean defaultValue)
{ {
super(key, section, setting, titleId, descriptionId); super(file, section, key, titleId, descriptionId);
mDefaultValue = defaultValue; mDefaultValue = defaultValue;
} }
public boolean isChecked() public boolean isChecked(Settings settings)
{ {
if (getSetting() == null || !(getSetting() instanceof BooleanSetting)) return settings.getSection(getFile(), getSection()).getBoolean(getKey(), mDefaultValue);
{
return mDefaultValue;
} }
BooleanSetting setting = (BooleanSetting) getSetting(); public void setChecked(Settings settings, boolean checked)
return setting.getValue();
}
/**
* Write a value to the backing boolean. If that boolean was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param checked Pretty self explanatory.
* @return null if overwritten successfully; otherwise, a newly created BooleanSetting.
*/
public BooleanSetting setChecked(boolean checked)
{ {
if (getSetting() == null || !(getSetting() instanceof BooleanSetting)) settings.getSection(getFile(), getSection()).setBoolean(getKey(), checked);
{
BooleanSetting setting = new BooleanSetting(getKey(), getSection(), checked);
setSetting(setting);
return setting;
}
else
{
BooleanSetting setting = (BooleanSetting) getSetting();
setting.setValue(checked);
return null;
}
} }
@Override @Override

View File

@ -1,39 +1,23 @@
package org.dolphinemu.dolphinemu.features.settings.model.view; package org.dolphinemu.dolphinemu.features.settings.model.view;
import org.dolphinemu.dolphinemu.features.settings.model.Setting; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
public final class FilePicker extends SettingsItem public final class FilePicker extends SettingsItem
{ {
private String mFile;
private String mDefaultValue; private String mDefaultValue;
private int mRequestType; private int mRequestType;
public FilePicker(String file, String key, String section, int titleId, int descriptionId, public FilePicker(String file, String section, String key, int titleId, int descriptionId,
String defaultVault, int requestType, Setting setting) String defaultVault, int requestType)
{ {
super(key, section, setting, titleId, descriptionId); super(file, section, key, titleId, descriptionId);
mFile = file;
mDefaultValue = defaultVault; mDefaultValue = defaultVault;
mRequestType = requestType; mRequestType = requestType;
} }
public String getFile() public String getSelectedValue(Settings settings)
{ {
return mFile + ".ini"; return settings.getSection(getFile(), getSection()).getString(getKey(), mDefaultValue);
}
public String getSelectedValue()
{
if (getSetting() == null || !(getSetting() instanceof StringSetting))
{
return mDefaultValue;
}
else
{
StringSetting setting = (StringSetting) getSetting();
return setting.getValue();
}
} }
public int getRequestType() public int getRequestType()

View File

@ -0,0 +1,26 @@
package org.dolphinemu.dolphinemu.features.settings.model.view;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
public class FloatSliderSetting extends SliderSetting
{
protected float mDefaultValue;
public FloatSliderSetting(String file, String section, String key, int titleId, int descriptionId,
int max, String units, float defaultValue)
{
super(file, section, key, titleId, descriptionId, max, units);
mDefaultValue = defaultValue;
}
public int getSelectedValue(Settings settings)
{
float value = settings.getSection(getFile(), getSection()).getFloat(getKey(), mDefaultValue);
return Math.round(value);
}
public void setSelectedValue(Settings settings, float selection)
{
settings.getSection(getFile(), getSection()).setFloat(getKey(), selection);
}
}

View File

@ -1,12 +1,10 @@
package org.dolphinemu.dolphinemu.features.settings.model.view; package org.dolphinemu.dolphinemu.features.settings.model.view;
import org.dolphinemu.dolphinemu.features.settings.model.Setting;
public final class HeaderSetting extends SettingsItem public final class HeaderSetting extends SettingsItem
{ {
public HeaderSetting(String key, Setting setting, int titleId, int descriptionId) public HeaderSetting(String key, int titleId, int descriptionId)
{ {
super(key, null, setting, titleId, descriptionId); super(null, null, key, titleId, descriptionId);
} }
@Override @Override

View File

@ -6,29 +6,21 @@ import android.view.InputDevice;
import android.view.KeyEvent; import android.view.KeyEvent;
import org.dolphinemu.dolphinemu.DolphinApplication; import org.dolphinemu.dolphinemu.DolphinApplication;
import org.dolphinemu.dolphinemu.features.settings.model.Setting; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
public class InputBindingSetting extends SettingsItem public class InputBindingSetting extends SettingsItem
{ {
private String gameId; private String gameId;
public InputBindingSetting(String key, String section, int titleId, Setting setting, public InputBindingSetting(String file, String section, String key, int titleId, String gameId)
String gameId)
{ {
super(key, section, setting, titleId, 0); super(file, section, key, titleId, 0);
this.gameId = gameId; this.gameId = gameId;
} }
public String getValue() public String getValue(Settings settings)
{ {
if (getSetting() == null || !(getSetting() instanceof StringSetting)) return settings.getSection(getFile(), getSection()).getString(getKey(), "");
{
return "";
}
StringSetting setting = (StringSetting) getSetting();
return setting.getValue();
} }
/** /**
@ -37,12 +29,12 @@ public class InputBindingSetting extends SettingsItem
* *
* @param keyEvent KeyEvent of this key press. * @param keyEvent KeyEvent of this key press.
*/ */
public void onKeyInput(KeyEvent keyEvent) public void onKeyInput(Settings settings, KeyEvent keyEvent)
{ {
InputDevice device = keyEvent.getDevice(); InputDevice device = keyEvent.getDevice();
String bindStr = "Device '" + device.getDescriptor() + "'-Button " + keyEvent.getKeyCode(); String bindStr = "Device '" + device.getDescriptor() + "'-Button " + keyEvent.getKeyCode();
String uiString = device.getName() + ": Button " + keyEvent.getKeyCode(); String uiString = device.getName() + ": Button " + keyEvent.getKeyCode();
setValue(bindStr, uiString); setValue(settings, bindStr, uiString);
} }
/** /**
@ -53,23 +45,16 @@ public class InputBindingSetting extends SettingsItem
* @param motionRange MotionRange of the movement * @param motionRange MotionRange of the movement
* @param axisDir Either '-' or '+' * @param axisDir Either '-' or '+'
*/ */
public void onMotionInput(InputDevice device, InputDevice.MotionRange motionRange, public void onMotionInput(Settings settings, InputDevice device,
char axisDir) InputDevice.MotionRange motionRange, char axisDir)
{ {
String bindStr = String bindStr =
"Device '" + device.getDescriptor() + "'-Axis " + motionRange.getAxis() + axisDir; "Device '" + device.getDescriptor() + "'-Axis " + motionRange.getAxis() + axisDir;
String uiString = device.getName() + ": Axis " + motionRange.getAxis() + axisDir; String uiString = device.getName() + ": Axis " + motionRange.getAxis() + axisDir;
setValue(bindStr, uiString); setValue(settings, bindStr, uiString);
} }
/** public void setValue(Settings settings, String bind, String ui)
* Write a value to the backing string. If that string was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param bind The input that will be bound
* @return null if overwritten successfully; otherwise, a newly created StringSetting.
*/
public StringSetting setValue(String bind, String ui)
{ {
SharedPreferences SharedPreferences
preferences = preferences =
@ -78,23 +63,12 @@ public class InputBindingSetting extends SettingsItem
editor.putString(getKey() + gameId, ui); editor.putString(getKey() + gameId, ui);
editor.apply(); editor.apply();
if (getSetting() == null || !(getSetting() instanceof StringSetting)) settings.getSection(getFile(), getSection()).setString(getKey(), bind);
{
StringSetting setting = new StringSetting(getKey(), getSection(), bind);
setSetting(setting);
return setting;
}
else
{
StringSetting setting = (StringSetting) getSetting();
setting.setValue(bind);
return null;
}
} }
public void clearValue() public void clearValue(Settings settings)
{ {
setValue("", ""); setValue(settings, "", "");
} }
@Override @Override

View File

@ -0,0 +1,25 @@
package org.dolphinemu.dolphinemu.features.settings.model.view;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
public final class IntSliderSetting extends SliderSetting
{
private int mDefaultValue;
public IntSliderSetting(String file, String section, String key, int titleId, int descriptionId,
int max, String units, int defaultValue)
{
super(file, section, key, titleId, descriptionId, max, units);
mDefaultValue = defaultValue;
}
public int getSelectedValue(Settings settings)
{
return settings.getSection(getFile(), getSection()).getInt(getKey(), mDefaultValue);
}
public void setSelectedValue(Settings settings, int selection)
{
settings.getSection(getFile(), getSection()).setInt(getKey(), selection);
}
}

View File

@ -0,0 +1,25 @@
package org.dolphinemu.dolphinemu.features.settings.model.view;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
public final class InvertedCheckBoxSetting extends CheckBoxSetting
{
public InvertedCheckBoxSetting(String file, String section, String key, int titleId,
int descriptionId, boolean defaultValue)
{
super(file, section, key, titleId, descriptionId, !defaultValue);
}
@Override
public boolean isChecked(Settings settings)
{
return !settings.getSection(getFile(), getSection()).getBoolean(getKey(), mDefaultValue);
}
@Override
public void setChecked(Settings settings, boolean checked)
{
settings.getSection(getFile(), getSection()).setBoolean(getKey(), !checked);
}
}

View File

@ -0,0 +1,25 @@
package org.dolphinemu.dolphinemu.features.settings.model.view;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
public final class PercentSliderSetting extends FloatSliderSetting
{
public PercentSliderSetting(String file, String section, String key, int titleId,
int descriptionId, int max, String units, float defaultValue)
{
super(file, section, key, titleId, descriptionId, max, units, defaultValue / 100);
}
@Override
public int getSelectedValue(Settings settings)
{
float value = settings.getSection(getFile(), getSection()).getFloat(getKey(), mDefaultValue);
return Math.round(value * 100);
}
@Override
public void setSelectedValue(Settings settings, float selection)
{
settings.getSection(getFile(), getSection()).setFloat(getKey(), selection / 100);
}
}

View File

@ -6,62 +6,46 @@ import android.view.KeyEvent;
import org.dolphinemu.dolphinemu.DolphinApplication; import org.dolphinemu.dolphinemu.DolphinApplication;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.settings.model.Setting; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
import org.dolphinemu.dolphinemu.utils.Rumble; import org.dolphinemu.dolphinemu.utils.Rumble;
public class RumbleBindingSetting extends InputBindingSetting public class RumbleBindingSetting extends InputBindingSetting
{ {
public RumbleBindingSetting(String file, String section, String key, int titleId, String gameId)
public RumbleBindingSetting(String key, String section, int titleId, Setting setting,
String gameId)
{ {
super(key, section, titleId, setting, gameId); super(file, section, key, titleId, gameId);
}
@Override
public String getValue()
{
if (getSetting() == null || !(getSetting() instanceof StringSetting))
{
return "";
}
StringSetting setting = (StringSetting) getSetting();
return setting.getValue();
} }
/** /**
* Just need the device when saving rumble. * Just need the device when saving rumble.
*/ */
@Override @Override
public void onKeyInput(KeyEvent keyEvent) public void onKeyInput(Settings settings, KeyEvent keyEvent)
{ {
saveRumble(keyEvent.getDevice()); saveRumble(settings, keyEvent.getDevice());
} }
/** /**
* Just need the device when saving rumble. * Just need the device when saving rumble.
*/ */
@Override @Override
public void onMotionInput(InputDevice device, public void onMotionInput(Settings settings, InputDevice device,
InputDevice.MotionRange motionRange, InputDevice.MotionRange motionRange, char axisDir)
char axisDir)
{ {
saveRumble(device); saveRumble(settings, device);
} }
private void saveRumble(InputDevice device) private void saveRumble(Settings settings, InputDevice device)
{ {
Vibrator vibrator = device.getVibrator(); Vibrator vibrator = device.getVibrator();
if (vibrator != null && vibrator.hasVibrator()) if (vibrator != null && vibrator.hasVibrator())
{ {
setValue(device.getDescriptor(), device.getName()); setValue(settings, device.getDescriptor(), device.getName());
Rumble.doRumble(vibrator); Rumble.doRumble(vibrator);
} }
else else
{ {
setValue("", setValue(settings, "",
DolphinApplication.getAppContext().getString(R.string.rumble_not_found)); DolphinApplication.getAppContext().getString(R.string.rumble_not_found));
} }
} }

View File

@ -1,14 +1,11 @@
package org.dolphinemu.dolphinemu.features.settings.model.view; package org.dolphinemu.dolphinemu.features.settings.model.view;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; 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. * ViewModel abstraction for an Item in the RecyclerView powering SettingsFragments.
* Each one corresponds to a {@link Setting} object, so this class's subclasses * Most of them correspond to a single line in an INI file, but there are a few with multiple
* should vaguely correspond to those subclasses. There are a few with multiple analogues * analogues and a few with none (Headers, for example, do not correspond to anything on disk.)
* and a few with none (Headers, for example, do not correspond to anything in the ini
* file.)
*/ */
public abstract class SettingsItem public abstract class SettingsItem
{ {
@ -24,36 +21,50 @@ public abstract class SettingsItem
public static final int TYPE_FILE_PICKER = 9; public static final int TYPE_FILE_PICKER = 9;
public static final int TYPE_CONFIRM_RUNNABLE = 10; public static final int TYPE_CONFIRM_RUNNABLE = 10;
private String mKey; private String mFile;
private String mSection; private String mSection;
private String mKey;
private Setting mSetting;
private int mNameId; private int mNameId;
private int mDescriptionId; private int mDescriptionId;
/** /**
* Base constructor. Takes a key / section name in case the third parameter, the Setting, * Base constructor.
* is null; in which case, one can be constructed and saved using the key / section.
* *
* @param key Identifier for the Setting represented by this Item. * @param file File to which the Setting belongs.
* @param section Section to which the Setting belongs. * @param section Section to which the Setting belongs.
* @param setting A possibly-null backing Setting, to be modified on UI events. * @param key Identifier for the Setting represented by this Item.
* @param nameId Resource ID for a text string to be displayed as this setting's name. * @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. * @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) public SettingsItem(String file, String section, String key, int nameId, int descriptionId)
{ {
mKey = key; mFile = file;
mSection = section; mSection = section;
mSetting = setting; mKey = key;
mNameId = nameId; mNameId = nameId;
mDescriptionId = descriptionId; mDescriptionId = descriptionId;
} }
/** /**
* @return The identifier for the backing Setting. * @return The file in which the backing setting belongs.
*/
public String getFile()
{
return mFile;
}
/**
* @return The header under which the backing setting belongs.
*/
public String getSection()
{
return mSection;
}
/**
* @return The identifier for the backing setting.
*/ */
public String getKey() public String getKey()
{ {
@ -61,35 +72,7 @@ public abstract class SettingsItem
} }
/** /**
* @return The header under which the backing Setting belongs. * @return A resource ID for a text string representing this setting's name.
*/
public String getSection()
{
return mSection;
}
/**
* @return The backing Setting, possibly null.
*/
public Setting getSetting()
{
return mSetting;
}
/**
* Replace the backing setting with a new one. Generally used in cases where
* the backing setting is null.
*
* @param setting A non-null Setting.
*/
public void setSetting(Setting setting)
{
mSetting = setting;
}
/**
* @return A resource ID for a text string representing this Setting's name.
*/ */
public int getNameId() public int getNameId()
{ {

View File

@ -1,7 +1,6 @@
package org.dolphinemu.dolphinemu.features.settings.model.view; package org.dolphinemu.dolphinemu.features.settings.model.view;
import org.dolphinemu.dolphinemu.features.settings.model.IntSetting; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.Setting;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
public final class SingleChoiceSetting extends SettingsItem public final class SingleChoiceSetting extends SettingsItem
@ -12,20 +11,20 @@ public final class SingleChoiceSetting extends SettingsItem
private int mValuesId; private int mValuesId;
private MenuTag menuTag; private MenuTag menuTag;
public SingleChoiceSetting(String key, String section, int titleId, int descriptionId, public SingleChoiceSetting(String file, String section, String key, int titleId,
int choicesId, int valuesId, int defaultValue, Setting setting, MenuTag menuTag) int descriptionId, int choicesId, int valuesId, int defaultValue, MenuTag menuTag)
{ {
super(key, section, setting, titleId, descriptionId); super(file, section, key, titleId, descriptionId);
mValuesId = valuesId; mValuesId = valuesId;
mChoicesId = choicesId; mChoicesId = choicesId;
mDefaultValue = defaultValue; mDefaultValue = defaultValue;
this.menuTag = menuTag; this.menuTag = menuTag;
} }
public SingleChoiceSetting(String key, String section, int titleId, int descriptionId, public SingleChoiceSetting(String file, String section, String key, int titleId,
int choicesId, int valuesId, int defaultValue, Setting setting) int descriptionId, int choicesId, int valuesId, int defaultValue)
{ {
this(key, section, titleId, descriptionId, choicesId, valuesId, defaultValue, setting, null); this(file, section, key, titleId, descriptionId, choicesId, valuesId, defaultValue, null);
} }
public int getChoicesId() public int getChoicesId()
@ -38,17 +37,9 @@ public final class SingleChoiceSetting extends SettingsItem
return mValuesId; return mValuesId;
} }
public int getSelectedValue() public int getSelectedValue(Settings settings)
{ {
if (getSetting() == null || !(getSetting() instanceof IntSetting)) return settings.getSection(getFile(), getSection()).getInt(getKey(), mDefaultValue);
{
return mDefaultValue;
}
else
{
IntSetting setting = (IntSetting) getSetting();
return setting.getValue();
}
} }
public MenuTag getMenuTag() public MenuTag getMenuTag()
@ -56,27 +47,9 @@ public final class SingleChoiceSetting extends SettingsItem
return menuTag; return menuTag;
} }
/** public void setSelectedValue(Settings settings, int selection)
* Write a value to the backing int. If that int was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param selection New value of the int.
* @return null if overwritten successfully otherwise; a newly created IntSetting.
*/
public IntSetting setSelectedValue(int selection)
{ {
if (getSetting() == null || !(getSetting() instanceof IntSetting)) settings.getSection(getFile(), getSection()).setInt(getKey(), selection);
{
IntSetting setting = new IntSetting(getKey(), getSection(), selection);
setSetting(setting);
return setting;
}
else
{
IntSetting setting = (IntSetting) getSetting();
setting.setValue(selection);
return null;
}
} }
@Override @Override

View File

@ -1,7 +1,6 @@
package org.dolphinemu.dolphinemu.features.settings.model.view; package org.dolphinemu.dolphinemu.features.settings.model.view;
import org.dolphinemu.dolphinemu.features.settings.model.IntSetting; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.Setting;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
public final class SingleChoiceSettingDynamicDescriptions extends SettingsItem public final class SingleChoiceSettingDynamicDescriptions extends SettingsItem
@ -14,12 +13,11 @@ public final class SingleChoiceSettingDynamicDescriptions extends SettingsItem
private int mDescriptionValuesId; private int mDescriptionValuesId;
private MenuTag menuTag; private MenuTag menuTag;
public SingleChoiceSettingDynamicDescriptions(String key, String section, int titleId, public SingleChoiceSettingDynamicDescriptions(String file, String section, String key,
int descriptionId, int titleId, int descriptionId, int choicesId, int valuesId, int descriptionChoicesId,
int choicesId, int valuesId, int descriptionChoicesId, int descriptionValuesId, int descriptionValuesId, int defaultValue, MenuTag menuTag)
int defaultValue, Setting setting, MenuTag menuTag)
{ {
super(key, section, setting, titleId, descriptionId); super(file, section, key, titleId, descriptionId);
mValuesId = valuesId; mValuesId = valuesId;
mChoicesId = choicesId; mChoicesId = choicesId;
mDescriptionChoicesId = descriptionChoicesId; mDescriptionChoicesId = descriptionChoicesId;
@ -28,13 +26,12 @@ public final class SingleChoiceSettingDynamicDescriptions extends SettingsItem
this.menuTag = menuTag; this.menuTag = menuTag;
} }
public SingleChoiceSettingDynamicDescriptions(String key, String section, int titleId, public SingleChoiceSettingDynamicDescriptions(String file, String section, String key,
int descriptionId, int titleId, int descriptionId, int choicesId, int valuesId, int descriptionChoicesId,
int choicesId, int valuesId, int descriptionChoicesId, int descriptionValuesId, int descriptionValuesId, int defaultValue)
int defaultValue, Setting setting)
{ {
this(key, section, titleId, descriptionId, choicesId, valuesId, descriptionChoicesId, this(file, section, key, titleId, descriptionId, choicesId, valuesId, descriptionChoicesId,
descriptionValuesId, defaultValue, setting, null); descriptionValuesId, defaultValue, null);
} }
public int getChoicesId() public int getChoicesId()
@ -57,17 +54,9 @@ public final class SingleChoiceSettingDynamicDescriptions extends SettingsItem
return mDescriptionValuesId; return mDescriptionValuesId;
} }
public int getSelectedValue() public int getSelectedValue(Settings settings)
{ {
if (getSetting() == null || !(getSetting() instanceof IntSetting)) return settings.getSection(getFile(), getSection()).getInt(getKey(), mDefaultValue);
{
return mDefaultValue;
}
else
{
IntSetting setting = (IntSetting) getSetting();
return setting.getValue();
}
} }
public MenuTag getMenuTag() public MenuTag getMenuTag()
@ -75,27 +64,9 @@ public final class SingleChoiceSettingDynamicDescriptions extends SettingsItem
return menuTag; return menuTag;
} }
/** public void setSelectedValue(Settings settings, int selection)
* Write a value to the backing int. If that int was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param selection New value of the int.
* @return null if overwritten successfully otherwise; a newly created IntSetting.
*/
public IntSetting setSelectedValue(int selection)
{ {
if (getSetting() == null || !(getSetting() instanceof IntSetting)) settings.getSection(getFile(), getSection()).setInt(getKey(), selection);
{
IntSetting setting = new IntSetting(getKey(), getSection(), selection);
setSetting(setting);
return setting;
}
else
{
IntSetting setting = (IntSetting) getSetting();
setting.setValue(selection);
return null;
}
} }
@Override @Override

View File

@ -1,117 +1,28 @@
package org.dolphinemu.dolphinemu.features.settings.model.view; package org.dolphinemu.dolphinemu.features.settings.model.view;
import org.dolphinemu.dolphinemu.features.settings.model.FloatSetting; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.IntSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Setting;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
import org.dolphinemu.dolphinemu.utils.Log;
public final class SliderSetting extends SettingsItem public abstract class SliderSetting extends SettingsItem
{ {
private int mMax; private int mMax;
private int mDefaultValue;
private String mUnits; private String mUnits;
public SliderSetting(String key, String section, int titleId, int descriptionId, int max, public SliderSetting(String file, String section, String key, int nameId, int descriptionId,
String units, int defaultValue, Setting setting) int max, String units)
{ {
super(key, section, setting, titleId, descriptionId); super(file, section, key, nameId, descriptionId);
mMax = max; mMax = max;
mUnits = units; mUnits = units;
mDefaultValue = defaultValue;
} }
public abstract int getSelectedValue(Settings settings);
public int getMax() public int getMax()
{ {
return mMax; return mMax;
} }
public int getSelectedValue()
{
Setting setting = getSetting();
if (setting == null)
{
return mDefaultValue;
}
if (setting instanceof IntSetting)
{
IntSetting intSetting = (IntSetting) setting;
return intSetting.getValue();
}
else if (setting instanceof FloatSetting)
{
FloatSetting floatSetting = (FloatSetting) setting;
if (isPercentSetting())
{
return Math.round(floatSetting.getValue() * 100);
}
else
{
return Math.round(floatSetting.getValue());
}
}
else
{
Log.error("[SliderSetting] Error casting setting type.");
return -1;
}
}
public boolean isPercentSetting()
{
return getKey().equals(SettingsFile.KEY_OVERCLOCK_PERCENT)
|| getKey().equals(SettingsFile.KEY_SPEED_LIMIT);
}
/**
* Write a value to the backing int. If that int was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param selection New value of the int.
* @return null if overwritten successfully otherwise; a newly created IntSetting.
*/
public IntSetting setSelectedValue(int selection)
{
if (getSetting() == null || !(getSetting() instanceof IntSetting))
{
IntSetting setting = new IntSetting(getKey(), getSection(), selection);
setSetting(setting);
return setting;
}
else
{
IntSetting setting = (IntSetting) getSetting();
setting.setValue(selection);
return null;
}
}
/**
* Write a value to the backing float. If that float was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param selection New value of the float.
* @return null if overwritten successfully otherwise; a newly created FloatSetting.
*/
public FloatSetting setSelectedValue(float selection)
{
if (getSetting() == null || !(getSetting() instanceof FloatSetting))
{
FloatSetting setting = new FloatSetting(getKey(), getSection(), selection);
setSetting(setting);
return setting;
}
else
{
FloatSetting setting = (FloatSetting) getSetting();
setting.setValue(selection);
return null;
}
}
public String getUnits() public String getUnits()
{ {
return mUnits; return mUnits;

View File

@ -1,8 +1,7 @@
package org.dolphinemu.dolphinemu.features.settings.model.view; package org.dolphinemu.dolphinemu.features.settings.model.view;
import org.dolphinemu.dolphinemu.DolphinApplication; import org.dolphinemu.dolphinemu.DolphinApplication;
import org.dolphinemu.dolphinemu.features.settings.model.Setting; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
public class StringSingleChoiceSetting extends SettingsItem public class StringSingleChoiceSetting extends SettingsItem
@ -13,37 +12,37 @@ public class StringSingleChoiceSetting extends SettingsItem
private String[] mValuesId; private String[] mValuesId;
private MenuTag mMenuTag; private MenuTag mMenuTag;
public StringSingleChoiceSetting(String key, String section, int titleId, int descriptionId, public StringSingleChoiceSetting(String file, String section, String key, int titleId,
String[] choicesId, String[] valuesId, String defaultValue, Setting setting, int descriptionId, String[] choicesId, String[] valuesId, String defaultValue,
MenuTag menuTag) MenuTag menuTag)
{ {
super(key, section, setting, titleId, descriptionId); super(file, section, key, titleId, descriptionId);
mChoicesId = choicesId; mChoicesId = choicesId;
mValuesId = valuesId; mValuesId = valuesId;
mDefaultValue = defaultValue; mDefaultValue = defaultValue;
mMenuTag = menuTag; mMenuTag = menuTag;
} }
public StringSingleChoiceSetting(String key, String section, int titleId, int descriptionId, public StringSingleChoiceSetting(String file, String section, String key, int titleId,
String[] choicesId, String[] valuesId, String defaultValue, Setting setting) int descriptionId, String[] choicesId, String[] valuesId, String defaultValue)
{ {
this(key, section, titleId, descriptionId, choicesId, valuesId, defaultValue, setting, null); this(file, section, key, titleId, descriptionId, choicesId, valuesId, defaultValue, null);
} }
public StringSingleChoiceSetting(String key, String section, int titleId, int descriptionId, public StringSingleChoiceSetting(String file, String section, String key, int titleId,
int choicesId, int valuesId, String defaultValue, Setting setting, MenuTag menuTag) int descriptionId, int choicesId, int valuesId, String defaultValue, MenuTag menuTag)
{ {
super(key, section, setting, titleId, descriptionId); super(file, section, key, titleId, descriptionId);
mChoicesId = DolphinApplication.getAppContext().getResources().getStringArray(choicesId); mChoicesId = DolphinApplication.getAppContext().getResources().getStringArray(choicesId);
mValuesId = DolphinApplication.getAppContext().getResources().getStringArray(valuesId); mValuesId = DolphinApplication.getAppContext().getResources().getStringArray(valuesId);
mDefaultValue = defaultValue; mDefaultValue = defaultValue;
mMenuTag = menuTag; mMenuTag = menuTag;
} }
public StringSingleChoiceSetting(String key, String section, int titleId, int descriptionId, public StringSingleChoiceSetting(String file, String section, String key, int titleId,
int choicesId, int valuesId, String defaultValue, Setting setting) int descriptionId, int choicesId, int valuesId, String defaultValue)
{ {
this(key, section, titleId, descriptionId, choicesId, valuesId, defaultValue, setting, null); this(file, section, key, titleId, descriptionId, choicesId, valuesId, defaultValue, null);
} }
public String[] getChoicesId() public String[] getChoicesId()
@ -69,22 +68,14 @@ public class StringSingleChoiceSetting extends SettingsItem
return ""; return "";
} }
public String getSelectedValue() public String getSelectedValue(Settings settings)
{ {
if (getSetting() == null || !(getSetting() instanceof StringSetting)) return settings.getSection(getFile(), getSection()).getString(getKey(), mDefaultValue);
{
return mDefaultValue;
}
else
{
StringSetting setting = (StringSetting) getSetting();
return setting.getValue();
}
} }
public int getSelectValueIndex() public int getSelectValueIndex(Settings settings)
{ {
String selectedValue = getSelectedValue(); String selectedValue = getSelectedValue(settings);
for (int i = 0; i < mValuesId.length; i++) for (int i = 0; i < mValuesId.length; i++)
{ {
if (mValuesId[i].equals(selectedValue)) if (mValuesId[i].equals(selectedValue))
@ -101,27 +92,9 @@ public class StringSingleChoiceSetting extends SettingsItem
return mMenuTag; return mMenuTag;
} }
/** public void setSelectedValue(Settings settings, String selection)
* Write a value to the backing int. If that int was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param selection New value of the int.
* @return null if overwritten successfully otherwise; a newly created IntSetting.
*/
public StringSetting setSelectedValue(String selection)
{ {
if (getSetting() == null || !(getSetting() instanceof StringSetting)) settings.getSection(getFile(), getSection()).setString(getKey(), selection);
{
StringSetting setting = new StringSetting(getKey(), getSection(), selection);
setSetting(setting);
return setting;
}
else
{
StringSetting setting = (StringSetting) getSetting();
setting.setValue(selection);
return null;
}
} }
@Override @Override

View File

@ -1,15 +1,14 @@
package org.dolphinemu.dolphinemu.features.settings.model.view; package org.dolphinemu.dolphinemu.features.settings.model.view;
import org.dolphinemu.dolphinemu.features.settings.model.Setting;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
public final class SubmenuSetting extends SettingsItem public final class SubmenuSetting extends SettingsItem
{ {
private MenuTag mMenuKey; private MenuTag mMenuKey;
public SubmenuSetting(String key, Setting setting, int titleId, MenuTag menuKey) public SubmenuSetting(String key, int titleId, MenuTag menuKey)
{ {
super(key, null, setting, titleId, 0); super(null, null, key, titleId, 0);
mMenuKey = menuKey; mMenuKey = menuKey;
} }

View File

@ -15,14 +15,12 @@ import android.widget.TextView;
import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.dialogs.MotionAlertDialog; import org.dolphinemu.dolphinemu.dialogs.MotionAlertDialog;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.model.FloatSetting;
import org.dolphinemu.dolphinemu.features.settings.model.IntSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.CheckBoxSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.CheckBoxSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker; import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker;
import org.dolphinemu.dolphinemu.features.settings.model.view.FloatSliderSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.IntSliderSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.RumbleBindingSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.RumbleBindingSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting;
@ -43,7 +41,9 @@ import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SubmenuViewHold
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
import org.dolphinemu.dolphinemu.ui.main.MainPresenter; import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
import org.dolphinemu.dolphinemu.utils.IniFile;
import java.io.File;
import java.security.InvalidParameterException; import java.security.InvalidParameterException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
@ -151,6 +151,11 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
return getItem(position).getType(); return getItem(position).getType();
} }
public Settings getSettings()
{
return mView.getSettings();
}
public void setSettings(ArrayList<SettingsItem> settings) public void setSettings(ArrayList<SettingsItem> settings)
{ {
mSettings = settings; mSettings = settings;
@ -159,20 +164,9 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
public void onBooleanClick(CheckBoxSetting item, int position, boolean checked) public void onBooleanClick(CheckBoxSetting item, int position, boolean checked)
{ {
BooleanSetting setting = item.setChecked(checked); item.setChecked(getSettings(), checked);
notifyItemChanged(position); notifyItemChanged(position);
if (setting != null)
{
mView.putSetting(setting);
}
if (item.getKey().equals(SettingsFile.KEY_SKIP_EFB) ||
item.getKey().equals(SettingsFile.KEY_IGNORE_FORMAT))
{
mView.putSetting(new BooleanSetting(item.getKey(), item.getSection(), !checked));
}
mView.onSettingChanged(item.getKey()); mView.onSettingChanged(item.getKey());
} }
@ -201,7 +195,8 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
R.style.DolphinDialogBase); R.style.DolphinDialogBase);
builder.setTitle(item.getNameId()); builder.setTitle(item.getNameId());
builder.setSingleChoiceItems(item.getChoicesId(), item.getSelectValueIndex(), this); builder.setSingleChoiceItems(item.getChoicesId(), item.getSelectValueIndex(getSettings()),
this);
mDialog = builder.show(); mDialog = builder.show();
} }
@ -227,7 +222,7 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
{ {
mClickedItem = item; mClickedItem = item;
mClickedPosition = position; mClickedPosition = position;
mSeekbarProgress = item.getSelectedValue(); mSeekbarProgress = item.getSelectedValue(getSettings());
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity(), AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity(),
R.style.DolphinDialogBase); R.style.DolphinDialogBase);
@ -261,7 +256,7 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
public void onInputBindingClick(final InputBindingSetting item, final int position) public void onInputBindingClick(final InputBindingSetting item, final int position)
{ {
final MotionAlertDialog dialog = new MotionAlertDialog(mContext, item); final MotionAlertDialog dialog = new MotionAlertDialog(mContext, item, this);
dialog.setTitle(R.string.input_binding); dialog.setTitle(R.string.input_binding);
dialog.setMessage(String.format(mContext.getString( dialog.setMessage(String.format(mContext.getString(
item instanceof RumbleBindingSetting ? item instanceof RumbleBindingSetting ?
@ -269,14 +264,10 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
mContext.getString(item.getNameId()))); mContext.getString(item.getNameId())));
dialog.setButton(AlertDialog.BUTTON_NEGATIVE, mContext.getString(R.string.cancel), this); dialog.setButton(AlertDialog.BUTTON_NEGATIVE, mContext.getString(R.string.cancel), this);
dialog.setButton(AlertDialog.BUTTON_NEUTRAL, mContext.getString(R.string.clear), dialog.setButton(AlertDialog.BUTTON_NEUTRAL, mContext.getString(R.string.clear),
(dialogInterface, i) -> item.clearValue()); (dialogInterface, i) -> item.clearValue(getSettings()));
dialog.setOnDismissListener(dialog1 -> dialog.setOnDismissListener(dialog1 ->
{ {
StringSetting setting = new StringSetting(item.getKey(), item.getSection(), item.getValue());
notifyItemChanged(position); notifyItemChanged(position);
mView.putSetting(setting);
mView.onSettingChanged(item.getKey()); mView.onSettingChanged(item.getKey());
}); });
dialog.setCanceledOnTouchOutside(false); dialog.setCanceledOnTouchOutside(false);
@ -315,12 +306,15 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
extensions); extensions);
} }
public void onFilePickerConfirmation(String file) public void onFilePickerConfirmation(String selectedFile)
{ {
FilePicker filePicker = (FilePicker) mClickedItem; FilePicker filePicker = (FilePicker) mClickedItem;
NativeLibrary.SetConfig(filePicker.getFile(), filePicker.getSection(), filePicker.getKey(), File file = SettingsFile.getSettingsFile(filePicker.getFile());
file); IniFile ini = new IniFile(file);
ini.setString(filePicker.getSection(), filePicker.getKey(), selectedFile);
ini.save(file);
NativeLibrary.ReloadConfig(); NativeLibrary.ReloadConfig();
mClickedItem = null; mClickedItem = null;
@ -328,40 +322,31 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
public void resetPaths() public void resetPaths()
{ {
StringSetting defaultISO = IniFile.Section coreSection = mView.getSettings().getSection(SettingsFile.FILE_NAME_DOLPHIN,
new StringSetting(SettingsFile.KEY_DEFAULT_ISO, Settings.SECTION_INI_CORE, ""); Settings.SECTION_INI_CORE);
StringSetting NANDRootPath = IniFile.Section generalSection = mView.getSettings().getSection(SettingsFile.FILE_NAME_DOLPHIN,
new StringSetting(SettingsFile.KEY_NAND_ROOT_PATH, Settings.SECTION_INI_GENERAL, Settings.SECTION_INI_GENERAL);
SettingsFragmentPresenter.getDefaultNANDRootPath());
StringSetting dumpPath =
new StringSetting(SettingsFile.KEY_DUMP_PATH, Settings.SECTION_INI_GENERAL,
SettingsFragmentPresenter.getDefaultDumpPath());
StringSetting loadPath =
new StringSetting(SettingsFile.KEY_LOAD_PATH, Settings.SECTION_INI_GENERAL,
SettingsFragmentPresenter.getDefaultLoadPath());
StringSetting resourcePackPath =
new StringSetting(SettingsFile.KEY_RESOURCE_PACK_PATH, Settings.SECTION_INI_GENERAL,
SettingsFragmentPresenter.getDefaultResourcePackPath());
StringSetting sdPath =
new StringSetting(SettingsFile.KEY_WII_SD_CARD_PATH, Settings.SECTION_INI_GENERAL,
SettingsFragmentPresenter.getDefaultSDPath());
mView.putSetting(defaultISO); coreSection.delete(SettingsFile.KEY_DEFAULT_ISO);
mView.putSetting(NANDRootPath); generalSection.delete(SettingsFile.KEY_NAND_ROOT_PATH);
mView.putSetting(dumpPath); generalSection.delete(SettingsFile.KEY_DUMP_PATH);
mView.putSetting(loadPath); generalSection.delete(SettingsFile.KEY_LOAD_PATH);
mView.putSetting(resourcePackPath); generalSection.delete(SettingsFile.KEY_RESOURCE_PACK_PATH);
mView.putSetting(sdPath); generalSection.delete(SettingsFile.KEY_WII_SD_CARD_PATH);
mView.onSettingChanged(null); mView.onSettingChanged(null);
} }
public void setAllLogTypes(String value) public void setAllLogTypes(boolean value)
{ {
IniFile.Section section = mView.getSettings().getSection(SettingsFile.FILE_NAME_LOGGER,
Settings.SECTION_LOGGER_LOGS);
for (Map.Entry<String, String> entry : SettingsFragmentPresenter.LOG_TYPE_NAMES.entrySet()) for (Map.Entry<String, String> entry : SettingsFragmentPresenter.LOG_TYPE_NAMES.entrySet())
{ {
mView.putSetting(new StringSetting(entry.getKey(), Settings.SECTION_LOGGER_LOGS, value)); section.setBoolean(entry.getKey(), value);
} }
mView.onSettingChanged(null); mView.onSettingChanged(null);
} }
@ -394,17 +379,12 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
SingleChoiceSetting scSetting = (SingleChoiceSetting) mClickedItem; SingleChoiceSetting scSetting = (SingleChoiceSetting) mClickedItem;
int value = getValueForSingleChoiceSelection(scSetting, which); int value = getValueForSingleChoiceSelection(scSetting, which);
if (scSetting.getSelectedValue() != value) if (scSetting.getSelectedValue(getSettings()) != value)
mView.onSettingChanged(mClickedItem.getKey()); mView.onSettingChanged(mClickedItem.getKey());
handleMenuTag(scSetting.getMenuTag(), value); handleMenuTag(scSetting.getMenuTag(), value);
// Get the backing Setting, which may be null (if for example it was missing from the file) scSetting.setSelectedValue(getSettings(), value);
IntSetting setting = scSetting.setSelectedValue(value);
if (setting != null)
{
mView.putSetting(setting);
}
closeDialog(); closeDialog();
} }
@ -414,15 +394,10 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
(SingleChoiceSettingDynamicDescriptions) mClickedItem; (SingleChoiceSettingDynamicDescriptions) mClickedItem;
int value = getValueForSingleChoiceDynamicDescriptionsSelection(scSetting, which); int value = getValueForSingleChoiceDynamicDescriptionsSelection(scSetting, which);
if (scSetting.getSelectedValue() != value) if (scSetting.getSelectedValue(getSettings()) != value)
mView.onSettingChanged(mClickedItem.getKey()); mView.onSettingChanged(mClickedItem.getKey());
// Get the backing Setting, which may be null (if for example it was missing from the file) scSetting.setSelectedValue(getSettings(), value);
IntSetting setting = scSetting.setSelectedValue(value);
if (setting != null)
{
mView.putSetting(setting);
}
closeDialog(); closeDialog();
} }
@ -430,52 +405,32 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
{ {
StringSingleChoiceSetting scSetting = (StringSingleChoiceSetting) mClickedItem; StringSingleChoiceSetting scSetting = (StringSingleChoiceSetting) mClickedItem;
String value = scSetting.getValueAt(which); String value = scSetting.getValueAt(which);
if (!scSetting.getSelectedValue().equals(value)) if (!scSetting.getSelectedValue(getSettings()).equals(value))
mView.onSettingChanged(mClickedItem.getKey()); mView.onSettingChanged(mClickedItem.getKey());
handleMenuTag(scSetting.getMenuTag(), which); handleMenuTag(scSetting.getMenuTag(), which);
StringSetting setting = scSetting.setSelectedValue(value); scSetting.setSelectedValue(getSettings(), value);
if (setting != null)
{
mView.putSetting(setting);
}
closeDialog(); closeDialog();
} }
else if (mClickedItem instanceof SliderSetting) else if (mClickedItem instanceof IntSliderSetting)
{ {
SliderSetting sliderSetting = (SliderSetting) mClickedItem; IntSliderSetting sliderSetting = (IntSliderSetting) mClickedItem;
if (sliderSetting.getSelectedValue() != mSeekbarProgress) if (sliderSetting.getSelectedValue(getSettings()) != mSeekbarProgress)
mView.onSettingChanged(mClickedItem.getKey()); mView.onSettingChanged(mClickedItem.getKey());
if (sliderSetting.isPercentSetting() || sliderSetting.getSetting() instanceof FloatSetting) sliderSetting.setSelectedValue(getSettings(), mSeekbarProgress);
{
float value;
if (sliderSetting.isPercentSetting()) closeDialog();
{
value = mSeekbarProgress / 100.0f;
} }
else else if (mClickedItem instanceof FloatSliderSetting)
{ {
value = (float) mSeekbarProgress; FloatSliderSetting sliderSetting = (FloatSliderSetting) mClickedItem;
} if (sliderSetting.getSelectedValue(getSettings()) != mSeekbarProgress)
mView.onSettingChanged(mClickedItem.getKey());
FloatSetting setting = sliderSetting.setSelectedValue(value); sliderSetting.setSelectedValue(getSettings(), mSeekbarProgress);
if (setting != null)
{
mView.putSetting(setting);
}
}
else
{
IntSetting setting = sliderSetting.setSelectedValue(mSeekbarProgress);
if (setting != null)
{
mView.putSetting(setting);
}
}
closeDialog(); closeDialog();
} }
@ -532,7 +487,7 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
private int getSelectionForSingleChoiceValue(SingleChoiceSetting item) private int getSelectionForSingleChoiceValue(SingleChoiceSetting item)
{ {
int value = item.getSelectedValue(); int value = item.getSelectedValue(getSettings());
int valuesId = item.getValuesId(); int valuesId = item.getValuesId();
if (valuesId > 0) if (valuesId > 0)
@ -574,7 +529,7 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
private int getSelectionForSingleChoiceDynamicDescriptionsValue( private int getSelectionForSingleChoiceDynamicDescriptionsValue(
SingleChoiceSettingDynamicDescriptions item) SingleChoiceSettingDynamicDescriptions item)
{ {
int value = item.getSelectedValue(); int value = item.getSelectedValue(getSettings());
int valuesId = item.getValuesId(); int valuesId = item.getValuesId();
if (valuesId > 0) if (valuesId > 0)

View File

@ -14,7 +14,7 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.settings.model.Setting; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
import org.dolphinemu.dolphinemu.ui.DividerItemDecoration; import org.dolphinemu.dolphinemu.ui.DividerItemDecoration;
@ -188,9 +188,9 @@ public final class SettingsFragment extends Fragment implements SettingsFragment
} }
@Override @Override
public void putSetting(Setting setting) public Settings getSettings()
{ {
mPresenter.putSetting(setting); return mPresenter.getSettings();
} }
@Override @Override

View File

@ -2,7 +2,6 @@ package org.dolphinemu.dolphinemu.features.settings.ui;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import org.dolphinemu.dolphinemu.features.settings.model.Setting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
@ -23,9 +22,9 @@ public interface SettingsFragmentView
void onSettingsFileLoaded(Settings settings); void onSettingsFileLoaded(Settings settings);
/** /**
* Pass an ArrayList to the View so that it can be displayed on screen. * Pass an ArrayList of settings to the View so that it can be displayed on screen.
* *
* @param settingsList The result of converting the HashMap to an ArrayList * @param settingsList The settings to display
*/ */
void showSettingsList(ArrayList<SettingsItem> settingsList); void showSettingsList(ArrayList<SettingsItem> settingsList);
@ -61,11 +60,9 @@ public interface SettingsFragmentView
void showToastMessage(String message); void showToastMessage(String message);
/** /**
* Have the fragment add a setting to the HashMap. * @return The backing settings store.
*
* @param setting The (possibly previously missing) new setting.
*/ */
void putSetting(Setting setting); Settings getSettings();
/** /**
* Have the fragment tell the containing Activity that a setting was modified. * Have the fragment tell the containing Activity that a setting was modified.

View File

@ -57,7 +57,7 @@ public final class CheckBoxSettingViewHolder extends SettingViewHolder
mTextSettingDescription.setText(""); mTextSettingDescription.setText("");
} }
mCheckbox.setChecked(mItem.isChecked()); mCheckbox.setChecked(mItem.isChecked(getAdapter().getSettings()));
} }
@Override @Override

View File

@ -3,12 +3,13 @@ package org.dolphinemu.dolphinemu.features.settings.ui.viewholder;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker; import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker;
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
import org.dolphinemu.dolphinemu.ui.main.MainPresenter; import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
import org.dolphinemu.dolphinemu.utils.IniFile;
public final class FilePickerViewHolder extends SettingViewHolder public final class FilePickerViewHolder extends SettingViewHolder
{ {
@ -44,9 +45,10 @@ public final class FilePickerViewHolder extends SettingViewHolder
} }
else else
{ {
mTextSettingDescription.setText(NativeLibrary // TODO: Reopening INI files all the time is slow
.GetConfig(mFilePicker.getFile(), item.getSection(), item.getKey(), IniFile ini = new IniFile(SettingsFile.getSettingsFile(mFilePicker.getFile()));
mFilePicker.getSelectedValue())); mTextSettingDescription.setText(ini.getString(item.getSection(), item.getKey(),
mFilePicker.getSelectedValue(getAdapter().getSettings())));
} }
} }

View File

@ -44,7 +44,7 @@ public final class SingleChoiceViewHolder extends SettingViewHolder
else if (item instanceof SingleChoiceSetting) else if (item instanceof SingleChoiceSetting)
{ {
SingleChoiceSetting setting = (SingleChoiceSetting) item; SingleChoiceSetting setting = (SingleChoiceSetting) item;
int selected = setting.getSelectedValue(); int selected = setting.getSelectedValue(getAdapter().getSettings());
Resources resMgr = mTextSettingDescription.getContext().getResources(); Resources resMgr = mTextSettingDescription.getContext().getResources();
String[] choices = resMgr.getStringArray(setting.getChoicesId()); String[] choices = resMgr.getStringArray(setting.getChoicesId());
int[] values = resMgr.getIntArray(setting.getValuesId()); int[] values = resMgr.getIntArray(setting.getValuesId());
@ -60,7 +60,7 @@ public final class SingleChoiceViewHolder extends SettingViewHolder
{ {
StringSingleChoiceSetting setting = (StringSingleChoiceSetting) item; StringSingleChoiceSetting setting = (StringSingleChoiceSetting) item;
String[] choices = setting.getChoicesId(); String[] choices = setting.getChoicesId();
int valueIndex = setting.getSelectValueIndex(); int valueIndex = setting.getSelectValueIndex(getAdapter().getSettings());
if (valueIndex != -1) if (valueIndex != -1)
mTextSettingDescription.setText(choices[valueIndex]); mTextSettingDescription.setText(choices[valueIndex]);
} }
@ -68,7 +68,7 @@ public final class SingleChoiceViewHolder extends SettingViewHolder
{ {
SingleChoiceSettingDynamicDescriptions setting = SingleChoiceSettingDynamicDescriptions setting =
(SingleChoiceSettingDynamicDescriptions) item; (SingleChoiceSettingDynamicDescriptions) item;
int selected = setting.getSelectedValue(); int selected = setting.getSelectedValue(getAdapter().getSettings());
Resources resMgr = mTextSettingDescription.getContext().getResources(); Resources resMgr = mTextSettingDescription.getContext().getResources();
String[] choices = resMgr.getStringArray(setting.getDescriptionChoicesId()); String[] choices = resMgr.getStringArray(setting.getDescriptionChoicesId());
int[] values = resMgr.getIntArray(setting.getDescriptionValuesId()); int[] values = resMgr.getIntArray(setting.getDescriptionValuesId());

View File

@ -46,8 +46,8 @@ public final class SliderViewHolder extends SettingViewHolder
else else
{ {
mTextSettingDescription.setText(mContext mTextSettingDescription.setText(mContext
.getString(R.string.slider_setting_value, mItem.getSelectedValue(), .getString(R.string.slider_setting_value,
mItem.getUnits())); mItem.getSelectedValue(getAdapter().getSettings()), mItem.getUnits()));
} }
} }

View File

@ -2,32 +2,14 @@ package org.dolphinemu.dolphinemu.features.settings.utils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import android.text.TextUtils;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.model.FloatSetting;
import org.dolphinemu.dolphinemu.features.settings.model.IntSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Setting;
import org.dolphinemu.dolphinemu.features.settings.model.SettingSection;
import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivityView; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivityView;
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization; import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
import org.dolphinemu.dolphinemu.utils.BiMap; import org.dolphinemu.dolphinemu.utils.BiMap;
import org.dolphinemu.dolphinemu.utils.IniFile;
import org.dolphinemu.dolphinemu.utils.Log; import org.dolphinemu.dolphinemu.utils.Log;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
/** /**
* Contains static methods for interacting with .ini files in which settings are stored. * Contains static methods for interacting with .ini files in which settings are stored.
@ -307,196 +289,103 @@ public final class SettingsFile
} }
/** /**
* Reads a given .ini file from disk and returns it as a HashMap of Settings, themselves * Reads a given .ini file from disk and returns it.
* effectively a HashMap of key/value settings. If unsuccessful, outputs an error telling why it * If unsuccessful, outputs an error telling why it failed.
* failed.
* *
* @param ini The ini file to load the settings from * @param file The ini file to load the settings from
* @param ini The object to load into
* @param view The current view. * @param view The current view.
*/ */
static HashMap<String, SettingSection> readFile(final File ini, boolean isCustomGame, static void readFile(final File file, IniFile ini, SettingsActivityView view)
SettingsActivityView view)
{ {
HashMap<String, SettingSection> sections = new Settings.SettingsSectionMap(); if (!ini.load(file, true))
BufferedReader reader = null;
try
{ {
reader = new BufferedReader(new FileReader(ini)); Log.error("[SettingsFile] Error reading from: " + file.getAbsolutePath());
SettingSection current = null;
for (String line; (line = reader.readLine()) != null; )
{
if (line.startsWith("[") && line.endsWith("]"))
{
current = sectionFromLine(line, isCustomGame);
sections.put(current.getName(), current);
}
else if ((current != null))
{
Setting setting = settingFromLine(current, line);
if (setting != null)
{
current.putSetting(setting);
}
}
}
}
catch (FileNotFoundException e)
{
Log.error("[SettingsFile] File not found: " + ini.getAbsolutePath() + e.getMessage());
if (view != null) if (view != null)
view.onSettingsFileNotFound(); view.onSettingsFileNotFound();
} }
catch (IOException e)
{
Log.error("[SettingsFile] Error reading from: " + ini.getAbsolutePath() + e.getMessage());
if (view != null)
view.onSettingsFileNotFound();
}
finally
{
if (reader != null)
{
try
{
reader.close();
}
catch (IOException e)
{
Log.error("[SettingsFile] Error closing: " + ini.getAbsolutePath() + e.getMessage());
}
}
} }
return sections; public static void readFile(final String fileName, IniFile ini, SettingsActivityView view)
}
public static HashMap<String, SettingSection> readFile(final String fileName,
SettingsActivityView view)
{ {
HashMap<String, SettingSection> sections = readFile(getSettingsFile(fileName), false, view); readFile(getSettingsFile(fileName), ini, view);
if (fileName.equals(SettingsFile.FILE_NAME_DOLPHIN)) if (fileName.equals(SettingsFile.FILE_NAME_DOLPHIN))
{ {
addGcPadSettingsIfTheyDontExist(sections); addGcPadSettingsIfTheyDontExist(ini);
} }
return sections;
} }
/** /**
* Reads a given .ini file from disk and returns it as a HashMap of SettingSections, themselves * Reads a given .ini file from disk and returns it.
* effectively a HashMap of key/value settings. If unsuccessful, outputs an error telling why it * If unsuccessful, outputs an error telling why it failed.
* failed.
* *
* @param gameId the id of the game to load it's settings. * @param gameId the id of the game to load settings for.
* @param ini The object to load into
* @param view The current view. * @param view The current view.
*/ */
public static HashMap<String, SettingSection> readCustomGameSettings(final String gameId, public static void readCustomGameSettings(final String gameId, IniFile ini,
SettingsActivityView view) SettingsActivityView view)
{ {
return readFile(getCustomGameSettingsFile(gameId), true, view); readFile(getCustomGameSettingsFile(gameId), ini, view);
} }
public static HashMap<String, SettingSection> readGenericGameSettings(final String gameId, public static void readGenericGameSettings(final String gameId, IniFile ini,
SettingsActivityView view) SettingsActivityView view)
{ {
return readFile(getGenericGameSettingsFile(gameId), true, view); readFile(getGenericGameSettingsFile(gameId), ini, view);
} }
public static HashMap<String, SettingSection> readGenericGameSettingsForAllRegions( public static void readGenericGameSettingsForAllRegions(final String gameId,
final String gameId, SettingsActivityView view) IniFile ini, SettingsActivityView view)
{ {
return readFile(getGenericGameSettingsForAllRegions(gameId), true, view); readFile(getGenericGameSettingsForAllRegions(gameId), ini, view);
} }
public static HashMap<String, SettingSection> readWiimoteProfile(final String gameId, public static void readWiimoteProfile(final String gameId, IniFile ini, final int padId)
final String padId)
{ {
String profile = gameId + "_Wii" + padId; String profile = gameId + "_Wii" + padId;
return readFile(getWiiProfile(profile, padId), true, null); readFile(getWiiProfile(profile), ini, null);
} }
/** /**
* Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error * Saves a given .ini file on disk.
* telling why it failed. * If unsuccessful, outputs an error telling why it failed.
* *
* @param fileName The target filename without a path or extension. * @param fileName The target filename without a path or extension.
* @param sections The HashMap containing the Settings we want to serialize. * @param ini The IniFile we want to serialize.
* @param view The current view. * @param view The current view.
*/ */
public static void saveFile(final String fileName, TreeMap<String, SettingSection> sections, public static void saveFile(final String fileName, IniFile ini, SettingsActivityView view)
SettingsActivityView view)
{ {
File ini = getSettingsFile(fileName); if (!ini.save(getSettingsFile(fileName)))
try (PrintWriter writer = new PrintWriter(ini, "UTF-8"))
{ {
Log.error("[SettingsFile] Error saving to: " + fileName + ".ini");
Set<String> keySet = sections.keySet();
Set<String> sortedKeySet = new TreeSet<>(keySet);
for (String key : sortedKeySet)
{
SettingSection section = sections.get(key);
writeSection(writer, section);
}
}
catch (FileNotFoundException e)
{
Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.getMessage());
if (view != null) if (view != null)
view.showToastMessage("Error saving " + fileName + ".ini: " + e.getMessage()); view.showToastMessage("Error saving " + fileName + ".ini");
}
catch (UnsupportedEncodingException e)
{
Log.error("[SettingsFile] Bad encoding; please file a bug report: " + fileName + ".ini: " +
e.getMessage());
if (view != null)
view.showToastMessage("Error saving " + fileName + ".ini: " + e.getMessage());
} }
} }
public static void saveCustomGameSettings(final String gameId, public static void saveCustomGameSettings(final String gameId, IniFile ini)
final HashMap<String, SettingSection> sections)
{ {
Set<String> sortedSections = new TreeSet<>(sections.keySet()); IniFile iniCopy = new IniFile(ini);
NativeLibrary.NewGameIniFile();
for (String sectionKey : sortedSections)
{
SettingSection section = sections.get(sectionKey);
HashMap<String, Setting> settings = section.getSettings();
Set<String> sortedKeySet = new TreeSet<>(settings.keySet());
// Profile options(wii extension) are not saved, only used to properly display values // Profile options(wii extension) are not saved, only used to properly display values
if (sectionKey.contains(Settings.SECTION_PROFILE)) iniCopy.deleteSection(Settings.SECTION_PROFILE);
for (int i = 0; i < 3; i++)
{ {
continue; 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);
}
} }
for (String settingKey : sortedKeySet) iniCopy.save(getCustomGameSettingsFile(gameId));
{
Setting setting = settings.get(settingKey);
// Special case. Extension gets saved into a controller profile
if (settingKey.contains(SettingsFile.KEY_WIIMOTE_EXTENSION))
{
String padId =
setting.getKey()
.substring(setting.getKey().length() - 1, setting.getKey().length());
saveCustomWiimoteSetting(gameId, KEY_WIIMOTE_EXTENSION, setting.getValueAsString(),
padId);
}
else
{
NativeLibrary.SetUserSetting(gameId, mapSectionNameFromIni(section.getName()),
setting.getKey(), setting.getValueAsString());
}
}
}
NativeLibrary.SaveGameIniFile(gameId);
} }
/** /**
@ -508,34 +397,43 @@ public final class SettingsFile
* @param padId * @param padId
*/ */
private static void saveCustomWiimoteSetting(final String gameId, final String key, private static void saveCustomWiimoteSetting(final String gameId, final String key,
final String value, final String value, final int padId)
final String padId)
{ {
String profile = gameId + "_Wii" + padId; String profile = gameId + "_Wii" + padId;
String wiiConfigPath = String wiiConfigPath =
DirectoryInitialization.getUserDirectory() + "/Config/Profiles/Wiimote/" + DirectoryInitialization.getUserDirectory() + "/Config/Profiles/Wiimote/" +
profile + ".ini"; profile + ".ini";
File wiiProfile = new File(wiiConfigPath); File wiiProfile = getWiiProfile(profile);
// If it doesn't exist, create it // If it doesn't exist, create it
if (!wiiProfile.exists()) boolean wiiProfileExists = wiiProfile.exists();
if (!wiiProfileExists)
{ {
String defautlWiiProfilePath = String defautlWiiProfilePath =
DirectoryInitialization.getUserDirectory() + DirectoryInitialization.getUserDirectory() +
"/Config/Profiles/Wiimote/WiimoteProfile.ini"; "/Config/Profiles/Wiimote/WiimoteProfile.ini";
DirectoryInitialization.copyFile(defautlWiiProfilePath, wiiConfigPath); DirectoryInitialization.copyFile(defautlWiiProfilePath, wiiConfigPath);
NativeLibrary.SetProfileSetting(profile, Settings.SECTION_PROFILE, "Device",
"Android/" + (Integer.parseInt(padId) + 4) + "/Touchscreen");
} }
NativeLibrary.SetProfileSetting(profile, Settings.SECTION_PROFILE, key, value); 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 // Enable the profile
NativeLibrary.SetUserSetting(gameId, Settings.SECTION_CONTROLS, File gameSettingsFile = SettingsFile.getCustomGameSettingsFile(gameId);
KEY_WIIMOTE_PROFILE + (Integer.parseInt(padId) + 1), profile); IniFile gameSettingsIni = new IniFile(gameSettingsFile);
gameSettingsIni.setString(Settings.SECTION_CONTROLS, KEY_WIIMOTE_PROFILE + (padId + 1),
profile);
gameSettingsIni.save(gameSettingsFile);
} }
private static String mapSectionNameFromIni(String generalSectionName) public static String mapSectionNameFromIni(String generalSectionName)
{ {
if (sectionsMap.getForward(generalSectionName) != null) if (sectionsMap.getForward(generalSectionName) != null)
{ {
@ -545,7 +443,7 @@ public final class SettingsFile
return generalSectionName; return generalSectionName;
} }
private static String mapSectionNameToIni(String generalSectionName) public static String mapSectionNameToIni(String generalSectionName)
{ {
if (sectionsMap.getBackward(generalSectionName) != null) if (sectionsMap.getBackward(generalSectionName) != null)
{ {
@ -556,7 +454,7 @@ public final class SettingsFile
} }
@NonNull @NonNull
private static File getSettingsFile(String fileName) public static File getSettingsFile(String fileName)
{ {
return new File( return new File(
DirectoryInitialization.getUserDirectory() + "/Config/" + fileName + ".ini"); DirectoryInitialization.getUserDirectory() + "/Config/" + fileName + ".ini");
@ -578,14 +476,13 @@ public final class SettingsFile
gameId + ".ini"); gameId + ".ini");
} }
private static File getCustomGameSettingsFile(String gameId) public static File getCustomGameSettingsFile(String gameId)
{ {
return new File( return new File(
DirectoryInitialization.getUserDirectory() + "/GameSettings/" + gameId + ".ini"); DirectoryInitialization.getUserDirectory() + "/GameSettings/" + gameId + ".ini");
} }
private static File getWiiProfile(String profile, String padId) private static File getWiiProfile(String profile)
{ {
String wiiConfigPath = String wiiConfigPath =
DirectoryInitialization.getUserDirectory() + "/Config/Profiles/Wiimote/" + DirectoryInitialization.getUserDirectory() + "/Config/Profiles/Wiimote/" +
@ -594,136 +491,29 @@ public final class SettingsFile
return new File(wiiConfigPath); return new File(wiiConfigPath);
} }
private static SettingSection sectionFromLine(String line, boolean isCustomGame) private static void addGcPadSettingsIfTheyDontExist(IniFile ini)
{ {
String sectionName = line.substring(1, line.length() - 1); IniFile.Section coreSection = ini.getOrCreateSection(Settings.SECTION_INI_CORE);
if (isCustomGame)
{
sectionName = mapSectionNameToIni(sectionName);
}
return new SettingSection(sectionName);
}
private static void addGcPadSettingsIfTheyDontExist(HashMap<String, SettingSection> sections)
{
SettingSection coreSection = sections.get(Settings.SECTION_INI_CORE);
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
String key = SettingsFile.KEY_GCPAD_TYPE + i; String key = SettingsFile.KEY_GCPAD_TYPE + i;
if (coreSection.getSetting(key) == null) if (!coreSection.exists(key))
{ {
// Set GameCube controller 1 to enabled, all others disabled // Set GameCube controller 1 to enabled, all others disabled
Setting gcPadSetting = new IntSetting(key, Settings.SECTION_INI_CORE, i == 0 ? 6 : 0); coreSection.setInt(key, i == 0 ? 6 : 0);
coreSection.putSetting(gcPadSetting);
} }
} }
sections.put(Settings.SECTION_INI_CORE, coreSection);
} }
public static void firstAnalyticsAdd(boolean enabled) public static void firstAnalyticsAdd(boolean enabled)
{ {
HashMap<String, SettingSection> dolphinSections = IniFile dolphinIni = new IniFile();
readFile(SettingsFile.FILE_NAME_DOLPHIN, null); readFile(SettingsFile.FILE_NAME_DOLPHIN, dolphinIni, null);
SettingSection analyticsSection = dolphinSections.get(Settings.SECTION_ANALYTICS);
Setting analyticsEnabled = new StringSetting(KEY_ANALYTICS_ENABLED, Settings.SECTION_ANALYTICS, dolphinIni.setBoolean(Settings.SECTION_ANALYTICS, KEY_ANALYTICS_ENABLED, enabled);
enabled ? "True" : "False"); dolphinIni.setBoolean(Settings.SECTION_ANALYTICS, KEY_ANALYTICS_PERMISSION_ASKED, true);
Setting analyticsFirstAsk =
new StringSetting(KEY_ANALYTICS_PERMISSION_ASKED, Settings.SECTION_ANALYTICS, "True");
analyticsSection.putSetting(analyticsFirstAsk); saveFile(SettingsFile.FILE_NAME_DOLPHIN, dolphinIni, null);
analyticsSection.putSetting(analyticsEnabled);
dolphinSections.put(Settings.SECTION_ANALYTICS, analyticsSection);
TreeMap<String, SettingSection> saveSection = new TreeMap<>(dolphinSections);
saveFile(SettingsFile.FILE_NAME_DOLPHIN, saveSection, null);
}
/**
* For a line of text, determines what type of data is being represented, and returns
* a Setting object containing this data.
*
* @param current The section currently being parsed by the consuming method.
* @param line The line of text being parsed.
* @return A typed Setting containing the key/value contained in the line.
*/
private static Setting settingFromLine(SettingSection current, String line)
{
String[] splitLine = line.split("=");
if (splitLine.length != 2)
{
Log.warning("Skipping invalid config line \"" + line + "\"");
return null;
}
String key = splitLine[0].trim();
String value = splitLine[1].trim();
try
{
int valueAsInt = Integer.parseInt(value);
return new IntSetting(key, current.getName(), valueAsInt);
}
catch (NumberFormatException ignored)
{
}
try
{
float valueAsFloat = Float.parseFloat(value);
return new FloatSetting(key, current.getName(), valueAsFloat);
}
catch (NumberFormatException ignored)
{
}
switch (value)
{
case "True":
return new BooleanSetting(key, current.getName(), true);
case "False":
return new BooleanSetting(key, current.getName(), false);
default:
return new StringSetting(key, current.getName(), value);
}
}
/**
* Writes the contents of a Section HashMap to disk.
*
* @param writer A PrintWriter pointed at a file on disk.
* @param section A section containing settings to be written to the file.
*/
private static void writeSection(PrintWriter writer, SettingSection section)
{
// Write the section header.
String header = sectionAsString(section);
writer.println(header);
// Write this section's values.
HashMap<String, Setting> settings = section.getSettings();
Set<String> keySet = settings.keySet();
Set<String> sortedKeySet = new TreeSet<>(keySet);
for (String key : sortedKeySet)
{
Setting setting = settings.get(key);
String valueAsString = setting.getValueAsString();
if (!TextUtils.isEmpty(valueAsString))
{
writer.println(setting.getKey() + " = " + valueAsString);
}
}
}
private static String sectionAsString(SettingSection section)
{
return "[" + section.getName() + "]";
} }
} }

View File

@ -15,7 +15,6 @@ import android.widget.TextView;
import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.EmulationActivity; import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
@ -72,12 +71,11 @@ public final class MenuFragment extends Fragment implements View.OnClickListener
showUnpauseEmulationButton(); showUnpauseEmulationButton();
} }
BooleanSetting enableSaveStates = boolean enableSaveStates = ((EmulationActivity) getActivity()).getSettings()
(BooleanSetting) ((EmulationActivity) getActivity()).getSettings() .getSection(SettingsFile.FILE_NAME_DOLPHIN, Settings.SECTION_INI_CORE)
.getSection(Settings.SECTION_INI_CORE) .getBoolean(SettingsFile.KEY_ENABLE_SAVE_STATES, false);
.getSetting(SettingsFile.KEY_ENABLE_SAVE_STATES);
if (enableSaveStates != null && enableSaveStates.getValue()) if (enableSaveStates)
{ {
options.findViewById(R.id.menu_quicksave).setVisibility(View.VISIBLE); options.findViewById(R.id.menu_quicksave).setVisibility(View.VISIBLE);
options.findViewById(R.id.menu_quickload).setVisibility(View.VISIBLE); options.findViewById(R.id.menu_quickload).setVisibility(View.VISIBLE);

View File

@ -7,6 +7,7 @@ import android.preference.PreferenceManager;
import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
import org.dolphinemu.dolphinemu.utils.IniFile;
import java.io.File; import java.io.File;
import java.util.HashSet; import java.util.HashSet;
@ -82,9 +83,10 @@ public class GameFileCache
*/ */
public boolean scanLibrary(Context context) public boolean scanLibrary(Context context)
{ {
boolean recursiveScan = NativeLibrary IniFile dolphinIni =
.GetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_GENERAL, new IniFile(SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_DOLPHIN));
SettingsFile.KEY_RECURSIVE_ISO_PATHS, "False").equals("True"); boolean recursiveScan = dolphinIni.getBoolean(Settings.SECTION_INI_GENERAL,
SettingsFile.KEY_RECURSIVE_ISO_PATHS, false);
removeNonExistentGameFolders(context); removeNonExistentGameFolders(context);

View File

@ -33,6 +33,7 @@ import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.EmulationActivity; import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
import org.dolphinemu.dolphinemu.utils.IniFile;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; 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_WIIMOTE_CLASSIC = 4;
public static final int OVERLAY_NONE = 5; public static final int OVERLAY_NONE = 5;
private static final String DISABLED_GAMECUBE_CONTROLLER = "0"; private static final int DISABLED_GAMECUBE_CONTROLLER = 0;
private static final String EMULATED_GAMECUBE_CONTROLLER = "6"; private static final int EMULATED_GAMECUBE_CONTROLLER = 6;
private static final String GAMECUBE_ADAPTER = "12"; private static final int GAMECUBE_ADAPTER = 12;
private final Set<InputOverlayDrawableButton> overlayButtons = new HashSet<>(); private final Set<InputOverlayDrawableButton> overlayButtons = new HashSet<>();
private final Set<InputOverlayDrawableDpad> overlayDpads = new HashSet<>(); private final Set<InputOverlayDrawableDpad> 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. // Add all the enabled overlay items back to the HashSet.
if (EmulationActivity.isGameCubeGame()) if (EmulationActivity.isGameCubeGame())
{ {
switch (NativeLibrary IniFile dolphinIni =
.GetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_CORE, new IniFile(SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_DOLPHIN));
SettingsFile.KEY_GCPAD_PLAYER_1, EMULATED_GAMECUBE_CONTROLLER))
switch (dolphinIni.getInt(Settings.SECTION_INI_CORE, SettingsFile.KEY_GCPAD_PLAYER_1,
EMULATED_GAMECUBE_CONTROLLER))
{ {
case DISABLED_GAMECUBE_CONTROLLER: case DISABLED_GAMECUBE_CONTROLLER:
if (mIsFirstRun) if (mIsFirstRun)

View File

@ -33,9 +33,12 @@ import org.dolphinemu.dolphinemu.services.GameFileCacheService;
import org.dolphinemu.dolphinemu.ui.platform.Platform; import org.dolphinemu.dolphinemu.ui.platform.Platform;
import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView; import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView;
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
import org.dolphinemu.dolphinemu.utils.IniFile;
import org.dolphinemu.dolphinemu.utils.PermissionsHandler; import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
import org.dolphinemu.dolphinemu.utils.StartupHandler; import org.dolphinemu.dolphinemu.utils.StartupHandler;
import java.io.File;
/** /**
* The main Activity of the Lollipop style UI. Manages several PlatformGamesFragments, which * 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. * 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) public void onTabSelected(@NonNull TabLayout.Tab tab)
{ {
super.onTabSelected(tab); super.onTabSelected(tab);
NativeLibrary
.SetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_ANDROID, File dolphinFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_DOLPHIN);
SettingsFile.KEY_LAST_PLATFORM_TAB, Integer.toString(tab.getPosition())); IniFile dolphinIni = new IniFile(dolphinFile);
dolphinIni.setInt(Settings.SECTION_INI_ANDROID, SettingsFile.KEY_LAST_PLATFORM_TAB,
tab.getPosition());
dolphinIni.save(dolphinFile);
} }
}); });
String platformTab = NativeLibrary IniFile dolphinIni =
.GetConfig(SettingsFile.FILE_NAME_DOLPHIN + ".ini", Settings.SECTION_INI_ANDROID, new IniFile(SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_DOLPHIN));
SettingsFile.KEY_LAST_PLATFORM_TAB, "0"); mViewPager.setCurrentItem(dolphinIni.getInt(Settings.SECTION_INI_ANDROID,
SettingsFile.KEY_LAST_PLATFORM_TAB, 0));
try
{
mViewPager.setCurrentItem(Integer.parseInt(platformTab));
}
catch (NumberFormatException ex)
{
mViewPager.setCurrentItem(0);
}
showGames(); showGames();
GameFileCacheService.startLoad(this); GameFileCacheService.startLoad(this);

View File

@ -0,0 +1,110 @@
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;
}
public native boolean exists(String key);
public native boolean delete(String key);
public native String getString(String key, String defaultValue);
public native boolean getBoolean(String key, boolean defaultValue);
public native int getInt(String key, int defaultValue);
public native float getFloat(String key, float defaultValue);
public native void setString(String key, String newValue);
public native void setBoolean(String key, boolean newValue);
public native void setInt(String key, int newValue);
public native void setFloat(String key, float newFloat);
}
private long mPointer; // Do not rename or move without editing the native code
public IniFile()
{
mPointer = newIniFile();
}
public IniFile(IniFile other)
{
mPointer = copyIniFile(other);
}
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 Section getOrCreateSection(String sectionName);
public native boolean exists(String sectionName);
public native boolean exists(String sectionName, String key);
public native boolean deleteSection(String sectionName);
public native boolean deleteKey(String sectionName, String key);
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 float getFloat(String sectionName, String key, float 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);
public native void setFloat(String sectionName, String key, float newValue);
@Override
public native void finalize();
private native long newIniFile();
private native long copyIniFile(IniFile other);
}

View File

@ -10,7 +10,6 @@ import android.view.InputDevice;
import org.dolphinemu.dolphinemu.activities.EmulationActivity; import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
public class Rumble public class Rumble
@ -29,15 +28,16 @@ public class Rumble
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
StringSetting deviceName = String deviceName = activity.getSettings()
(StringSetting) activity.getSettings().getSection(Settings.SECTION_BINDINGS) .getSection(SettingsFile.FILE_NAME_DOLPHIN, Settings.SECTION_BINDINGS)
.getSetting(SettingsFile.KEY_EMU_RUMBLE + i); .getString(SettingsFile.KEY_EMU_RUMBLE + i, "");
if (deviceName != null && !deviceName.getValue().isEmpty())
if (!deviceName.isEmpty())
{ {
for (int id : InputDevice.getDeviceIds()) for (int id : InputDevice.getDeviceIds())
{ {
InputDevice device = InputDevice.getDevice(id); InputDevice device = InputDevice.getDevice(id);
if (deviceName.getValue().equals(device.getDescriptor())) if (deviceName.equals(device.getDescriptor()))
{ {
Vibrator vib = device.getVibrator(); Vibrator vib = device.getVibrator();
if (vib != null && vib.hasVibrator()) if (vib != null && vib.hasVibrator())

View File

@ -30,6 +30,12 @@ static jclass s_linked_hash_map_class;
static jmethodID s_linked_hash_map_init; static jmethodID s_linked_hash_map_init;
static jmethodID s_linked_hash_map_put; 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 namespace IDCache
{ {
JNIEnv* GetEnvForThread() JNIEnv* GetEnvForThread()
@ -89,6 +95,7 @@ jmethodID GetAnalyticsValue()
{ {
return s_get_analytics_value; return s_get_analytics_value;
} }
jclass GetGameFileClass() jclass GetGameFileClass()
{ {
return s_game_file_class; return s_game_file_class;
@ -129,6 +136,31 @@ jmethodID GetLinkedHashMapPut()
return s_linked_hash_map_put; 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 } // namespace IDCache
#ifdef __cplusplus #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_do_rumble = env->GetStaticMethodID(s_native_library_class, "rumble", "(ID)V");
s_get_update_touch_pointer = s_get_update_touch_pointer =
env->GetStaticMethodID(s_native_library_class, "updateTouchPointer", "()V"); 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"); const jclass game_file_class = env->FindClass("org/dolphinemu/dolphinemu/model/GameFile");
s_game_file_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_file_class)); s_game_file_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_file_class));
s_game_file_pointer = env->GetFieldID(game_file_class, "mPointer", "J"); s_game_file_pointer = env->GetFieldID(game_file_class, "mPointer", "J");
s_game_file_constructor = env->GetMethodID(game_file_class, "<init>", "(J)V"); s_game_file_constructor = env->GetMethodID(game_file_class, "<init>", "(J)V");
env->DeleteLocalRef(game_file_class);
const jclass game_file_cache_class = const jclass game_file_cache_class =
env->FindClass("org/dolphinemu/dolphinemu/model/GameFileCache"); env->FindClass("org/dolphinemu/dolphinemu/model/GameFileCache");
s_game_file_cache_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_file_cache_class)); s_game_file_cache_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_file_cache_class));
s_game_file_cache_pointer = env->GetFieldID(game_file_cache_class, "mPointer", "J"); 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"); const jclass analytics_class = env->FindClass("org/dolphinemu/dolphinemu/utils/Analytics");
s_analytics_class = reinterpret_cast<jclass>(env->NewGlobalRef(analytics_class)); s_analytics_class = reinterpret_cast<jclass>(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"); env->GetStaticMethodID(s_analytics_class, "sendReport", "(Ljava/lang/String;[B)V");
s_get_analytics_value = env->GetStaticMethodID(s_analytics_class, "getValue", s_get_analytics_value = env->GetStaticMethodID(s_analytics_class, "getValue",
"(Ljava/lang/String;)Ljava/lang/String;"); "(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<jclass>(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<jclass>(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, "<init>", "(Lorg/dolphinemu/dolphinemu/utils/IniFile;J)V");
env->DeleteLocalRef(ini_file_section_class);
const jclass map_class = env->FindClass("java/util/LinkedHashMap"); const jclass map_class = env->FindClass("java/util/LinkedHashMap");
s_linked_hash_map_class = reinterpret_cast<jclass>(env->NewGlobalRef(map_class)); s_linked_hash_map_class = reinterpret_cast<jclass>(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_game_file_cache_class);
env->DeleteGlobalRef(s_analytics_class); env->DeleteGlobalRef(s_analytics_class);
env->DeleteGlobalRef(s_linked_hash_map_class); env->DeleteGlobalRef(s_linked_hash_map_class);
env->DeleteGlobalRef(s_ini_file_class);
env->DeleteGlobalRef(s_ini_file_section_class);
} }
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -32,4 +32,10 @@ jclass GetLinkedHashMapClass();
jmethodID GetLinkedHashMapInit(); jmethodID GetLinkedHashMapInit();
jmethodID GetLinkedHashMapPut(); jmethodID GetLinkedHashMapPut();
jclass GetIniFileClass();
jfieldID GetIniFilePointer();
jclass GetIniFileSectionClass();
jfieldID GetIniFileSectionPointer();
jmethodID GetIniFileSectionConstructor();
} // namespace IDCache } // namespace IDCache

View File

@ -3,6 +3,7 @@ add_library(main SHARED
AndroidCommon/IDCache.cpp AndroidCommon/IDCache.cpp
GameList/GameFile.cpp GameList/GameFile.cpp
GameList/GameFileCache.cpp GameList/GameFileCache.cpp
IniFile.cpp
MainAndroid.cpp MainAndroid.cpp
) )

View File

@ -0,0 +1,249 @@
// Copyright 2020 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <jni.h>
#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<IniFile::Section*>(
env->GetLongField(obj, IDCache::GetIniFileSectionPointer()));
}
static IniFile* GetIniFilePointer(JNIEnv* env, jobject obj)
{
return reinterpret_cast<IniFile*>(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<jlong>(section));
}
template <typename T>
static T GetInSection(JNIEnv* env, jobject obj, jstring key, T default_value)
{
T result;
GetSectionPointer(env, obj)->Get(GetJString(env, key), &result, default_value);
return result;
}
template <typename T>
static void SetInSection(JNIEnv* env, jobject obj, jstring key, T new_value)
{
GetSectionPointer(env, obj)->Set(GetJString(env, key), new_value);
}
template <typename T>
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 <typename T>
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_00024Section_exists(
JNIEnv* env, jobject obj, jstring key)
{
return static_cast<jboolean>(GetSectionPointer(env, obj)->Exists(GetJString(env, key)));
}
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_00024Section_delete(
JNIEnv* env, jobject obj, jstring key)
{
return static_cast<jboolean>(GetSectionPointer(env, obj)->Delete(GetJString(env, key)));
}
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_00024Section_getString(
JNIEnv* env, jobject obj, jstring key, jstring default_value)
{
return ToJString(env, GetInSection(env, obj, key, GetJString(env, default_value)));
}
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_00024Section_getBoolean(
JNIEnv* env, jobject obj, jstring key, jboolean default_value)
{
return static_cast<jboolean>(GetInSection(env, obj, key, static_cast<bool>(default_value)));
}
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_00024Section_getInt(
JNIEnv* env, jobject obj, jstring key, jint default_value)
{
return GetInSection(env, obj, key, default_value);
}
JNIEXPORT jfloat JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_00024Section_getFloat(
JNIEnv* env, jobject obj, jstring key, jfloat default_value)
{
return GetInSection(env, obj, key, default_value);
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_00024Section_setString(
JNIEnv* env, jobject obj, jstring key, jstring new_value)
{
SetInSection(env, obj, key, GetJString(env, new_value));
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_00024Section_setBoolean(
JNIEnv* env, jobject obj, jstring key, jboolean new_value)
{
SetInSection(env, obj, key, static_cast<bool>(new_value));
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_00024Section_setInt(
JNIEnv* env, jobject obj, jstring key, jint new_value)
{
SetInSection(env, obj, key, new_value);
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_00024Section_setFloat(
JNIEnv* env, jobject obj, jstring key, jfloat new_value)
{
SetInSection(env, obj, key, new_value);
}
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_load(
JNIEnv* env, jobject obj, jstring path, jboolean keep_current_data)
{
return static_cast<jboolean>(
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<jboolean>(GetIniFilePointer(env, obj)->Save(GetJString(env, path)));
}
JNIEXPORT jobject JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_getOrCreateSection(
JNIEnv* env, jobject obj, jstring section_name)
{
return SectionToJava(
env, obj, GetIniFilePointer(env, obj)->GetOrCreateSection(GetJString(env, section_name)));
}
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_exists__Ljava_lang_String_2(
JNIEnv* env, jobject obj, jstring section_name)
{
return static_cast<jboolean>(GetIniFilePointer(env, obj)->Exists(GetJString(env, section_name)));
}
JNIEXPORT jboolean JNICALL
Java_org_dolphinemu_dolphinemu_utils_IniFile_exists__Ljava_lang_String_2Ljava_lang_String_2(
JNIEnv* env, jobject obj, jstring section_name, jstring key)
{
return static_cast<jboolean>(
GetIniFilePointer(env, obj)->Exists(GetJString(env, section_name), GetJString(env, key)));
}
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_deleteSection(
JNIEnv* env, jobject obj, jstring section_name)
{
return static_cast<jboolean>(
GetIniFilePointer(env, obj)->DeleteSection(GetJString(env, section_name)));
}
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_deleteKey(
JNIEnv* env, jobject obj, jstring section_name, jstring key)
{
return static_cast<jboolean>(
GetIniFilePointer(env, obj)->DeleteKey(GetJString(env, section_name), GetJString(env, key)));
}
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<jboolean>(Get(env, obj, section_name, key, static_cast<bool>(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 jfloat JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_getFloat(
JNIEnv* env, jobject obj, jstring section_name, jstring key, jfloat 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<bool>(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_setFloat(
JNIEnv* env, jobject obj, jstring section_name, jstring key, jfloat 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<jlong>(new IniFile);
}
JNIEXPORT jlong JNICALL Java_org_dolphinemu_dolphinemu_utils_IniFile_copyIniFile(JNIEnv* env,
jobject obj,
jobject other)
{
return reinterpret_cast<jlong>(new IniFile(*GetIniFilePointer(env, other)));
}
#ifdef __cplusplus
}
#endif

View File

@ -213,10 +213,6 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveScreenSh
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_eglBindAPI(JNIEnv* env, JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_eglBindAPI(JNIEnv* env,
jobject obj, jobject obj,
jint api); 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, JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetFilename(JNIEnv* env,
jobject obj, jobject obj,
jstring jFile); jstring jFile);
@ -355,129 +351,6 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_eglBindAPI(J
eglBindAPI(api); 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, JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveState(JNIEnv* env,
jobject obj, jobject obj,
jint slot, jint slot,