Merge pull request #10885 from JosJuice/android-graphics-mods

Android: Add graphics mods support to GUI
This commit is contained in:
JosJuice 2022-07-26 15:44:01 +02:00 committed by GitHub
commit 97100290ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 477 additions and 48 deletions

View file

@ -3,11 +3,13 @@
package org.dolphinemu.dolphinemu.features.cheats.model; package org.dolphinemu.dolphinemu.features.cheats.model;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public abstract class AbstractCheat implements Cheat public abstract class AbstractCheat extends ReadOnlyCheat
{ {
private Runnable mChangedCallback = null; public boolean supportsCode()
{
return true;
}
public int trySet(@NonNull String name, @NonNull String creator, @NonNull String notes, public int trySet(@NonNull String name, @NonNull String creator, @NonNull String notes,
@NonNull String code) @NonNull String code)
@ -38,25 +40,6 @@ public abstract class AbstractCheat implements Cheat
return result; return result;
} }
public void setEnabled(boolean enabled)
{
setEnabledImpl(enabled);
onChanged();
}
public void setChangedCallback(@Nullable Runnable callback)
{
mChangedCallback = callback;
}
protected void onChanged()
{
if (mChangedCallback != null)
mChangedCallback.run();
}
protected abstract int trySetImpl(@NonNull String name, @NonNull String creator, protected abstract int trySetImpl(@NonNull String name, @NonNull String creator,
@NonNull String notes, @NonNull String code); @NonNull String notes, @NonNull String code);
protected abstract void setEnabledImpl(boolean enabled);
} }

View file

@ -17,6 +17,8 @@ public interface Cheat
boolean supportsNotes(); boolean supportsNotes();
boolean supportsCode();
@NonNull @NonNull
String getName(); String getName();
@ -33,7 +35,10 @@ public interface Cheat
} }
@NonNull @NonNull
String getCode(); default String getCode()
{
return "";
}
int trySet(@NonNull String name, @NonNull String creator, @NonNull String notes, int trySet(@NonNull String name, @NonNull String creator, @NonNull String notes,
@NonNull String code); @NonNull String code);

View file

