@@ -7,21 +7,35 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

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.ui.MenuTag;
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 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
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -35,6 +49,9 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
{
mView = view;

TextView textView = view.findViewById(R.id.text_warning);
textView.setText(mText);

Button settingsButton = view.findViewById(R.id.button_settings);
settingsButton.setOnClickListener(this);

@@ -51,13 +68,13 @@ public void onResume()
CheatsActivity activity = (CheatsActivity) requireActivity();
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);
}
}

public void onClick(View view)
{
SettingsActivity.launch(requireContext(), MenuTag.CONFIG_GENERAL);
SettingsActivity.launch(requireContext(), mSettingShortcut);
}
}
@@ -200,6 +200,7 @@ public enum BooleanSetting implements AbstractBooleanSetting
"WaitForShadersBeforeStarting", false),
GFX_SAVE_TEXTURE_CACHE_TO_STATE(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS,
"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,
"ForceFiltering", false),
@@ -756,7 +756,9 @@ private void addHackSettings(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,
R.string.load_custom_texture, R.string.load_custom_texture_description));
sl.add(new CheckBoxSetting(mContext, BooleanSetting.GFX_CACHE_HIRES_TEXTURES,
@@ -6,13 +6,23 @@
android:layout_height="match_parent">

<androidx.fragment.app.FragmentContainerView
android:id="@+id/cheat_warning"
android:name="org.dolphinemu.dolphinemu.features.cheats.ui.CheatWarningFragment"
android:id="@+id/cheats_warning"
android:name="org.dolphinemu.dolphinemu.features.cheats.ui.CheatsDisabledWarningFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="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" />

<androidx.recyclerview.widget.RecyclerView
@@ -21,7 +31,7 @@
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="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" />

</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -10,7 +11,7 @@
android:layout_width="0dp"
android:layout_height="match_parent"
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_constraintEnd_toStartOf="@id/button_settings"
app:layout_constraintStart_toStartOf="parent"
@@ -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="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_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>
@@ -488,6 +490,7 @@
<string name="cheats_header_ar">AR Codes</string>
<string name="cheats_header_gecko">Gecko Codes</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_gecko">Add New Gecko Code</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_succeeded">Downloaded %1$d codes. (added %2$d)</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>

<!-- Convert Screen -->
@@ -70,6 +70,14 @@ static jclass s_patch_cheat_class;
static jfieldID s_patch_cheat_pointer;
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 jfieldID s_riivolution_patches_pointer;

@@ -331,6 +339,36 @@ jmethodID GetPatchCheatConstructor()
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()
{
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");
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 =
env->FindClass("org/dolphinemu/dolphinemu/features/riivolution/model/RiivolutionPatches");
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_gecko_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_wii_update_cb_class);
}
@@ -69,6 +69,14 @@ jclass GetPatchCheatClass();
jfieldID GetPatchCheatPointer();
jmethodID GetPatchCheatConstructor();

jclass GetGraphicsModGroupClass();
jfieldID GetGraphicsModGroupPointer();
jmethodID GetGraphicsModGroupConstructor();

jclass GetGraphicsModClass();
jfieldID GetGraphicsModPointer();
jmethodID GetGraphicsModConstructor();

jclass GetRiivolutionPatchesClass();
jfieldID GetRiivolutionPatchesPointer();

@@ -2,6 +2,8 @@ add_library(main SHARED
Cheats/ARCheat.cpp
Cheats/Cheats.h
Cheats/GeckoCheat.cpp
Cheats/GraphicsMod.cpp
Cheats/GraphicsModGroup.cpp
Cheats/PatchCheat.cpp
Config/NativeConfig.cpp
Config/PostProcessing.cpp
@@ -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);
}
}
@@ -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);
}
}
@@ -120,24 +120,15 @@ void GraphicsModListWidget::RefreshModList()

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);
}
}

for (const auto& mod : m_mod_group.GetMods())
for (const GraphicsModConfig& mod : m_mod_group.GetMods())
{
// Group only mods shouldn't be shown
if (mod.m_features.empty())
continue;

// If the group doesn't exist in the available mod's features, skip
// 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;
@@ -168,6 +168,11 @@ const std::vector<GraphicsModConfig>& GraphicsModGroupConfig::GetMods() const
return m_graphics_mods;
}

std::vector<GraphicsModConfig>& GraphicsModGroupConfig::GetMods()
{
return m_graphics_mods;
}

GraphicsModConfig* GraphicsModGroupConfig::GetMod(const std::string& absolute_path) const
{
if (const auto iter = m_path_to_graphics_mod.find(absolute_path);
@@ -32,6 +32,7 @@ class GraphicsModGroupConfig
u32 GetChangeCount() const;

const std::vector<GraphicsModConfig>& GetMods() const;
std::vector<GraphicsModConfig>& GetMods();

GraphicsModConfig* GetMod(const std::string& absolute_path) const;