Android: Add patch code (cheat) import

This commit is contained in:
Connor McLaughlin
2020-11-07 22:04:07 +10:00
parent 8cb89636eb
commit 74942443d3
8 changed files with 161 additions and 38 deletions

View File

@ -70,9 +70,9 @@ public class AndroidHostInterface {
public native void setDisplayAlignment(int alignment);
public native CheatCode[] getCheatList();
public native void setCheatEnabled(int index, boolean enabled);
public native PatchCode[] getPatchCodeList();
public native void setPatchCodeEnabled(int index, boolean enabled);
public native boolean importPatchCodesFromString(String str);
public native void addOSDMessage(String message, float duration);

View File

@ -5,6 +5,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.hardware.input.InputManager;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
@ -54,6 +55,19 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
editor.apply();
}
private void reportErrorOnUIThread(String message) {
// Toast.makeText(this, message, Toast.LENGTH_LONG);
new AlertDialog.Builder(this)
.setTitle("Error")
.setMessage(message)
.setPositiveButton("OK", (dialog, button) -> {
dialog.dismiss();
enableFullscreenImmersive();
})
.create()
.show();
}
public void reportError(String message) {
Log.e("EmulationActivity", message);
@ -65,6 +79,7 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
.setMessage(message)
.setPositiveButton("OK", (dialog, button) -> {
dialog.dismiss();
enableFullscreenImmersive();
synchronized (lock) {
lock.notify();
}
@ -133,7 +148,6 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// Once we get a surface, we can boot.
if (AndroidHostInterface.getInstance().isEmulationThreadRunning()) {
final boolean hadSurface = AndroidHostInterface.getInstance().hasSurface();
AndroidHostInterface.getInstance().surfaceChanged(holder.getSurface(), format, width, height);
updateOrientation();
@ -217,6 +231,9 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
if (requestCode == REQUEST_CODE_SETTINGS) {
if (AndroidHostInterface.getInstance().isEmulationThreadRunning())
applySettings();
} else if (requestCode == REQUEST_IMPORT_PATCH_CODES) {
if (data != null)
importPatchesFromFile(data.getData());
}
}
@ -261,6 +278,7 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
}
private static final int REQUEST_CODE_SETTINGS = 0;
private static final int REQUEST_IMPORT_PATCH_CODES = 1;
private void showMenu() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
@ -331,7 +349,7 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
return;
}
case 1: // Patches
case 1: // Patch Codes
{
showPatchesMenu();
return;
@ -373,25 +391,42 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
}
private void showPatchesMenu() {
final CheatCode[] cheats = AndroidHostInterface.getInstance().getCheatList();
if (cheats == null) {
AndroidHostInterface.getInstance().addOSDMessage("No patches are loaded.", 5.0f);
return;
}
final PatchCode[] codes = AndroidHostInterface.getInstance().getPatchCodeList();
AlertDialog.Builder builder = new AlertDialog.Builder(this);
CharSequence[] items = new CharSequence[cheats.length];
for (int i = 0; i < cheats.length; i++) {
final CheatCode cc = cheats[i];
items[i] = String.format("%s %s", cc.isEnabled() ? "(ON)" : "(OFF)", cc.getName());
CharSequence[] items = new CharSequence[(codes != null) ? (codes.length + 1) : 1];
items[0] = "Import Patch Codes...";
if (codes != null) {
for (int i = 0; i < codes.length; i++) {
final PatchCode cc = codes[i];
items[i + 1] = String.format("%s %s", cc.isEnabled() ? "(ON)" : "(OFF)", cc.getDescription());
}
}
builder.setItems(items, (dialogInterface, i) -> AndroidHostInterface.getInstance().setCheatEnabled(i, !cheats[i].isEnabled()));
builder.setOnDismissListener(dialogInterface -> enableFullscreenImmersive());
builder.setItems(items, (dialogInterface, i) -> {
if (i > 0) {
AndroidHostInterface.getInstance().setPatchCodeEnabled(i - 1, !codes[i - 1].isEnabled());
enableFullscreenImmersive();
} else {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(Intent.createChooser(intent, "Choose Patch Code File"), REQUEST_IMPORT_PATCH_CODES);
}
});
builder.setOnCancelListener(dialogInterface -> enableFullscreenImmersive());
builder.create().show();
}
private void importPatchesFromFile(Uri uri) {
String str = FileUtil.readFileFromUri(this, uri, 512 * 1024);
if (str == null || !AndroidHostInterface.getInstance().importPatchCodesFromString(str)) {
reportErrorOnUIThread("Failed to import patch codes. Make sure you selected a PCSXR or Libretro format file.");
}
}
/**
* Touchscreen controller overlay
*/

View File

@ -9,12 +9,23 @@ import android.net.Uri;
import android.os.Build;
import android.os.storage.StorageManager;
import android.provider.DocumentsContract;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
public final class FileUtil {
static String TAG = "TAG";
@ -138,4 +149,32 @@ public final class FileUtil {
return null;
}
}
public static String readFileFromUri(final Context context, final Uri uri, int maxSize) {
InputStream stream = null;
try {
stream = context.getContentResolver().openInputStream(uri);
} catch (FileNotFoundException e) {
return null;
}
StringBuilder os = new StringBuilder();
try {
char[] buffer = new char[1024];
InputStreamReader reader = new InputStreamReader(stream, Charset.forName(StandardCharsets.UTF_8.name()));
int len;
while ((len = reader.read(buffer)) > 0) {
os.append(buffer, 0, len);
if (os.length() > maxSize)
return null;
}
} catch (IOException e) {
return null;
}
if (os.length() == 0)
return null;
return os.toString();
}
}

View File

@ -1,13 +1,13 @@
package com.github.stenzek.duckstation;
public class CheatCode {
public class PatchCode {
private int mIndex;
private String mName;
private String mDescription;
private boolean mEnabled;
public CheatCode(int index, String name, boolean enabled) {
public PatchCode(int index, String description, boolean enabled) {
mIndex = index;
mName = name;
mDescription = description;
mEnabled = enabled;
}
@ -15,8 +15,8 @@ public class CheatCode {
return mIndex;
}
public String getName() {
return mName;
public String getDescription() {
return mDescription;
}
public boolean isEnabled() {

View File

@ -143,7 +143,7 @@
</string-array>
<string-array name="emulation_more_menu">
<item>Reset</item>
<item>Patches</item>
<item>Patch Codes</item>
<item>Change Disc</item>
<item>Change Touchscreen Controller</item>
<item>Settings</item>

View File

@ -41,9 +41,9 @@
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
app:key="Main/AutoLoadCheats"
app:title="Load Cheats"
app:title="Load Patch Codes"
app:defaultValue="false"
app:summary="Loads cheats from cheats/&lt;game name&gt;.cht in PCSXR format. Cheats can be toggled while ingame."
app:summary="Loads patch codes from cheats/&lt;game name&gt;.cht in PCSXR format. Codes can be toggled while ingame."
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
app:key="Display/VSync"