@ -24,10 +24,13 @@ public class CheatsViewModel extends ViewModel
private final MutableLiveData<Integer> mGeckoCheatsDownloadedEvent = new MutableLiveData<>(null); private final MutableLiveData<Integer> mGeckoCheatsDownloadedEvent = new MutableLiveData<>(null);
private final MutableLiveData<Boolean> mOpenDetailsViewEvent = new MutableLiveData<>(false); private final MutableLiveData<Boolean> mOpenDetailsViewEvent = new MutableLiveData<>(false);
private GraphicsModGroup mGraphicsModGroup;
private ArrayList<GraphicsMod> mGraphicsMods;
private ArrayList<PatchCheat> mPatchCheats; private ArrayList<PatchCheat> mPatchCheats;
private ArrayList<ARCheat> mARCheats; private ArrayList<ARCheat> mARCheats;
private ArrayList<GeckoCheat> mGeckoCheats; private ArrayList<GeckoCheat> mGeckoCheats;
private boolean mGraphicsModsNeedSaving = false;
private boolean mPatchCheatsNeedSaving = false; private boolean mPatchCheatsNeedSaving = false;
private boolean mARCheatsNeedSaving = false; private boolean mARCheatsNeedSaving = false;
private boolean mGeckoCheatsNeedSaving = false; private boolean mGeckoCheatsNeedSaving = false;
@ -37,13 +40,23 @@ public class CheatsViewModel extends ViewModel
if (mLoaded) if (mLoaded)
return; return;
mGraphicsModGroup = GraphicsModGroup.load(gameID);
mGraphicsMods = new ArrayList<>();
Collections.addAll(mGraphicsMods, mGraphicsModGroup.getMods());
mPatchCheats = new ArrayList<>(); mPatchCheats = new ArrayList<>();
Collections.addAll(mPatchCheats, PatchCheat.loadCodes(gameID, revision)); Collections.addAll(mPatchCheats, PatchCheat.loadCodes(gameID, revision));
mARCheats = new ArrayList<>(); mARCheats = new ArrayList<>();
Collections.addAll(mARCheats, ARCheat.loadCodes(gameID, revision)); Collections.addAll(mARCheats, ARCheat.loadCodes(gameID, revision));
mGeckoCheats = new ArrayList<>(); mGeckoCheats = new ArrayList<>();
Collections.addAll(mGeckoCheats, GeckoCheat.loadCodes(gameID, revision)); Collections.addAll(mGeckoCheats, GeckoCheat.loadCodes(gameID, revision));
for (GraphicsMod mod : mGraphicsMods)
{
mod.setChangedCallback(() -> mGraphicsModsNeedSaving = true);
}
for (PatchCheat cheat : mPatchCheats) for (PatchCheat cheat : mPatchCheats)
{ {
cheat.setChangedCallback(() -> mPatchCheatsNeedSaving = true); cheat.setChangedCallback(() -> mPatchCheatsNeedSaving = true);
@ -62,6 +75,12 @@ public class CheatsViewModel extends ViewModel
public void saveIfNeeded(String gameID, int revision) public void saveIfNeeded(String gameID, int revision)
{ {
if (mGraphicsModsNeedSaving)
{
mGraphicsModGroup.save();
mGraphicsModsNeedSaving = false;
}
if (mPatchCheatsNeedSaving) if (mPatchCheatsNeedSaving)
{ {
PatchCheat.saveCodes(gameID, revision, mPatchCheats.toArray(new PatchCheat[0])); PatchCheat.saveCodes(gameID, revision, mPatchCheats.toArray(new PatchCheat[0]));
@ -280,6 +299,11 @@ public class CheatsViewModel extends ViewModel
mOpenDetailsViewEvent.setValue(false); mOpenDetailsViewEvent.setValue(false);
} }
public ArrayList<GraphicsMod> getGraphicsMods()
{
return mGraphicsMods;
}
public ArrayList<PatchCheat> getPatchCheats() public ArrayList<PatchCheat> getPatchCheats()
{ {
return mPatchCheats; return mPatchCheats;

View file

@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.cheats.model;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class GraphicsMod extends ReadOnlyCheat
{
@Keep
private final long mPointer;
// When a C++ GraphicsModGroup object is destroyed, it also destroys the GraphicsMods it owns.
// To avoid getting dangling pointers, we keep a reference to the GraphicsModGroup here.
@Keep
private final GraphicsModGroup mParent;
@Keep
private GraphicsMod(long pointer, GraphicsModGroup parent)
{
mPointer = pointer;
mParent = parent;
}
public boolean supportsCreator()
{
return true;
}
public boolean supportsNotes()
{
return true;
}
public boolean supportsCode()
{
return false;
}
@NonNull
public native String getName();
@NonNull
public native String getCreator();
@NonNull
public native String getNotes();
public boolean getUserDefined()
{
// Technically graphics mods can be user defined, but we don't support editing graphics mods
// in the GUI, and editability is what this really controls
return false;
}
public native boolean getEnabled();
@Override
protected native void setEnabledImpl(boolean enabled);
}

View file

@ -0,0 +1,27 @@
package org.dolphinemu.dolphinemu.features.cheats.model;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
public class GraphicsModGroup
{
@Keep
private final long mPointer;
@Keep
private GraphicsModGroup(long pointer)
{
mPointer = pointer;
}
@Override
public native void finalize();
@NonNull
public native GraphicsMod[] getMods();
public native void save();
@NonNull
public static native GraphicsModGroup load(String gameId);
}

View file

@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.cheats.model;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public abstract class ReadOnlyCheat implements Cheat
{
private Runnable mChangedCallback = null;
public int trySet(@NonNull String name, @NonNull String creator, @NonNull String notes,
@NonNull String code)
{
throw new UnsupportedOperationException();
}
public void setEnabled(boolean enabled)
{
setEnabledImpl(enabled);
onChanged();
}
public void setChangedCallback(@Nullable Runnable callback)
{
mChangedCallback = callback;
}
protected void onChanged()
{
if (mChangedCallback != null)
mChangedCallback.run();
}
protected abstract void setEnabledImpl(boolean enabled);
}

View file

@ -31,6 +31,7 @@ public class CheatDetailsFragment extends Fragment
private EditText mEditCreator; private EditText mEditCreator;
private TextView mLabelNotes; private TextView mLabelNotes;
private EditText mEditNotes; private EditText mEditNotes;
private TextView mLabelCode;
private EditText mEditCode; private EditText mEditCode;
private Button mButtonDelete; private Button mButtonDelete;
private Button mButtonEdit; private Button mButtonEdit;
@ -59,6 +60,7 @@ public class CheatDetailsFragment extends Fragment
mEditCreator = view.findViewById(R.id.edit_creator); mEditCreator = view.findViewById(R.id.edit_creator);
mLabelNotes = view.findViewById(R.id.label_notes); mLabelNotes = view.findViewById(R.id.label_notes);
mEditNotes = view.findViewById(R.id.edit_notes); mEditNotes = view.findViewById(R.id.edit_notes);
mLabelCode = view.findViewById(R.id.label_code);
mEditCode = view.findViewById(R.id.edit_code); mEditCode = view.findViewById(R.id.edit_code);
mButtonDelete = view.findViewById(R.id.button_delete); mButtonDelete = view.findViewById(R.id.button_delete);
mButtonEdit = view.findViewById(R.id.button_edit); mButtonEdit = view.findViewById(R.id.button_edit);
@ -158,10 +160,13 @@ public class CheatDetailsFragment extends Fragment
int creatorVisibility = cheat != null && cheat.supportsCreator() ? View.VISIBLE : View.GONE; int creatorVisibility = cheat != null && cheat.supportsCreator() ? View.VISIBLE : View.GONE;
int notesVisibility = cheat != null && cheat.supportsNotes() ? View.VISIBLE : View.GONE; int notesVisibility = cheat != null && cheat.supportsNotes() ? View.VISIBLE : View.GONE;
int codeVisibility = cheat != null && cheat.supportsCode() ? View.VISIBLE : View.GONE;
mLabelCreator.setVisibility(creatorVisibility); mLabelCreator.setVisibility(creatorVisibility);
mEditCreator.setVisibility(creatorVisibility); mEditCreator.setVisibility(creatorVisibility);
mLabelNotes.setVisibility(notesVisibility); mLabelNotes.setVisibility(notesVisibility);
mEditNotes.setVisibility(notesVisibility); mEditNotes.setVisibility(notesVisibility);
mLabelCode.setVisibility(codeVisibility);
mEditCode.setVisibility(codeVisibility);
boolean userDefined = cheat != null && cheat.getUserDefined(); boolean userDefined = cheat != null && cheat.getUserDefined();
mButtonDelete.setEnabled(userDefined); mButtonDelete.setEnabled(userDefined);

View file

@ -13,6 +13,7 @@ import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.cheats.model.ARCheat; import org.dolphinemu.dolphinemu.features.cheats.model.ARCheat;
import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel; import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel;
import org.dolphinemu.dolphinemu.features.cheats.model.GeckoCheat; import org.dolphinemu.dolphinemu.features.cheats.model.GeckoCheat;
import org.dolphinemu.dolphinemu.features.cheats.model.GraphicsMod;
import org.dolphinemu.dolphinemu.features.cheats.model.PatchCheat; import org.dolphinemu.dolphinemu.features.cheats.model.PatchCheat;
import java.util.ArrayList; import java.util.ArrayList;
@ -90,8 +91,8 @@ public class CheatsAdapter extends RecyclerView.Adapter<CheatItemViewHolder>
@Override @Override
public int getItemCount() public int getItemCount()
{ {
return mViewModel.getARCheats().size() + mViewModel.getGeckoCheats().size() + return mViewModel.getGraphicsMods().size() + mViewModel.getPatchCheats().size() +
mViewModel.getPatchCheats().size() + 7; mViewModel.getARCheats().size() + mViewModel.getGeckoCheats().size() + 8;
} }
@Override @Override
@ -108,6 +109,17 @@ public class CheatsAdapter extends RecyclerView.Adapter<CheatItemViewHolder>
private CheatItem getItemAt(int position) private CheatItem getItemAt(int position)
{ {
// Graphics mods
if (position == 0)
return new CheatItem(CheatItem.TYPE_HEADER, R.string.cheats_header_graphics_mod);
position -= 1;
ArrayList<GraphicsMod> graphicsMods = mViewModel.getGraphicsMods();
if (position < graphicsMods.size())
return new CheatItem(graphicsMods.get(position));
position -= graphicsMods.size();
// Patches // Patches
if (position == 0) if (position == 0)

View file

@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.cheats.ui;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
public class CheatsDisabledWarningFragment extends SettingDisabledWarningFragment
{
public CheatsDisabledWarningFragment()
{
super(BooleanSetting.MAIN_ENABLE_CHEATS, MenuTag.CONFIG_GENERAL,
R.string.cheats_disabled_warning);
}
}

View file

@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.cheats.ui;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
public class GraphicsModsDisabledWarningFragment extends SettingDisabledWarningFragment
{
public GraphicsModsDisabledWarningFragment()
{
super(BooleanSetting.GFX_MODS_ENABLE, MenuTag.ADVANCED_GRAPHICS,
R.string.gfx_mods_disabled_warning);
}
}

View file

@ -7,21 +7,35 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
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.AbstractBooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity;
public class CheatWarningFragment extends Fragment implements View.OnClickListener public abstract class SettingDisabledWarningFragment extends Fragment
implements View.OnClickListener
{ {
private View mView; private View mView;
private final AbstractBooleanSetting mSetting;
private final MenuTag mSettingShortcut;
private final int mText;
public SettingDisabledWarningFragment(AbstractBooleanSetting setting, MenuTag settingShortcut,
int text)
{
mSetting = setting;
mSettingShortcut = settingShortcut;
mText = text;
}
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@ -35,6 +49,9 @@ public class CheatWarningFragment extends Fragment implements View.OnClickListen
{ {
mView = view; mView = view;
TextView textView = view.findViewById(R.id.text_warning);
textView.setText(mText);
Button settingsButton = view.findViewById(R.id.button_settings); Button settingsButton = view.findViewById(R.id.button_settings);
settingsButton.setOnClickListener(this); settingsButton.setOnClickListener(this);
@ -51,13 +68,13 @@ public class CheatWarningFragment extends Fragment implements View.OnClickListen
CheatsActivity activity = (CheatsActivity) requireActivity(); CheatsActivity activity = (CheatsActivity) requireActivity();
try (Settings settings = activity.loadGameSpecificSettings()) try (Settings settings = activity.loadGameSpecificSettings())
{ {
boolean cheatsEnabled = BooleanSetting.MAIN_ENABLE_CHEATS.getBoolean(settings); boolean cheatsEnabled = mSetting.getBoolean(settings);
mView.setVisibility(cheatsEnabled ? View.GONE : View.VISIBLE); mView.setVisibility(cheatsEnabled ? View.GONE : View.VISIBLE);
} }
} }
public void onClick(View view) public void onClick(View view)
{ {
SettingsActivity.launch(requireContext(), MenuTag.CONFIG_GENERAL); SettingsActivity.launch(requireContext(), mSettingShortcut);
} }
} }

View file

@ -200,6 +200,7 @@ public enum BooleanSetting implements AbstractBooleanSetting
"WaitForShadersBeforeStarting", false), "WaitForShadersBeforeStarting", false),
GFX_SAVE_TEXTURE_CACHE_TO_STATE(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, GFX_SAVE_TEXTURE_CACHE_TO_STATE(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS,
"SaveTextureCacheToState", true), "SaveTextureCacheToState", true),
GFX_MODS_ENABLE(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "EnableMods", false),
GFX_ENHANCE_FORCE_FILTERING(Settings.FILE_GFX, Settings.SECTION_GFX_ENHANCEMENTS, GFX_ENHANCE_FORCE_FILTERING(Settings.FILE_GFX, Settings.SECTION_GFX_ENHANCEMENTS,
"ForceFiltering", false), "ForceFiltering", false),

View file

@ -756,7 +756,9 @@ public final class SettingsFragmentPresenter
private void addAdvancedGraphicsSettings(ArrayList<SettingsItem> sl) private void addAdvancedGraphicsSettings(ArrayList<SettingsItem> sl)
{ {
sl.add(new HeaderSetting(mContext, R.string.custom_textures, 0)); sl.add(new HeaderSetting(mContext, R.string.gfx_mods_and_custom_textures, 0));
sl.add(new CheckBoxSetting(mContext, BooleanSetting.GFX_MODS_ENABLE,
R.string.gfx_mods, R.string.gfx_mods_description));
sl.add(new CheckBoxSetting(mContext, BooleanSetting.GFX_HIRES_TEXTURES, sl.add(new CheckBoxSetting(mContext, BooleanSetting.GFX_HIRES_TEXTURES,
R.string.load_custom_texture, R.string.load_custom_texture_description)); R.string.load_custom_texture, R.string.load_custom_texture_description));
sl.add(new CheckBoxSetting(mContext, BooleanSetting.GFX_CACHE_HIRES_TEXTURES, sl.add(new CheckBoxSetting(mContext, BooleanSetting.GFX_CACHE_HIRES_TEXTURES,

View file

@ -6,13 +6,23 @@
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView <androidx.fragment.app.FragmentContainerView
android:id="@+id/cheat_warning" android:id="@+id/cheats_warning"
android:name="org.dolphinemu.dolphinemu.features.cheats.ui.CheatWarningFragment" android:name="org.dolphinemu.dolphinemu.features.cheats.ui.CheatsDisabledWarningFragment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/gfx_mods_warning" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/gfx_mods_warning"
android:name="org.dolphinemu.dolphinemu.features.cheats.ui.GraphicsModsDisabledWarningFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/cheats_warning"
app:layout_constraintBottom_toTopOf="@id/cheat_list" /> app:layout_constraintBottom_toTopOf="@id/cheat_list" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
@ -21,7 +31,7 @@
android:layout_height="0dp" android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/cheat_warning" app:layout_constraintTop_toBottomOf="@id/gfx_mods_warning"
app:layout_constraintBottom_toBottomOf="parent" /> app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -10,7 +11,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_margin="@dimen/spacing_large" android:layout_margin="@dimen/spacing_large"
android:text="@string/cheats_disabled_warning" tools:text="@string/cheats_disabled_warning"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/button_settings" app:layout_constraintEnd_toStartOf="@id/button_settings"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"

View file

@ -319,7 +319,9 @@
<string name="wait_for_shaders_description">This causes a delay when launching games, but will reduce stuttering early on.</string> <string name="wait_for_shaders_description">This causes a delay when launching games, but will reduce stuttering early on.</string>
<string name="advanced_graphics_submenu">Advanced</string> <string name="advanced_graphics_submenu">Advanced</string>
<string name="custom_textures">Custom Textures</string> <string name="gfx_mods_and_custom_textures">Graphics Mods and Custom Textures</string>
<string name="gfx_mods">Graphics Mods</string>
<string name="gfx_mods_description">Loads graphics mods from User/Load/GraphicsMods/.</string>
<string name="load_custom_texture">Load Custom Textures</string> <string name="load_custom_texture">Load Custom Textures</string>
<string name="load_custom_texture_description">Loads custom textures from User/Load/Textures/&lt;game_id&gt;/ and User/Load/DynamicInputTextures/&lt;game_id&gt;/.</string> <string name="load_custom_texture_description">Loads custom textures from User/Load/Textures/&lt;game_id&gt;/ and User/Load/DynamicInputTextures/&lt;game_id&gt;/.</string>
<string name="cache_custom_texture">Prefetch Custom Textures</string> <string name="cache_custom_texture">Prefetch Custom Textures</string>
@ -488,6 +490,7 @@
<string name="cheats_header_ar">AR Codes</string> <string name="cheats_header_ar">AR Codes</string>
<string name="cheats_header_gecko">Gecko Codes</string> <string name="cheats_header_gecko">Gecko Codes</string>
<string name="cheats_header_patch">Patches</string> <string name="cheats_header_patch">Patches</string>
<string name="cheats_header_graphics_mod">Graphics Mods</string>
<string name="cheats_add_ar">Add New AR Code</string> <string name="cheats_add_ar">Add New AR Code</string>
<string name="cheats_add_gecko">Add New Gecko Code</string> <string name="cheats_add_gecko">Add New Gecko Code</string>
<string name="cheats_add_patch">Add New Patch</string> <string name="cheats_add_patch">Add New Patch</string>
@ -508,6 +511,7 @@
<string name="cheats_download_empty">File contained no codes.</string> <string name="cheats_download_empty">File contained no codes.</string>
<string name="cheats_download_succeeded">Downloaded %1$d codes. (added %2$d)</string> <string name="cheats_download_succeeded">Downloaded %1$d codes. (added %2$d)</string>
<string name="cheats_disabled_warning">Dolphin\'s cheat system is currently disabled.</string> <string name="cheats_disabled_warning">Dolphin\'s cheat system is currently disabled.</string>
<string name="gfx_mods_disabled_warning">Graphics mods are currently disabled.</string>
<string name="cheats_open_settings">Settings</string> <string name="cheats_open_settings">Settings</string>
<!-- Convert Screen --> <!-- Convert Screen -->

View file

@ -70,6 +70,14 @@ static jclass s_patch_cheat_class;
static jfieldID s_patch_cheat_pointer; static jfieldID s_patch_cheat_pointer;
static jmethodID s_patch_cheat_constructor; static jmethodID s_patch_cheat_constructor;
static jclass s_graphics_mod_group_class;
static jfieldID s_graphics_mod_group_pointer;
static jmethodID s_graphics_mod_group_constructor;
static jclass s_graphics_mod_class;
static jfieldID s_graphics_mod_pointer;
static jmethodID s_graphics_mod_constructor;
static jclass s_riivolution_patches_class; static jclass s_riivolution_patches_class;
static jfieldID s_riivolution_patches_pointer; static jfieldID s_riivolution_patches_pointer;
@ -331,6 +339,36 @@ jmethodID GetPatchCheatConstructor()
return s_patch_cheat_constructor; return s_patch_cheat_constructor;
} }
jclass GetGraphicsModClass()
{
return s_graphics_mod_class;
}
jfieldID GetGraphicsModPointer()
{
return s_graphics_mod_pointer;
}
jmethodID GetGraphicsModConstructor()
{
return s_graphics_mod_constructor;
}
jclass GetGraphicsModGroupClass()
{
return s_graphics_mod_group_class;
}
jfieldID GetGraphicsModGroupPointer()
{
return s_graphics_mod_group_pointer;
}
jmethodID GetGraphicsModGroupConstructor()
{
return s_graphics_mod_group_constructor;
}
jclass GetRiivolutionPatchesClass() jclass GetRiivolutionPatchesClass()
{ {
return s_riivolution_patches_class; return s_riivolution_patches_class;
@ -480,6 +518,23 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
s_patch_cheat_constructor = env->GetMethodID(patch_cheat_class, "<init>", "(J)V"); s_patch_cheat_constructor = env->GetMethodID(patch_cheat_class, "<init>", "(J)V");
env->DeleteLocalRef(patch_cheat_class); env->DeleteLocalRef(patch_cheat_class);
const jclass graphics_mod_group_class =
env->FindClass("org/dolphinemu/dolphinemu/features/cheats/model/GraphicsModGroup");
s_graphics_mod_group_class =
reinterpret_cast<jclass>(env->NewGlobalRef(graphics_mod_group_class));
s_graphics_mod_group_pointer = env->GetFieldID(graphics_mod_group_class, "mPointer", "J");
s_graphics_mod_group_constructor = env->GetMethodID(graphics_mod_group_class, "<init>", "(J)V");
env->DeleteLocalRef(graphics_mod_group_class);
const jclass graphics_mod_class =
env->FindClass("org/dolphinemu/dolphinemu/features/cheats/model/GraphicsMod");
s_graphics_mod_class = reinterpret_cast<jclass>(env->NewGlobalRef(graphics_mod_class));
s_graphics_mod_pointer = env->GetFieldID(graphics_mod_class, "mPointer", "J");
s_graphics_mod_constructor =
env->GetMethodID(graphics_mod_class, "<init>",
"(JLorg/dolphinemu/dolphinemu/features/cheats/model/GraphicsModGroup;)V");
env->DeleteLocalRef(graphics_mod_class);
const jclass riivolution_patches_class = const jclass riivolution_patches_class =
env->FindClass("org/dolphinemu/dolphinemu/features/riivolution/model/RiivolutionPatches"); env->FindClass("org/dolphinemu/dolphinemu/features/riivolution/model/RiivolutionPatches");
s_riivolution_patches_class = s_riivolution_patches_class =
@ -516,6 +571,8 @@ JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved)
env->DeleteGlobalRef(s_ar_cheat_class); env->DeleteGlobalRef(s_ar_cheat_class);
env->DeleteGlobalRef(s_gecko_cheat_class); env->DeleteGlobalRef(s_gecko_cheat_class);
env->DeleteGlobalRef(s_patch_cheat_class); env->DeleteGlobalRef(s_patch_cheat_class);
env->DeleteGlobalRef(s_graphics_mod_group_class);
env->DeleteGlobalRef(s_graphics_mod_class);
env->DeleteGlobalRef(s_riivolution_patches_class); env->DeleteGlobalRef(s_riivolution_patches_class);
env->DeleteGlobalRef(s_wii_update_cb_class); env->DeleteGlobalRef(s_wii_update_cb_class);
} }

View file

@ -69,6 +69,14 @@ jclass GetPatchCheatClass();
jfieldID GetPatchCheatPointer(); jfieldID GetPatchCheatPointer();
jmethodID GetPatchCheatConstructor(); jmethodID GetPatchCheatConstructor();
jclass GetGraphicsModGroupClass();
jfieldID GetGraphicsModGroupPointer();
jmethodID GetGraphicsModGroupConstructor();
jclass GetGraphicsModClass();
jfieldID GetGraphicsModPointer();
jmethodID GetGraphicsModConstructor();
jclass GetRiivolutionPatchesClass(); jclass GetRiivolutionPatchesClass();
jfieldID GetRiivolutionPatchesPointer(); jfieldID GetRiivolutionPatchesPointer();

View file

@ -2,6 +2,8 @@ add_library(main SHARED
Cheats/ARCheat.cpp Cheats/ARCheat.cpp
Cheats/Cheats.h Cheats/Cheats.h
Cheats/GeckoCheat.cpp Cheats/GeckoCheat.cpp
Cheats/GraphicsMod.cpp
Cheats/GraphicsModGroup.cpp
Cheats/PatchCheat.cpp Cheats/PatchCheat.cpp
Config/NativeConfig.cpp Config/NativeConfig.cpp
Config/PostProcessing.cpp Config/PostProcessing.cpp

View file

@ -0,0 +1,53 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string>
#include <jni.h>
#include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h"
#include "jni/AndroidCommon/AndroidCommon.h"
#include "jni/AndroidCommon/IDCache.h"
static GraphicsModConfig* GetPointer(JNIEnv* env, jobject obj)
{
return reinterpret_cast<GraphicsModConfig*>(
env->GetLongField(obj, IDCache::GetGraphicsModPointer()));
}
extern "C" {
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_GraphicsMod_getName(JNIEnv* env, jobject obj)
{
return ToJString(env, GetPointer(env, obj)->m_title);
}
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_GraphicsMod_getCreator(JNIEnv* env,
jobject obj)
{
return ToJString(env, GetPointer(env, obj)->m_author);
}
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_GraphicsMod_getNotes(JNIEnv* env, jobject obj)
{
return ToJString(env, GetPointer(env, obj)->m_description);
}
JNIEXPORT jboolean JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_GraphicsMod_getEnabled(JNIEnv* env,
jobject obj)
{
return static_cast<jboolean>(GetPointer(env, obj)->m_enabled);
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_GraphicsMod_setEnabledImpl(JNIEnv* env,
jobject obj,
jboolean enabled)
{
GetPointer(env, obj)->m_enabled = static_cast<bool>(enabled);
}
}

View file

@ -0,0 +1,92 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <jni.h>
#include <set>
#include <vector>
#include "VideoCommon/GraphicsModSystem/Config/GraphicsMod.h"
#include "VideoCommon/GraphicsModSystem/Config/GraphicsModGroup.h"
#include "jni/AndroidCommon/AndroidCommon.h"
#include "jni/AndroidCommon/IDCache.h"
static GraphicsModGroupConfig* GetPointer(JNIEnv* env, jobject obj)
{
return reinterpret_cast<GraphicsModGroupConfig*>(
env->GetLongField(obj, IDCache::GetGraphicsModGroupPointer()));
}
jobject GraphicsModToJava(JNIEnv* env, GraphicsModConfig* mod, jobject jGraphicsModGroup)
{
return env->NewObject(IDCache::GetGraphicsModClass(), IDCache::GetGraphicsModConstructor(),
reinterpret_cast<jlong>(mod), jGraphicsModGroup);
}
extern "C" {
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_GraphicsModGroup_finalize(JNIEnv* env,
jobject obj)
{
delete GetPointer(env, obj);
}
JNIEXPORT jobjectArray JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_GraphicsModGroup_getMods(JNIEnv* env,
jobject obj)
{
GraphicsModGroupConfig* mod_group = GetPointer(env, obj);
std::set<std::string> groups;
for (const GraphicsModConfig& mod : mod_group->GetMods())
{
for (const GraphicsTargetGroupConfig& group : mod.m_groups)
groups.insert(group.m_name);
}
std::vector<GraphicsModConfig*> mods;
for (GraphicsModConfig& mod : mod_group->GetMods())
{
// If no group matches the mod's features, or if the mod has no features, skip it
if (std::none_of(mod.m_features.begin(), mod.m_features.end(),
[&groups](const GraphicsModFeatureConfig& feature) {
return groups.count(feature.m_group) == 1;
}))
{
continue;
}
mods.push_back(&mod);
}
const jobjectArray array =
env->NewObjectArray(static_cast<jsize>(mods.size()), IDCache::GetGraphicsModClass(), nullptr);
jsize i = 0;
for (GraphicsModConfig* mod : mods)
env->SetObjectArrayElement(array, i++, GraphicsModToJava(env, mod, obj));
return array;
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_GraphicsModGroup_save(JNIEnv* env, jobject obj)
{
GetPointer(env, obj)->Save();
}
JNIEXPORT jobject JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_GraphicsModGroup_load(JNIEnv* env, jclass,
jstring jGameId)
{
auto* mod_group = new GraphicsModGroupConfig(GetJString(env, jGameId));
mod_group->Load();
return env->NewObject(IDCache::GetGraphicsModGroupClass(),
IDCache::GetGraphicsModGroupConstructor(), mod_group);
}
}

View file

@ -120,24 +120,15 @@ void GraphicsModListWidget::RefreshModList()
std::set<std::string> groups; std::set<std::string> groups;
for (const auto& mod : m_mod_group.GetMods()) for (const GraphicsModConfig& mod : m_mod_group.GetMods())
{
if (mod.m_groups.empty())
continue;
for (const auto& group : mod.m_groups)
{ {
for (const GraphicsTargetGroupConfig& group : mod.m_groups)
groups.insert(group.m_name); groups.insert(group.m_name);
} }
}
for (const auto& mod : m_mod_group.GetMods()) for (const GraphicsModConfig& mod : m_mod_group.GetMods())
{ {
// Group only mods shouldn't be shown // If no group matches the mod's features, or if the mod has no features, skip it
if (mod.m_features.empty())
continue;
// If the group doesn't exist in the available mod's features, skip
if (std::none_of(mod.m_features.begin(), mod.m_features.end(), if (std::none_of(mod.m_features.begin(), mod.m_features.end(),
[&groups](const GraphicsModFeatureConfig& feature) { [&groups](const GraphicsModFeatureConfig& feature) {
return groups.count(feature.m_group) == 1; return groups.count(feature.m_group) == 1;

View file

@ -168,6 +168,11 @@ const std::vector<GraphicsModConfig>& GraphicsModGroupConfig::GetMods() const
return m_graphics_mods; return m_graphics_mods;
} }
std::vector<GraphicsModConfig>& GraphicsModGroupConfig::GetMods()
{
return m_graphics_mods;
}
GraphicsModConfig* GraphicsModGroupConfig::GetMod(const std::string& absolute_path) const GraphicsModConfig* GraphicsModGroupConfig::GetMod(const std::string& absolute_path) const
{ {
if (const auto iter = m_path_to_graphics_mod.find(absolute_path); if (const auto iter = m_path_to_graphics_mod.find(absolute_path);

View file

@ -32,6 +32,7 @@ public:
u32 GetChangeCount() const; u32 GetChangeCount() const;
const std::vector<GraphicsModConfig>& GetMods() const; const std::vector<GraphicsModConfig>& GetMods() const;
std::vector<GraphicsModConfig>& GetMods();
GraphicsModConfig* GetMod(const std::string& absolute_path) const; GraphicsModConfig* GetMod(const std::string& absolute_path) const;