| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| package org.dolphinemu.dolphinemu.features.infinitybase.ui | ||
|
|
||
| import android.app.AlertDialog | ||
| import android.content.Intent | ||
| import android.view.LayoutInflater | ||
| import android.view.View | ||
| import android.view.ViewGroup | ||
| import android.widget.AdapterView | ||
| import android.widget.AdapterView.OnItemClickListener | ||
| import android.widget.ArrayAdapter | ||
| import android.widget.Toast | ||
| import androidx.recyclerview.widget.RecyclerView | ||
| import com.google.android.material.dialog.MaterialAlertDialogBuilder | ||
| import org.dolphinemu.dolphinemu.R | ||
| import org.dolphinemu.dolphinemu.activities.EmulationActivity | ||
| import org.dolphinemu.dolphinemu.databinding.DialogCreateInfinityFigureBinding | ||
| import org.dolphinemu.dolphinemu.databinding.ListItemNfcFigureSlotBinding | ||
| import org.dolphinemu.dolphinemu.features.infinitybase.InfinityConfig | ||
| import org.dolphinemu.dolphinemu.features.infinitybase.InfinityConfig.removeFigure | ||
|
|
||
| class FigureSlotAdapter( | ||
| private val figures: List<FigureSlot>, | ||
| private val activity: EmulationActivity | ||
| ) : RecyclerView.Adapter<FigureSlotAdapter.ViewHolder>(), | ||
| OnItemClickListener { | ||
|
|
||
| class ViewHolder(var binding: ListItemNfcFigureSlotBinding) : | ||
| RecyclerView.ViewHolder(binding.getRoot()) | ||
|
|
||
| private lateinit var binding: DialogCreateInfinityFigureBinding | ||
|
|
||
| override fun onCreateViewHolder( | ||
| parent: ViewGroup, | ||
| viewType: Int | ||
| ): ViewHolder { | ||
| val inflater = LayoutInflater.from(parent.context) | ||
| val binding = ListItemNfcFigureSlotBinding.inflate(inflater, parent, false) | ||
| return ViewHolder(binding) | ||
| } | ||
|
|
||
| override fun onBindViewHolder(holder: ViewHolder, position: Int) { | ||
| val figure = figures[position] | ||
| holder.binding.textFigureName.text = figure.label | ||
|
|
||
| holder.binding.buttonClearFigure.setOnClickListener { | ||
| removeFigure(figure.position) | ||
| activity.clearInfinityFigure(position) | ||
| } | ||
|
|
||
| holder.binding.buttonLoadFigure.setOnClickListener { | ||
| val loadFigure = Intent(Intent.ACTION_OPEN_DOCUMENT) | ||
| loadFigure.addCategory(Intent.CATEGORY_OPENABLE) | ||
| loadFigure.type = "*/*" | ||
| activity.setInfinityFigureData(0, "", figure.position, position) | ||
| activity.startActivityForResult( | ||
| loadFigure, | ||
| EmulationActivity.REQUEST_INFINITY_FIGURE_FILE | ||
| ) | ||
| } | ||
|
|
||
| val inflater = LayoutInflater.from(activity) | ||
| binding = DialogCreateInfinityFigureBinding.inflate(inflater) | ||
|
|
||
| binding.infinityDropdown.onItemClickListener = this | ||
|
|
||
| holder.binding.buttonCreateFigure.setOnClickListener { | ||
| var validFigures = InfinityConfig.REVERSE_LIST_FIGURES | ||
| // Filter adapter list by position, either Hexagon Pieces, Characters or Abilities | ||
| validFigures = when (figure.position) { | ||
| 0 -> { | ||
| // Hexagon Pieces | ||
| validFigures.filter { (_, value) -> value in 2000000..2999999 || value in 4000000..4999999 } | ||
| } | ||
|
|
||
| 1, 2 -> { | ||
| // Characters | ||
| validFigures.filter { (_, value) -> value in 1000000..1999999 } | ||
| } | ||
|
|
||
| else -> { | ||
| // Abilities | ||
| validFigures.filter { (_, value) -> value in 3000000..3999999 } | ||
| } | ||
| } | ||
| val figureListKeys = validFigures.keys.toMutableList() | ||
| figureListKeys.sort() | ||
| val figureNames: ArrayList<String> = ArrayList(figureListKeys) | ||
| binding.infinityDropdown.setAdapter( | ||
| ArrayAdapter( | ||
| activity, R.layout.support_simple_spinner_dropdown_item, | ||
| figureNames | ||
| ) | ||
| ) | ||
|
|
||
| if (binding.getRoot().parent != null) { | ||
| (binding.getRoot().parent as ViewGroup).removeAllViews() | ||
| } | ||
| val createDialog = MaterialAlertDialogBuilder(activity) | ||
| .setTitle(R.string.create_figure_title) | ||
| .setView(binding.getRoot()) | ||
| .setPositiveButton(R.string.create_figure, null) | ||
| .setNegativeButton(R.string.cancel, null) | ||
| .show() | ||
| createDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { | ||
| if (binding.infinityNum.text.toString().isNotBlank()) { | ||
| val createFigure = Intent(Intent.ACTION_CREATE_DOCUMENT) | ||
| createFigure.addCategory(Intent.CATEGORY_OPENABLE) | ||
| createFigure.type = "*/*" | ||
| val num = binding.infinityNum.text.toString().toLong() | ||
| val name = InfinityConfig.LIST_FIGURES[num] | ||
| if (name != null) { | ||
| createFigure.putExtra( | ||
| Intent.EXTRA_TITLE, | ||
| "$name.bin" | ||
| ) | ||
| activity.setInfinityFigureData(num, name, figure.position, position) | ||
| } else { | ||
| createFigure.putExtra( | ||
| Intent.EXTRA_TITLE, | ||
| "Unknown(Number: $num).bin" | ||
| ) | ||
| activity.setInfinityFigureData(num, "Unknown", figure.position, position) | ||
| } | ||
| activity.startActivityForResult( | ||
| createFigure, | ||
| EmulationActivity.REQUEST_CREATE_INFINITY_FIGURE | ||
| ) | ||
| createDialog.dismiss() | ||
| } else { | ||
| Toast.makeText( | ||
| activity, R.string.invalid_infinity_figure, | ||
| Toast.LENGTH_SHORT | ||
| ).show() | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| override fun getItemCount(): Int { | ||
| return figures.size | ||
| } | ||
|
|
||
| override fun onItemClick(parent: AdapterView<*>, view: View, position: Int, id: Long) { | ||
| val figureNumber = InfinityConfig.REVERSE_LIST_FIGURES[parent.getItemAtPosition(position)] | ||
| binding.infinityNum.setText(figureNumber.toString()) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/root" | ||
| xmlns:android="http://schemas.android.com/apk/res/android" | ||
| xmlns:app="http://schemas.android.com/apk/res-auto" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="match_parent" | ||
| android:padding="@dimen/spacing_medlarge"> | ||
|
|
||
| <com.google.android.material.textfield.TextInputLayout | ||
| android:id="@+id/layout_infinity_dropdown" | ||
| style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="wrap_content" | ||
| android:paddingBottom="@dimen/spacing_medlarge" | ||
| android:paddingHorizontal="@dimen/spacing_medlarge" | ||
| android:hint="@string/infinity_label" | ||
| app:layout_constraintEnd_toEndOf="parent" | ||
| app:layout_constraintStart_toStartOf="parent" | ||
| app:layout_constraintTop_toTopOf="parent"> | ||
|
|
||
| <com.google.android.material.textfield.MaterialAutoCompleteTextView | ||
| android:id="@+id/infinity_dropdown" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="wrap_content" | ||
| android:spinnerMode="dialog" | ||
| android:imeOptions="actionDone" /> | ||
|
|
||
| </com.google.android.material.textfield.TextInputLayout> | ||
|
|
||
| <LinearLayout | ||
| android:layout_width="match_parent" | ||
| android:layout_height="wrap_content" | ||
| android:orientation="horizontal" | ||
| app:layout_constraintTop_toBottomOf="@+id/layout_infinity_dropdown" | ||
| android:gravity="center_vertical" | ||
| android:baselineAligned="false"> | ||
|
|
||
| <com.google.android.material.textfield.TextInputLayout | ||
| android:id="@+id/layout_infinity_num" | ||
| android:layout_width="0dp" | ||
| android:layout_height="wrap_content" | ||
| android:layout_weight="1" | ||
| android:hint="@string/infinity_number" | ||
| android:paddingHorizontal="@dimen/spacing_medlarge"> | ||
|
|
||
| <com.google.android.material.textfield.TextInputEditText | ||
| android:id="@+id/infinity_num" | ||
| android:layout_width="match_parent" | ||
| android:layout_height="wrap_content" | ||
| android:inputType="number" /> | ||
|
|
||
| </com.google.android.material.textfield.TextInputLayout> | ||
|
|
||
| </LinearLayout> | ||
|
|
||
| </androidx.constraintlayout.widget.ConstraintLayout> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| // Copyright 2023 Dolphin Emulator Project | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
|
|
||
| #include <jni.h> | ||
|
|
||
| #include <array> | ||
|
|
||
| #include "AndroidCommon/AndroidCommon.h" | ||
| #include "AndroidCommon/IDCache.h" | ||
| #include "Core/IOS/USB/Emulated/Infinity.h" | ||
| #include "Core/System.h" | ||
|
|
||
| extern "C" { | ||
|
|
||
| JNIEXPORT jobject JNICALL | ||
| Java_org_dolphinemu_dolphinemu_features_infinitybase_InfinityConfig_getFigureMap(JNIEnv* env, | ||
| jobject obj) | ||
| { | ||
| auto& system = Core::System::GetInstance(); | ||
|
|
||
| jobject hash_map_obj = env->NewObject(IDCache::GetHashMapClass(), IDCache::GetHashMapInit(), | ||
| system.GetInfinityBase().GetFigureList().size()); | ||
|
|
||
| jclass long_class = env->FindClass("java/lang/Long"); | ||
| jmethodID long_init = env->GetMethodID(long_class, "<init>", "(J)V"); | ||
|
|
||
| for (const auto& it : system.GetInfinityBase().GetFigureList()) | ||
| { | ||
| const std::string& name = it.first; | ||
| jobject figure_number = env->NewObject(long_class, long_init, (jlong)it.second); | ||
| env->CallObjectMethod(hash_map_obj, IDCache::GetHashMapPut(), figure_number, | ||
| ToJString(env, name)); | ||
| env->DeleteLocalRef(figure_number); | ||
| } | ||
|
|
||
| return hash_map_obj; | ||
| } | ||
|
|
||
| JNIEXPORT jobject JNICALL | ||
| Java_org_dolphinemu_dolphinemu_features_infinitybase_InfinityConfig_getInverseFigureMap(JNIEnv* env, | ||
| jobject obj) | ||
| { | ||
| auto& system = Core::System::GetInstance(); | ||
|
|
||
| jobject hash_map_obj = env->NewObject(IDCache::GetHashMapClass(), IDCache::GetHashMapInit(), | ||
| system.GetInfinityBase().GetFigureList().size()); | ||
|
|
||
| jclass long_class = env->FindClass("java/lang/Long"); | ||
| jmethodID long_init = env->GetMethodID(long_class, "<init>", "(J)V"); | ||
|
|
||
| for (const auto& it : system.GetInfinityBase().GetFigureList()) | ||
| { | ||
| const std::string& name = it.first; | ||
| jobject figure_number = env->NewObject(long_class, long_init, (jlong)it.second); | ||
| env->CallObjectMethod(hash_map_obj, IDCache::GetHashMapPut(), ToJString(env, name), | ||
| figure_number); | ||
| env->DeleteLocalRef(figure_number); | ||
| } | ||
|
|
||
| return hash_map_obj; | ||
| } | ||
|
|
||
| JNIEXPORT void JNICALL | ||
| Java_org_dolphinemu_dolphinemu_features_infinitybase_InfinityConfig_removeFigure(JNIEnv* env, | ||
| jclass clazz, | ||
| jint position) | ||
| { | ||
| auto& system = Core::System::GetInstance(); | ||
| system.GetInfinityBase().RemoveFigure(position); | ||
| } | ||
|
|
||
| JNIEXPORT jstring JNICALL | ||
| Java_org_dolphinemu_dolphinemu_features_infinitybase_InfinityConfig_loadFigure(JNIEnv* env, | ||
| jclass clazz, | ||
| jint position, | ||
| jstring file_name) | ||
| { | ||
| File::IOFile inf_file(GetJString(env, file_name), "r+b"); | ||
| if (!inf_file) | ||
| { | ||
| return nullptr; | ||
| } | ||
| std::array<u8, 0x14 * 0x10> file_data{}; | ||
| if (!inf_file.ReadBytes(file_data.data(), file_data.size())) | ||
| { | ||
| return nullptr; | ||
| } | ||
|
|
||
| auto& system = Core::System::GetInstance(); | ||
| system.GetInfinityBase().RemoveFigure(position); | ||
| return ToJString(env, | ||
| system.GetInfinityBase().LoadFigure(file_data, std::move(inf_file), position)); | ||
| } | ||
|
|
||
| JNIEXPORT jstring JNICALL | ||
| Java_org_dolphinemu_dolphinemu_features_infinitybase_InfinityConfig_createFigure( | ||
| JNIEnv* env, jclass clazz, jlong figure_number, jstring fileName, jint position) | ||
| { | ||
| u32 fig_num = static_cast<u32>(figure_number); | ||
|
|
||
| std::string file_name = GetJString(env, fileName); | ||
|
|
||
| auto& system = Core::System::GetInstance(); | ||
| system.GetInfinityBase().CreateFigure(file_name, fig_num); | ||
| system.GetInfinityBase().RemoveFigure(position); | ||
|
|
||
| File::IOFile inf_file(file_name, "r+b"); | ||
| if (!inf_file) | ||
| { | ||
| return nullptr; | ||
| } | ||
| std::array<u8, 0x14 * 0x10> file_data{}; | ||
| if (!inf_file.ReadBytes(file_data.data(), file_data.size())) | ||
| { | ||
| return nullptr; | ||
| } | ||
|
|
||
| return ToJString(env, | ||
| system.GetInfinityBase().LoadFigure(file_data, std::move(inf_file), position)); | ||
| } | ||
| } |