Skip to content
This repository has been archived by the owner on Oct 29, 2024. It is now read-only.

Commit

Permalink
Add Artic Base support (#105)
Browse files Browse the repository at this point in the history
* Add Artic Base support

* Add Android support
  • Loading branch information
PabloMK7 authored May 12, 2024
1 parent 572d3ab commit 24c6ec5
Show file tree
Hide file tree
Showing 83 changed files with 5,596 additions and 520 deletions.
66 changes: 52 additions & 14 deletions src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,13 @@ object NativeLibrary {
private var coreErrorAlertResult = false
private val coreErrorAlertLock = Object()

private fun onCoreErrorImpl(title: String, message: String) {
private fun onCoreErrorImpl(title: String, message: String, canContinue: Boolean) {
val emulationActivity = sEmulationActivity.get()
if (emulationActivity == null) {
Log.error("[NativeLibrary] EmulationActivity not present")
return
}
val fragment = CoreErrorDialogFragment.newInstance(title, message)
val fragment = CoreErrorDialogFragment.newInstance(title, message, canContinue)
fragment.show(emulationActivity.supportFragmentManager, CoreErrorDialogFragment.TAG)
}

Expand All @@ -207,23 +207,33 @@ object NativeLibrary {
}
val title: String
val message: String
val canContinue: Boolean
when (error) {
CoreError.ErrorSystemFiles -> {
title = emulationActivity.getString(R.string.system_archive_not_found)
message = emulationActivity.getString(
R.string.system_archive_not_found_message,
details.ifEmpty { emulationActivity.getString(R.string.system_archive_general) }
)
canContinue = true
}

CoreError.ErrorSavestate -> {
title = emulationActivity.getString(R.string.save_load_error)
message = details
canContinue = true
}

CoreError.ErrorArticDisconnected -> {
title = emulationActivity.getString(R.string.artic_base)
message = emulationActivity.getString(R.string.artic_server_comm_error)
canContinue = false
}

CoreError.ErrorUnknown -> {
title = emulationActivity.getString(R.string.fatal_error)
message = emulationActivity.getString(R.string.fatal_error_message)
canContinue = true
}

else -> {
Expand All @@ -232,7 +242,7 @@ object NativeLibrary {
}

// Show the AlertDialog on the main thread.
emulationActivity.runOnUiThread(Runnable { onCoreErrorImpl(title, message) })
emulationActivity.runOnUiThread(Runnable { onCoreErrorImpl(title, message, canContinue) })

// Wait for the lock to notify that it is complete.
synchronized(coreErrorAlertLock) {
Expand Down Expand Up @@ -346,6 +356,11 @@ object NativeLibrary {
return
}

if (resultCode == EmulationErrorDialogFragment.ShutdownRequested) {
emulationActivity.finish()
return
}

emulationActivity.runOnUiThread {
EmulationErrorDialogFragment.newInstance(resultCode).showNow(
emulationActivity.supportFragmentManager,
Expand All @@ -361,16 +376,23 @@ object NativeLibrary {
emulationActivity = requireActivity() as EmulationActivity

var captionId = R.string.loader_error_invalid_format
if (requireArguments().getInt(RESULT_CODE) == ErrorLoader_ErrorEncrypted) {
val result = requireArguments().getInt(RESULT_CODE)
if (result == ErrorLoader_ErrorEncrypted) {
captionId = R.string.loader_error_encrypted
}
if (result == ErrorArticDisconnected) {
captionId = R.string.artic_base
}

val alert = MaterialAlertDialogBuilder(requireContext())
.setTitle(captionId)
.setMessage(
Html.fromHtml(
CitraApplication.appContext.resources.getString(R.string.redump_games),
Html.FROM_HTML_MODE_LEGACY
if (result == ErrorArticDisconnected)
CitraApplication.appContext.resources.getString(R.string.artic_server_comm_error)
else
CitraApplication.appContext.resources.getString(R.string.redump_games),
Html.FROM_HTML_MODE_LEGACY
)
)
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
Expand Down Expand Up @@ -398,7 +420,10 @@ object NativeLibrary {
const val ErrorLoader = 4
const val ErrorLoader_ErrorEncrypted = 5
const val ErrorLoader_ErrorInvalidFormat = 6
const val ErrorSystemFiles = 7
const val ErrorLoader_ErrorGBATitle = 7
const val ErrorSystemFiles = 8
const val ErrorSavestate = 9
const val ErrorArticDisconnected = 10
const val ShutdownRequested = 11
const val ErrorUnknown = 12

Expand Down Expand Up @@ -619,6 +644,7 @@ object NativeLibrary {
enum class CoreError {
ErrorSystemFiles,
ErrorSavestate,
ErrorArticDisconnected,
ErrorUnknown
}

Expand All @@ -633,23 +659,33 @@ object NativeLibrary {
}

class CoreErrorDialogFragment : DialogFragment() {
private var userChosen = false
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val title = requireArguments().getString(TITLE)
val message = requireArguments().getString(MESSAGE)
return MaterialAlertDialogBuilder(requireContext())
val canContinue = requireArguments().getBoolean(CAN_CONTINUE)
val dialog = MaterialAlertDialogBuilder(requireContext())
.setTitle(title)
.setMessage(message)
.setPositiveButton(R.string.continue_button) { _: DialogInterface?, _: Int ->
if (canContinue) {
dialog.setPositiveButton(R.string.continue_button) { _: DialogInterface?, _: Int ->
coreErrorAlertResult = true
userChosen = true
}
.setNegativeButton(R.string.abort_button) { _: DialogInterface?, _: Int ->
coreErrorAlertResult = false
}.show()
}
dialog.setNegativeButton(R.string.abort_button) { _: DialogInterface?, _: Int ->
coreErrorAlertResult = false
userChosen = true
}
return dialog.show()
}

override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
coreErrorAlertResult = true
val canContinue = requireArguments().getBoolean(CAN_CONTINUE)
if (!userChosen) {
coreErrorAlertResult = canContinue
}
synchronized(coreErrorAlertLock) { coreErrorAlertLock.notify() }
}

Expand All @@ -658,12 +694,14 @@ object NativeLibrary {

const val TITLE = "title"
const val MESSAGE = "message"
const val CAN_CONTINUE = "canContinue"

fun newInstance(title: String, message: String): CoreErrorDialogFragment {
fun newInstance(title: String, message: String, canContinue: Boolean): CoreErrorDialogFragment {
val frag = CoreErrorDialogFragment()
val args = Bundle()
args.putString(TITLE, title)
args.putString(MESSAGE, message)
args.putBoolean(CAN_CONTINUE, canContinue)
frag.arguments = args
return frag
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,27 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.core.widget.doOnTextChanged
import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.transition.MaterialSharedAxis
import org.citra.citra_emu.CitraApplication
import org.citra.citra_emu.HomeNavigationDirections
import org.citra.citra_emu.R
import org.citra.citra_emu.adapters.HomeSettingAdapter
import org.citra.citra_emu.databinding.DialogSoftwareKeyboardBinding
import org.citra.citra_emu.databinding.FragmentHomeSettingsBinding
import org.citra.citra_emu.features.settings.model.Settings
import org.citra.citra_emu.features.settings.model.StringSetting
import org.citra.citra_emu.features.settings.ui.SettingsActivity
import org.citra.citra_emu.features.settings.utils.SettingsFile
import org.citra.citra_emu.model.Game
import org.citra.citra_emu.model.HomeSetting
import org.citra.citra_emu.ui.main.MainActivity
import org.citra.citra_emu.utils.GameHelper
Expand Down Expand Up @@ -76,6 +82,41 @@ class HomeSettingsFragment : Fragment() {
R.drawable.ic_settings,
{ SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") }
),
HomeSetting(
R.string.artic_base_connect,
R.string.artic_base_connect_description,
R.drawable.ic_network,
{
val inflater = LayoutInflater.from(context)
val inputBinding = DialogSoftwareKeyboardBinding.inflate(inflater)
var textInputValue: String = ""

inputBinding.editTextInput.setText(textInputValue)
inputBinding.editTextInput.doOnTextChanged { text, _, _, _ ->
textInputValue = text.toString()
}

val dialog = context?.let {
MaterialAlertDialogBuilder(it)
.setView(inputBinding.root)
.setTitle(getString(R.string.artic_base_enter_address))
.setPositiveButton(android.R.string.ok) { _, _ ->
if (textInputValue.isNotEmpty()) {
val menu = Game(
title = getString(R.string.artic_base),
path = "articbase://$textInputValue",
filename = ""
)
val action =
HomeNavigationDirections.actionGlobalEmulationActivity(menu)
binding.root.findNavController().navigate(action)
}
}
.setNegativeButton(android.R.string.cancel) {_, _ -> }
.show()
}
}
),
HomeSetting(
R.string.system_files,
R.string.system_files_description,
Expand Down
10 changes: 9 additions & 1 deletion src/android/app/src/main/jni/native.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ static jobject ToJavaCoreError(Core::System::ResultStatus result) {
static const std::map<Core::System::ResultStatus, const char*> CoreErrorNameMap{
{Core::System::ResultStatus::ErrorSystemFiles, "ErrorSystemFiles"},
{Core::System::ResultStatus::ErrorSavestate, "ErrorSavestate"},
{Core::System::ResultStatus::ErrorArticDisconnected, "ErrorArticDisconnected"},
{Core::System::ResultStatus::ErrorUnknown, "ErrorUnknown"},
};

Expand Down Expand Up @@ -178,6 +179,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
auto app_loader = Loader::GetLoader(filepath);
if (app_loader) {
app_loader->ReadProgramId(program_id);
system.RegisterAppLoaderEarly(app_loader);
GameSettings::LoadOverrides(program_id);
}
system.ApplySettings();
Expand Down Expand Up @@ -231,6 +233,10 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
InputManager::NDKMotionHandler()->DisableSensors();
if (!HandleCoreError(result, system.GetStatusDetails())) {
// Frontend requests us to abort
// If the error was an Artic disconnect, return shutdown request.
if (result == Core::System::ResultStatus::ErrorArticDisconnected) {
return Core::System::ResultStatus::ShutdownRequested;
}
return result;
}
InputManager::NDKMotionHandler()->EnableSensors();
Expand Down Expand Up @@ -314,7 +320,9 @@ void Java_org_citra_citra_1emu_NativeLibrary_doFrame([[maybe_unused]] JNIEnv* en
if (stop_run || pause_emulation) {
return;
}
window->TryPresenting();
if (window) {
window->TryPresenting();
}
}

void JNICALL Java_org_citra_citra_1emu_NativeLibrary_initializeGpuDriver(
Expand Down
9 changes: 9 additions & 0 deletions src/android/app/src/main/res/drawable/ic_network.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M200,840q-33,0 -56.5,-23.5T120,760q0,-33 23.5,-56.5T200,680q33,0 56.5,23.5T280,760q0,33 -23.5,56.5T200,840ZM680,840q0,-117 -44,-218.5T516,444q-76,-76 -177.5,-120T120,280v-120q142,0 265,53t216,146q93,93 146,216t53,265L680,840ZM440,840q0,-67 -25,-124.5T346,614q-44,-44 -101.5,-69T120,520v-120q92,0 171.5,34.5T431,529q60,60 94.5,139.5T560,840L440,840Z"/>
</vector>
7 changes: 7 additions & 0 deletions src/android/app/src/main/res/values-es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -657,4 +657,11 @@ Se esperan fallos gráficos temporales cuando ésta esté activado.</string>
<string name="november">Noviembre</string>
<string name="december">Diciembre</string>

<!-- Artic base -->
<string name="artic_server_comm_error">Fallo de comunicación con el servidor Artic Base. La emulación se detendrá.</string>
<string name="artic_base">Artic Base</string>
<string name="artic_base_connect">Conectar con Artic Base</string>
<string name="artic_base_connect_description">Conectar con una consola real que esté ejecutando un servidor Artic Base</string>
<string name="artic_base_enter_address">Introduce la dirección del servidor Artic Base</string>

</resources>
7 changes: 7 additions & 0 deletions src/android/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -683,4 +683,11 @@
<string name="november">November</string>
<string name="december">December</string>

<!-- Artic base -->
<string name="artic_server_comm_error">Failed to communicate with the Artic Base server. Emulation will stop.</string>
<string name="artic_base">Artic Base</string>
<string name="artic_base_connect_description">Connect to a real console that is running an Artic Base server</string>
<string name="artic_base_connect">Connect to Artic Base</string>
<string name="artic_base_enter_address">Enter Artic Base server address</string>

</resources>
4 changes: 4 additions & 0 deletions src/citra_qt/configuration/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,8 @@ void Config::ReadPathValues() {
UISettings::values.game_dirs.append(game_dir);
}
}
UISettings::values.last_artic_base_addr =
ReadSetting(QStringLiteral("last_artic_base_addr"), QString{}).toString();
UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList();
UISettings::values.language = ReadSetting(QStringLiteral("language"), QString{}).toString();
}
Expand Down Expand Up @@ -1135,6 +1137,8 @@ void Config::SavePathValues() {
WriteSetting(QStringLiteral("expanded"), game_dir.expanded, true);
}
qt_config->endArray();
WriteSetting(QStringLiteral("last_artic_base_addr"),
UISettings::values.last_artic_base_addr, QString{});
WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files);
WriteSetting(QStringLiteral("language"), UISettings::values.language, QString{});
}
Expand Down
Loading

0 comments on commit 24c6ec5

Please sign in to comment.