mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-18 22:35:39 +00:00
Android: Add BIOS importer
This commit is contained in:
parent
423054e8ac
commit
13a9411b07
|
@ -1,9 +1,11 @@
|
|||
#include "android_host_interface.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/audio_stream.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "common/string.h"
|
||||
#include "common/timestamp.h"
|
||||
#include "core/bios.h"
|
||||
#include "core/cheats.h"
|
||||
#include "core/controller.h"
|
||||
#include "core/gpu.h"
|
||||
|
@ -848,3 +850,37 @@ DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_addOSDMessage, jobject obj, js
|
|||
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||
hi->AddOSDMessage(AndroidHelpers::JStringToString(env, message), duration);
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(jboolean, AndroidHostInterface_hasAnyBIOSImages, jobject obj)
|
||||
{
|
||||
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||
return hi->HasAnyBIOSImages();
|
||||
}
|
||||
|
||||
DEFINE_JNI_ARGS_METHOD(jstring, AndroidHostInterface_importBIOSImage, jobject obj, jbyteArray data)
|
||||
{
|
||||
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||
|
||||
const jsize len = env->GetArrayLength(data);
|
||||
if (len != BIOS::BIOS_SIZE)
|
||||
return nullptr;
|
||||
|
||||
BIOS::Image image;
|
||||
image.resize(static_cast<size_t>(len));
|
||||
env->GetByteArrayRegion(data, 0, len, reinterpret_cast<jbyte*>(image.data()));
|
||||
|
||||
const BIOS::Hash hash = BIOS::GetHash(image);
|
||||
const BIOS::ImageInfo* ii = BIOS::GetImageInfoForHash(hash);
|
||||
|
||||
const std::string dest_path(hi->GetUserDirectoryRelativePath("bios/%s.bin", hash.ToString().c_str()));
|
||||
if (FileSystem::FileExists(dest_path.c_str()) ||
|
||||
!FileSystem::WriteBinaryFile(dest_path.c_str(), image.data(), image.size()))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (ii)
|
||||
return env->NewStringUTF(ii->description);
|
||||
else
|
||||
return env->NewStringUTF(hash.ToString().c_str());
|
||||
}
|
||||
|
|
|
@ -76,6 +76,9 @@ public class AndroidHostInterface {
|
|||
|
||||
public native void addOSDMessage(String message, float duration);
|
||||
|
||||
public native boolean hasAnyBIOSImages();
|
||||
public native String importBIOSImage(byte[] data);
|
||||
|
||||
static {
|
||||
System.loadLibrary("duckstation-native");
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
|
@ -33,7 +34,11 @@ import android.widget.ListView;
|
|||
import android.widget.PopupMenu;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.prefs.Preferences;
|
||||
|
@ -43,6 +48,7 @@ import static com.google.android.material.snackbar.Snackbar.make;
|
|||
public class MainActivity extends AppCompatActivity {
|
||||
private static final int REQUEST_EXTERNAL_STORAGE_PERMISSIONS = 1;
|
||||
private static final int REQUEST_ADD_DIRECTORY_TO_GAME_LIST = 2;
|
||||
private static final int REQUEST_IMPORT_BIOS_IMAGE = 3;
|
||||
|
||||
private GameList mGameList;
|
||||
private ListView mGameListView;
|
||||
|
@ -153,11 +159,11 @@ public class MainActivity extends AppCompatActivity {
|
|||
startAddGameDirectory();
|
||||
} else if (id == R.id.action_scan_for_new_games) {
|
||||
mGameList.refresh(false, false);
|
||||
}
|
||||
if (id == R.id.action_rescan_all_games) {
|
||||
} else if (id == R.id.action_rescan_all_games) {
|
||||
mGameList.refresh(true, true);
|
||||
}
|
||||
if (id == R.id.action_settings) {
|
||||
} else if (id == R.id.action_import_bios) {
|
||||
importBIOSImage();
|
||||
} else if (id == R.id.action_settings) {
|
||||
Intent intent = new Intent(this, SettingsActivity.class);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
|
@ -202,6 +208,14 @@ public class MainActivity extends AppCompatActivity {
|
|||
mGameList.refresh(false, false);
|
||||
}
|
||||
break;
|
||||
|
||||
case REQUEST_IMPORT_BIOS_IMAGE: {
|
||||
if (resultCode != RESULT_OK)
|
||||
return;
|
||||
|
||||
onImportBIOSImageResult(data.getData());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,10 +254,65 @@ public class MainActivity extends AppCompatActivity {
|
|||
}
|
||||
|
||||
private boolean startEmulation(String bootPath, boolean resumeState) {
|
||||
if (!doBIOSCheck())
|
||||
return false;
|
||||
|
||||
Intent intent = new Intent(this, EmulationActivity.class);
|
||||
intent.putExtra("bootPath", bootPath);
|
||||
intent.putExtra("resumeState", resumeState);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean doBIOSCheck() {
|
||||
if (AndroidHostInterface.getInstance().hasAnyBIOSImages())
|
||||
return true;
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Missing BIOS Image")
|
||||
.setMessage("No BIOS image was found in DuckStation's bios directory. Do you with to locate and import a BIOS image now?")
|
||||
.setPositiveButton("Yes", (dialog, button) -> importBIOSImage())
|
||||
.setNegativeButton("No", (dialog, button) -> {})
|
||||
.create()
|
||||
.show();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void importBIOSImage() {
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.setType("*/*");
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
startActivityForResult(Intent.createChooser(intent, "Choose BIOS Image"), REQUEST_IMPORT_BIOS_IMAGE);
|
||||
}
|
||||
|
||||
private void onImportBIOSImageResult(Uri uri) {
|
||||
InputStream stream = null;
|
||||
try {
|
||||
stream = getContentResolver().openInputStream(uri);
|
||||
} catch (FileNotFoundException e) {
|
||||
Toast.makeText(this, "Failed to open BIOS image.", Toast.LENGTH_LONG);
|
||||
return;
|
||||
}
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
try {
|
||||
byte[] buffer = new byte[512 * 1024];
|
||||
int len;
|
||||
while ((len = stream.read(buffer)) > 0)
|
||||
os.write(buffer, 0, len);
|
||||
} catch (IOException e) {
|
||||
Toast.makeText(this, "Failed to read BIOS image.", Toast.LENGTH_LONG);
|
||||
return;
|
||||
}
|
||||
|
||||
String importResult = AndroidHostInterface.getInstance().importBIOSImage(os.toByteArray());
|
||||
String message = (importResult == null) ? "This BIOS image is invalid, or has already been imported." : ("BIOS '" + importResult + "' imported.");
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setMessage(message)
|
||||
.setPositiveButton("OK", (dialog, button) -> {})
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
<item
|
||||
android:id="@+id/action_rescan_all_games"
|
||||
android:title="Rescan All Games" />
|
||||
<item
|
||||
android:id="@+id/action_import_bios"
|
||||
android:title="Import BIOS" />
|
||||
</group>
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
|
|
|
@ -334,6 +334,12 @@ HostInterface::FindBIOSImagesInDirectory(const char* directory)
|
|||
return results;
|
||||
}
|
||||
|
||||
bool HostInterface::HasAnyBIOSImages()
|
||||
{
|
||||
const std::string dir = GetBIOSDirectory();
|
||||
return (FindBIOSImageInDirectory(ConsoleRegion::NTSC_U, dir.c_str()).has_value());
|
||||
}
|
||||
|
||||
bool HostInterface::LoadState(const char* filename)
|
||||
{
|
||||
std::unique_ptr<ByteStream> stream = FileSystem::OpenFile(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED);
|
||||
|
|
|
@ -22,8 +22,7 @@ class GameList;
|
|||
|
||||
struct SystemBootParameters;
|
||||
|
||||
namespace BIOS
|
||||
{
|
||||
namespace BIOS {
|
||||
struct ImageInfo;
|
||||
}
|
||||
|
||||
|
@ -131,6 +130,9 @@ public:
|
|||
/// Returns a list of filenames and descriptions for BIOS images in a directory.
|
||||
std::vector<std::pair<std::string, const BIOS::ImageInfo*>> FindBIOSImagesInDirectory(const char* directory);
|
||||
|
||||
/// Returns true if any BIOS images are found in the configured BIOS directory.
|
||||
bool HasAnyBIOSImages();
|
||||
|
||||
virtual void OnRunningGameChanged();
|
||||
virtual void OnSystemPerformanceCountersUpdated();
|
||||
|
||||
|
|
Loading…
Reference in a new issue