Android: Allow starting game with Riivolution patches from the GUI

This commit is contained in:
JosJuice 2021-10-24 16:29:38 +02:00
parent 05b4aecf18
commit 34021b5ebc
16 changed files with 200 additions and 37 deletions

View File

@ -127,6 +127,11 @@
android:label="@string/user_data_submenu" android:label="@string/user_data_submenu"
android:theme="@style/DolphinSettingsBase" /> android:theme="@style/DolphinSettingsBase" />
<activity
android:name=".features.riivolution.ui.RiivolutionBootActivity"
android:exported="false"
android:theme="@style/DolphinBase" />
<service <service
android:name=".utils.DirectoryInitialization" android:name=".utils.DirectoryInitialization"
android:exported="false"/> android:exported="false"/>

View File

@ -379,12 +379,13 @@ public final class NativeLibrary
/** /**
* Begins emulation. * Begins emulation.
*/ */
public static native void Run(String[] path); public static native void Run(String[] path, boolean riivolution);
/** /**
* Begins emulation from the specified savestate. * Begins emulation from the specified savestate.
*/ */
public static native void Run(String[] path, String savestatePath, boolean deleteSavestate); public static native void Run(String[] path, boolean riivolution, String savestatePath,
boolean deleteSavestate);
public static native void ChangeDisc(String path); public static native void ChangeDisc(String path);

View File

@ -140,6 +140,6 @@ public class AppLinkActivity extends FragmentActivity
mAfterDirectoryInitializationRunner.cancel(); mAfterDirectoryInitializationRunner.cancel();
mAfterDirectoryInitializationRunner = null; mAfterDirectoryInitializationRunner = null;
} }
EmulationActivity.launch(this, GameFileCacheService.findSecondDiscAndGetPaths(game)); EmulationActivity.launch(this, GameFileCacheService.findSecondDiscAndGetPaths(game), false);
} }
} }

View File

@ -79,11 +79,13 @@ public final class EmulationActivity extends AppCompatActivity
private boolean activityRecreated; private boolean activityRecreated;
private String[] mPaths; private String[] mPaths;
private boolean mRiivolution;
private boolean mIgnoreWarnings; private boolean mIgnoreWarnings;
private static boolean sUserPausedEmulation; private static boolean sUserPausedEmulation;
private boolean mMenuToastShown; private boolean mMenuToastShown;
public static final String EXTRA_SELECTED_GAMES = "SelectedGames"; public static final String EXTRA_SELECTED_GAMES = "SelectedGames";
public static final String EXTRA_RIIVOLUTION = "Riivolution";
public static final String EXTRA_IGNORE_WARNINGS = "IgnoreWarnings"; public static final String EXTRA_IGNORE_WARNINGS = "IgnoreWarnings";
public static final String EXTRA_USER_PAUSED_EMULATION = "sUserPausedEmulation"; public static final String EXTRA_USER_PAUSED_EMULATION = "sUserPausedEmulation";
public static final String EXTRA_MENU_TOAST_SHOWN = "MenuToastShown"; public static final String EXTRA_MENU_TOAST_SHOWN = "MenuToastShown";
@ -164,12 +166,12 @@ public final class EmulationActivity extends AppCompatActivity
EmulationActivity.MENU_ACTION_MOTION_CONTROLS); EmulationActivity.MENU_ACTION_MOTION_CONTROLS);
} }
public static void launch(FragmentActivity activity, String filePath) public static void launch(FragmentActivity activity, String filePath, boolean riivolution)
{ {
launch(activity, new String[]{filePath}); launch(activity, new String[]{filePath}, riivolution);
} }
public static void launch(FragmentActivity activity, String[] filePaths) public static void launch(FragmentActivity activity, String[] filePaths, boolean riivolution)
{ {
if (sIgnoreLaunchRequests) if (sIgnoreLaunchRequests)
return; return;
@ -183,7 +185,7 @@ public final class EmulationActivity extends AppCompatActivity
FileBrowserHelper.isPathEmptyOrValid(StringSetting.MAIN_RESOURCEPACK_PATH) && FileBrowserHelper.isPathEmptyOrValid(StringSetting.MAIN_RESOURCEPACK_PATH) &&
FileBrowserHelper.isPathEmptyOrValid(StringSetting.MAIN_SD_PATH)) FileBrowserHelper.isPathEmptyOrValid(StringSetting.MAIN_SD_PATH))
{ {
launchWithoutChecks(activity, filePaths); launchWithoutChecks(activity, filePaths, riivolution);
} }
else else
{ {
@ -192,18 +194,20 @@ public final class EmulationActivity extends AppCompatActivity
builder.setPositiveButton(R.string.yes, (dialogInterface, i) -> builder.setPositiveButton(R.string.yes, (dialogInterface, i) ->
SettingsActivity.launch(activity, MenuTag.CONFIG_PATHS)); SettingsActivity.launch(activity, MenuTag.CONFIG_PATHS));
builder.setNeutralButton(R.string.continue_anyway, (dialogInterface, i) -> builder.setNeutralButton(R.string.continue_anyway, (dialogInterface, i) ->
launchWithoutChecks(activity, filePaths)); launchWithoutChecks(activity, filePaths, riivolution));
builder.show(); builder.show();
} }
}); });
} }
private static void launchWithoutChecks(FragmentActivity activity, String[] filePaths) private static void launchWithoutChecks(FragmentActivity activity, String[] filePaths,
boolean riivolution)
{ {
sIgnoreLaunchRequests = true; sIgnoreLaunchRequests = true;
Intent launcher = new Intent(activity, EmulationActivity.class); Intent launcher = new Intent(activity, EmulationActivity.class);
launcher.putExtra(EXTRA_SELECTED_GAMES, filePaths); launcher.putExtra(EXTRA_SELECTED_GAMES, filePaths);
launcher.putExtra(EXTRA_RIIVOLUTION, riivolution);
activity.startActivity(launcher); activity.startActivity(launcher);
} }
@ -251,6 +255,7 @@ public final class EmulationActivity extends AppCompatActivity
// Get params we were passed // Get params we were passed
Intent gameToEmulate = getIntent(); Intent gameToEmulate = getIntent();
mPaths = gameToEmulate.getStringArrayExtra(EXTRA_SELECTED_GAMES); mPaths = gameToEmulate.getStringArrayExtra(EXTRA_SELECTED_GAMES);
mRiivolution = gameToEmulate.getBooleanExtra(EXTRA_RIIVOLUTION, false);
mIgnoreWarnings = gameToEmulate.getBooleanExtra(EXTRA_IGNORE_WARNINGS, false); mIgnoreWarnings = gameToEmulate.getBooleanExtra(EXTRA_IGNORE_WARNINGS, false);
sUserPausedEmulation = gameToEmulate.getBooleanExtra(EXTRA_USER_PAUSED_EMULATION, false); sUserPausedEmulation = gameToEmulate.getBooleanExtra(EXTRA_USER_PAUSED_EMULATION, false);
mMenuToastShown = false; mMenuToastShown = false;
@ -283,7 +288,7 @@ public final class EmulationActivity extends AppCompatActivity
.findFragmentById(R.id.frame_emulation_fragment); .findFragmentById(R.id.frame_emulation_fragment);
if (mEmulationFragment == null) if (mEmulationFragment == null)
{ {
mEmulationFragment = EmulationFragment.newInstance(mPaths); mEmulationFragment = EmulationFragment.newInstance(mPaths, mRiivolution);
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.add(R.id.frame_emulation_fragment, mEmulationFragment) .add(R.id.frame_emulation_fragment, mEmulationFragment)
.commit(); .commit();

View File

@ -141,7 +141,7 @@ public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> impl
GameViewHolder holder = (GameViewHolder) view.getTag(); GameViewHolder holder = (GameViewHolder) view.getTag();
String[] paths = GameFileCacheService.findSecondDiscAndGetPaths(holder.gameFile); String[] paths = GameFileCacheService.findSecondDiscAndGetPaths(holder.gameFile);
EmulationActivity.launch((FragmentActivity) view.getContext(), paths); EmulationActivity.launch((FragmentActivity) view.getContext(), paths, false);
} }
/** /**

View File

@ -15,6 +15,7 @@ import org.dolphinemu.dolphinemu.DolphinApplication;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.ConvertActivity; import org.dolphinemu.dolphinemu.activities.ConvertActivity;
import org.dolphinemu.dolphinemu.features.cheats.ui.CheatsActivity; import org.dolphinemu.dolphinemu.features.cheats.ui.CheatsActivity;
import org.dolphinemu.dolphinemu.features.riivolution.ui.RiivolutionBootActivity;
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.StringSetting;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
@ -34,6 +35,7 @@ public class GamePropertiesDialog extends DialogFragment
private static final String ARG_GAME_ID = "game_id"; private static final String ARG_GAME_ID = "game_id";
private static final String ARG_GAMETDB_ID = "gametdb_id"; private static final String ARG_GAMETDB_ID = "gametdb_id";
public static final String ARG_REVISION = "revision"; public static final String ARG_REVISION = "revision";
public static final String ARG_DISC_NUMBER = "disc_number";
private static final String ARG_PLATFORM = "platform"; private static final String ARG_PLATFORM = "platform";
private static final String ARG_SHOULD_ALLOW_CONVERSION = "should_allow_conversion"; private static final String ARG_SHOULD_ALLOW_CONVERSION = "should_allow_conversion";
@ -46,6 +48,7 @@ public class GamePropertiesDialog extends DialogFragment
arguments.putString(ARG_GAME_ID, gameFile.getGameId()); arguments.putString(ARG_GAME_ID, gameFile.getGameId());
arguments.putString(ARG_GAMETDB_ID, gameFile.getGameTdbId()); arguments.putString(ARG_GAMETDB_ID, gameFile.getGameTdbId());
arguments.putInt(ARG_REVISION, gameFile.getRevision()); arguments.putInt(ARG_REVISION, gameFile.getRevision());
arguments.putInt(ARG_DISC_NUMBER, gameFile.getDiscNumber());
arguments.putInt(ARG_PLATFORM, gameFile.getPlatform()); arguments.putInt(ARG_PLATFORM, gameFile.getPlatform());
arguments.putBoolean(ARG_SHOULD_ALLOW_CONVERSION, gameFile.shouldAllowConversion()); arguments.putBoolean(ARG_SHOULD_ALLOW_CONVERSION, gameFile.shouldAllowConversion());
fragment.setArguments(arguments); fragment.setArguments(arguments);
@ -61,6 +64,7 @@ public class GamePropertiesDialog extends DialogFragment
final String gameId = requireArguments().getString(ARG_GAME_ID); final String gameId = requireArguments().getString(ARG_GAME_ID);
final String gameTdbId = requireArguments().getString(ARG_GAMETDB_ID); final String gameTdbId = requireArguments().getString(ARG_GAMETDB_ID);
final int revision = requireArguments().getInt(ARG_REVISION); final int revision = requireArguments().getInt(ARG_REVISION);
final int discNumber = requireArguments().getInt(ARG_DISC_NUMBER);
final int platform = requireArguments().getInt(ARG_PLATFORM); final int platform = requireArguments().getInt(ARG_PLATFORM);
final boolean shouldAllowConversion = final boolean shouldAllowConversion =
requireArguments().getBoolean(ARG_SHOULD_ALLOW_CONVERSION); requireArguments().getBoolean(ARG_SHOULD_ALLOW_CONVERSION);
@ -75,14 +79,11 @@ public class GamePropertiesDialog extends DialogFragment
GameDetailsDialog.newInstance(path).show(requireActivity() GameDetailsDialog.newInstance(path).show(requireActivity()
.getSupportFragmentManager(), "game_details")); .getSupportFragmentManager(), "game_details"));
if (shouldAllowConversion)
{
itemsBuilder.add(R.string.properties_convert, (dialog, i) ->
ConvertActivity.launch(getContext(), path));
}
if (isDisc) if (isDisc)
{ {
itemsBuilder.add(R.string.properties_start_with_riivolution, (dialog, i) ->
RiivolutionBootActivity.launch(getContext(), path, gameId, revision, discNumber));
itemsBuilder.add(R.string.properties_set_default_iso, (dialog, i) -> itemsBuilder.add(R.string.properties_set_default_iso, (dialog, i) ->
{ {
try (Settings settings = new Settings()) try (Settings settings = new Settings())
@ -94,6 +95,12 @@ public class GamePropertiesDialog extends DialogFragment
}); });
} }
if (shouldAllowConversion)
{
itemsBuilder.add(R.string.properties_convert, (dialog, i) ->
ConvertActivity.launch(getContext(), path));
}
itemsBuilder.add(R.string.properties_edit_game_settings, (dialog, i) -> itemsBuilder.add(R.string.properties_edit_game_settings, (dialog, i) ->
SettingsActivity.launch(getContext(), MenuTag.SETTINGS, gameId, revision, isWii)); SettingsActivity.launch(getContext(), MenuTag.SETTINGS, gameId, revision, isWii));

View File

@ -0,0 +1,50 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.riivolution.ui;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
public class RiivolutionBootActivity extends AppCompatActivity
{
private static final String ARG_GAME_PATH = "game_path";
private static final String ARG_GAME_ID = "game_id";
private static final String ARG_REVISION = "revision";
private static final String ARG_DISC_NUMBER = "disc_number";
public static void launch(Context context, String gamePath, String gameId, int revision,
int discNumber)
{
Intent launcher = new Intent(context, RiivolutionBootActivity.class);
launcher.putExtra(ARG_GAME_PATH, gamePath);
launcher.putExtra(ARG_GAME_ID, gameId);
launcher.putExtra(ARG_REVISION, revision);
launcher.putExtra(ARG_DISC_NUMBER, discNumber);
context.startActivity(launcher);
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_riivolution_boot);
Intent intent = getIntent();
String path = getIntent().getStringExtra(ARG_GAME_PATH);
String gameId = intent.getStringExtra(ARG_GAME_ID);
int revision = intent.getIntExtra(ARG_REVISION, -1);
int discNumber = intent.getIntExtra(ARG_DISC_NUMBER, -1);
Button buttonStart = findViewById(R.id.button_start);
buttonStart.setOnClickListener((v) -> EmulationActivity.launch(this, path, true));
}
}

View File

@ -29,19 +29,22 @@ import java.io.File;
public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback
{ {
private static final String KEY_GAMEPATHS = "gamepaths"; private static final String KEY_GAMEPATHS = "gamepaths";
private static final String KEY_RIIVOLUTION = "riivolution";
private InputOverlay mInputOverlay; private InputOverlay mInputOverlay;
private String[] mGamePaths; private String[] mGamePaths;
private boolean mRiivolution;
private boolean mRunWhenSurfaceIsValid; private boolean mRunWhenSurfaceIsValid;
private boolean mLoadPreviousTemporaryState; private boolean mLoadPreviousTemporaryState;
private EmulationActivity activity; private EmulationActivity activity;
public static EmulationFragment newInstance(String[] gamePaths) public static EmulationFragment newInstance(String[] gamePaths, boolean riivolution)
{ {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putStringArray(KEY_GAMEPATHS, gamePaths); args.putStringArray(KEY_GAMEPATHS, gamePaths);
args.putBoolean(KEY_RIIVOLUTION, riivolution);
EmulationFragment fragment = new EmulationFragment(); EmulationFragment fragment = new EmulationFragment();
fragment.setArguments(args); fragment.setArguments(args);
@ -76,6 +79,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
setRetainInstance(true); setRetainInstance(true);
mGamePaths = getArguments().getStringArray(KEY_GAMEPATHS); mGamePaths = getArguments().getStringArray(KEY_GAMEPATHS);
mRiivolution = getArguments().getBoolean(KEY_RIIVOLUTION);
} }
/** /**
@ -267,12 +271,12 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
if (mLoadPreviousTemporaryState) if (mLoadPreviousTemporaryState)
{ {
Log.debug("[EmulationFragment] Starting emulation thread from previous state."); Log.debug("[EmulationFragment] Starting emulation thread from previous state.");
NativeLibrary.Run(mGamePaths, getTemporaryStateFilePath(), true); NativeLibrary.Run(mGamePaths, mRiivolution, getTemporaryStateFilePath(), true);
} }
else else
{ {
Log.debug("[EmulationFragment] Starting emulation thread."); Log.debug("[EmulationFragment] Starting emulation thread.");
NativeLibrary.Run(mGamePaths); NativeLibrary.Run(mGamePaths, mRiivolution);
} }
EmulationActivity.stopIgnoringLaunchRequests(); EmulationActivity.stopIgnoringLaunchRequests();
}, "NativeEmulation"); }, "NativeEmulation");

View File

@ -216,7 +216,7 @@ public final class MainActivity extends AppCompatActivity
case MainPresenter.REQUEST_GAME_FILE: case MainPresenter.REQUEST_GAME_FILE:
FileBrowserHelper.runAfterExtensionCheck(this, uri, FileBrowserHelper.runAfterExtensionCheck(this, uri,
FileBrowserHelper.GAME_LIKE_EXTENSIONS, FileBrowserHelper.GAME_LIKE_EXTENSIONS,
() -> EmulationActivity.launch(this, result.getData().toString())); () -> EmulationActivity.launch(this, result.getData().toString(), false));
break; break;
case MainPresenter.REQUEST_WAD_FILE: case MainPresenter.REQUEST_WAD_FILE:

View File

@ -153,7 +153,7 @@ public final class TvMainActivity extends FragmentActivity
// Start the emulation activity and send the path of the clicked ISO to it. // Start the emulation activity and send the path of the clicked ISO to it.
String[] paths = GameFileCacheService.findSecondDiscAndGetPaths(holder.gameFile); String[] paths = GameFileCacheService.findSecondDiscAndGetPaths(holder.gameFile);
EmulationActivity.launch(TvMainActivity.this, paths); EmulationActivity.launch(TvMainActivity.this, paths, false);
} }
}); });
} }
@ -255,7 +255,7 @@ public final class TvMainActivity extends FragmentActivity
case MainPresenter.REQUEST_GAME_FILE: case MainPresenter.REQUEST_GAME_FILE:
FileBrowserHelper.runAfterExtensionCheck(this, uri, FileBrowserHelper.runAfterExtensionCheck(this, uri,
FileBrowserHelper.GAME_LIKE_EXTENSIONS, FileBrowserHelper.GAME_LIKE_EXTENSIONS,
() -> EmulationActivity.launch(this, result.getData().toString())); () -> EmulationActivity.launch(this, result.getData().toString(), false));
break; break;
case MainPresenter.REQUEST_WAD_FILE: case MainPresenter.REQUEST_WAD_FILE:

View File

@ -51,7 +51,7 @@ public final class StartupHandler
if (start_files != null && start_files.length > 0) if (start_files != null && start_files.length > 0)
{ {
// Start the emulation activity, send the ISO passed in and finish the main activity // Start the emulation activity, send the ISO passed in and finish the main activity
EmulationActivity.launch(parent, start_files); EmulationActivity.launch(parent, start_files, false);
parent.finish(); parent.finish();
} }
} }

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/button_start">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp" />
</ScrollView>
<Button
android:id="@+id/button_start"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/spacing_large"
android:text="@string/riivolution_start"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/scroll_view"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -399,8 +399,9 @@
<!-- Game Properties Screen --> <!-- Game Properties Screen -->
<string name="properties_details">Details</string> <string name="properties_details">Details</string>
<string name="properties_convert">Convert File</string> <string name="properties_start_with_riivolution">Start with Riivolution Patches</string>
<string name="properties_set_default_iso">Set as Default ISO</string> <string name="properties_set_default_iso">Set as Default ISO</string>
<string name="properties_convert">Convert File</string>
<string name="properties_edit_game_settings">Edit Game Settings</string> <string name="properties_edit_game_settings">Edit Game Settings</string>
<string name="properties_edit_cheats">Edit Cheats</string> <string name="properties_edit_cheats">Edit Cheats</string>
<string name="properties_clear_game_settings">Clear Game Settings and Cheats</string> <string name="properties_clear_game_settings">Clear Game Settings and Cheats</string>
@ -480,6 +481,9 @@ and a few other programs. It can efficiently compress encrypted Wii data, but no
It can efficiently compress both junk data and encrypted Wii data. It can efficiently compress both junk data and encrypted Wii data.
</string> </string>
<!-- Riivolution Boot Screen -->
<string name="riivolution_start">Start</string>
<!-- Emulation Menu --> <!-- Emulation Menu -->
<string name="pause_emulation">Pause Emulation</string> <string name="pause_emulation">Pause Emulation</string>
<string name="unpause_emulation">Unpause Emulation</string> <string name="unpause_emulation">Unpause Emulation</string>

View File

@ -48,6 +48,7 @@
#include "DiscIO/Blob.h" #include "DiscIO/Blob.h"
#include "DiscIO/Enums.h" #include "DiscIO/Enums.h"
#include "DiscIO/RiivolutionParser.h"
#include "DiscIO/ScrubbedBlob.h" #include "DiscIO/ScrubbedBlob.h"
#include "DiscIO/Volume.h" #include "DiscIO/Volume.h"
@ -547,7 +548,7 @@ static float GetRenderSurfaceScale(JNIEnv* env)
return env->CallStaticFloatMethod(native_library_class, get_render_surface_scale_method); return env->CallStaticFloatMethod(native_library_class, get_render_surface_scale_method);
} }
static void Run(JNIEnv* env, const std::vector<std::string>& paths, static void Run(JNIEnv* env, const std::vector<std::string>& paths, bool riivolution,
const std::optional<std::string>& savestate_path = {}, const std::optional<std::string>& savestate_path = {},
bool delete_savestate = false) bool delete_savestate = false)
{ {
@ -564,6 +565,16 @@ static void Run(JNIEnv* env, const std::vector<std::string>& paths,
if (boot) if (boot)
boot->delete_savestate = delete_savestate; boot->delete_savestate = delete_savestate;
if (riivolution && std::holds_alternative<BootParameters::Disc>(boot->parameters))
{
const std::string& riivolution_dir = File::GetUserPath(D_RIIVOLUTION_IDX);
const DiscIO::Volume& volume = *std::get<BootParameters::Disc>(boot->parameters).volume;
AddRiivolutionPatches(boot.get(), DiscIO::Riivolution::GenerateRiivolutionPatchesFromConfig(
riivolution_dir, volume.GetGameID(), volume.GetRevision(),
volume.GetDiscNumber()));
}
WindowSystemInfo wsi(WindowSystemType::Android, nullptr, s_surf, s_surf); WindowSystemInfo wsi(WindowSystemType::Android, nullptr, s_surf, s_surf);
wsi.render_surface_scale = GetRenderSurfaceScale(env); wsi.render_surface_scale = GetRenderSurfaceScale(env);
@ -616,17 +627,19 @@ static void Run(JNIEnv* env, const std::vector<std::string>& paths,
IDCache::GetFinishEmulationActivity()); IDCache::GetFinishEmulationActivity());
} }
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run___3Ljava_lang_String_2( JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run___3Ljava_lang_String_2Z(
JNIEnv* env, jclass, jobjectArray jPaths) JNIEnv* env, jclass, jobjectArray jPaths, jboolean jRiivolution)
{ {
Run(env, JStringArrayToVector(env, jPaths)); Run(env, JStringArrayToVector(env, jPaths), jRiivolution);
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_NativeLibrary_Run___3Ljava_lang_String_2Ljava_lang_String_2Z( Java_org_dolphinemu_dolphinemu_NativeLibrary_Run___3Ljava_lang_String_2ZLjava_lang_String_2Z(
JNIEnv* env, jclass, jobjectArray jPaths, jstring jSavestate, jboolean jDeleteSavestate) JNIEnv* env, jclass, jobjectArray jPaths, jboolean jRiivolution, jstring jSavestate,
jboolean jDeleteSavestate)
{ {
Run(env, JStringArrayToVector(env, jPaths), GetJString(env, jSavestate), jDeleteSavestate); Run(env, JStringArrayToVector(env, jPaths), jRiivolution, GetJString(env, jSavestate),
jDeleteSavestate);
} }
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_ChangeDisc(JNIEnv* env, jclass, JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_ChangeDisc(JNIEnv* env, jclass,

View File

@ -9,8 +9,10 @@
#include <string_view> #include <string_view>
#include <vector> #include <vector>
#include <fmt/format.h>
#include <pugixml.hpp> #include <pugixml.hpp>
#include "Common/FileSearch.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/IOFile.h" #include "Common/IOFile.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
@ -354,7 +356,7 @@ std::vector<Patch> GenerateRiivolutionPatchesFromGameModDescriptor(
{ {
for (auto& option : section.m_options) for (auto& option : section.m_options)
{ {
const auto* info = [&]() -> const DiscIO::GameModDescriptorRiivolutionPatchOption* { const auto* info = [&]() -> const GameModDescriptorRiivolutionPatchOption* {
for (const auto& o : patch_info.options) for (const auto& o : patch_info.options)
{ {
if (o.section_name == section.m_name) if (o.section_name == section.m_name)
@ -374,14 +376,47 @@ std::vector<Patch> GenerateRiivolutionPatchesFromGameModDescriptor(
for (auto& p : parsed->GeneratePatches(game_id)) for (auto& p : parsed->GeneratePatches(game_id))
{ {
p.m_file_data_loader = std::make_shared<DiscIO::Riivolution::FileDataLoaderHostFS>( p.m_file_data_loader =
patch_info.root, parsed->m_xml_path, p.m_root); std::make_shared<FileDataLoaderHostFS>(patch_info.root, parsed->m_xml_path, p.m_root);
result.emplace_back(std::move(p)); result.emplace_back(std::move(p));
} }
} }
return result; return result;
} }
std::vector<Patch> GenerateRiivolutionPatchesFromConfig(const std::string root_directory,
const std::string& game_id,
std::optional<u16> revision,
std::optional<u8> disc_number)
{
std::vector<Patch> result;
// The way Riivolution stores settings only makes sense for standard game IDs.
if (!(game_id.size() == 4 || game_id.size() == 6))
return result;
const std::optional<Config> config = ParseConfigFile(
fmt::format("{}/riivolution/config/{}.xml", root_directory, game_id.substr(0, 4)));
for (const std::string& path : Common::DoFileSearch({root_directory + "riivolution"}, {".xml"}))
{
std::optional<Disc> parsed = ParseFile(path);
if (!parsed || !parsed->IsValidForGame(game_id, revision, disc_number))
continue;
if (config)
ApplyConfigDefaults(&*parsed, *config);
for (auto& patch : parsed->GeneratePatches(game_id))
{
patch.m_file_data_loader =
std::make_shared<FileDataLoaderHostFS>(root_directory, parsed->m_xml_path, patch.m_root);
result.emplace_back(std::move(patch));
}
}
return result;
}
std::optional<Config> ParseConfigFile(const std::string& filename) std::optional<Config> ParseConfigFile(const std::string& filename)
{ {
::File::IOFile f(filename, "rb"); ::File::IOFile f(filename, "rb");
@ -460,7 +495,7 @@ void ApplyConfigDefaults(Disc* disc, const Config& config)
{ {
for (const auto& config_option : config.m_options) for (const auto& config_option : config.m_options)
{ {
auto* matching_option = [&]() -> DiscIO::Riivolution::Option* { auto* matching_option = [&]() -> Option* {
for (auto& section : disc->m_sections) for (auto& section : disc->m_sections)
{ {
for (auto& option : section.m_options) for (auto& option : section.m_options)

View File

@ -143,7 +143,6 @@ struct Memory
std::vector<u8> m_original; std::vector<u8> m_original;
// If true, this memory patch is an ocarina-style patch. // If true, this memory patch is an ocarina-style patch.
// TODO: I'm unsure what this means exactly, need to check some examples...
bool m_ocarina = false; bool m_ocarina = false;
// If true, the offset is not known, and instead we should search for the m_original bytes in // If true, the offset is not known, and instead we should search for the m_original bytes in
@ -223,6 +222,10 @@ std::optional<Disc> ParseString(std::string_view xml, std::string xml_path);
std::vector<Patch> GenerateRiivolutionPatchesFromGameModDescriptor( std::vector<Patch> GenerateRiivolutionPatchesFromGameModDescriptor(
const GameModDescriptorRiivolution& descriptor, const std::string& game_id, const GameModDescriptorRiivolution& descriptor, const std::string& game_id,
std::optional<u16> revision, std::optional<u8> disc_number); std::optional<u16> revision, std::optional<u8> disc_number);
std::vector<Patch> GenerateRiivolutionPatchesFromConfig(const std::string root_directory,
const std::string& game_id,
std::optional<u16> revision,
std::optional<u8> disc_number);
std::optional<Config> ParseConfigFile(const std::string& filename); std::optional<Config> ParseConfigFile(const std::string& filename);
std::optional<Config> ParseConfigString(std::string_view xml); std::optional<Config> ParseConfigString(std::string_view xml);
std::string WriteConfigString(const Config& config); std::string WriteConfigString(const Config& config);