mirror of
https://github.com/rafaelvcaetano/melonDS-android.git
synced 2024-11-26 23:20:40 +00:00
Add support for slot 2 Memory Expansion
This commit is contained in:
parent
ec8a89e341
commit
0a3ee936c0
@ -6,6 +6,7 @@
|
||||
#include <unistd.h>
|
||||
#include <cstdlib>
|
||||
#include <MelonDS.h>
|
||||
#include <RomGbaSlotConfig.h>
|
||||
#include <InputAndroid.h>
|
||||
#include <android/asset_manager_jni.h>
|
||||
#include "UriFileHandler.h"
|
||||
@ -18,7 +19,14 @@
|
||||
|
||||
#define MAX_CHEAT_SIZE (2*64)
|
||||
|
||||
enum GbaSlotType {
|
||||
NONE = 0,
|
||||
GBA_ROM = 1,
|
||||
MEMORY_EXPANSION = 2,
|
||||
};
|
||||
|
||||
void* emulate(void*);
|
||||
MelonDSAndroid::RomGbaSlotConfig* buildGbaSlotConfig(GbaSlotType slotType, const char* romPath, const char* savePath);
|
||||
|
||||
pthread_t emuThread;
|
||||
pthread_mutex_t emuThreadMutex;
|
||||
@ -186,7 +194,7 @@ Java_me_magnum_melonds_MelonEmulator_getRichPresenceStatus(JNIEnv* env, jobject
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_me_magnum_melonds_MelonEmulator_loadRomInternal(JNIEnv* env, jobject thiz, jstring romPath, jstring sramPath, jboolean loadGbaRom, jstring gbaRomPath, jstring gbaSramPath)
|
||||
Java_me_magnum_melonds_MelonEmulator_loadRomInternal(JNIEnv* env, jobject thiz, jstring romPath, jstring sramPath, jint gbaSlotType, jstring gbaRomPath, jstring gbaSramPath)
|
||||
{
|
||||
jboolean isCopy = JNI_FALSE;
|
||||
const char* rom = romPath == nullptr ? nullptr : env->GetStringUTFChars(romPath, &isCopy);
|
||||
@ -194,7 +202,9 @@ Java_me_magnum_melonds_MelonEmulator_loadRomInternal(JNIEnv* env, jobject thiz,
|
||||
const char* gbaRom = gbaRomPath == nullptr ? nullptr : env->GetStringUTFChars(gbaRomPath, &isCopy);
|
||||
const char* gbaSram = gbaSramPath == nullptr ? nullptr : env->GetStringUTFChars(gbaSramPath, &isCopy);
|
||||
|
||||
int result = MelonDSAndroid::loadRom(const_cast<char*>(rom), const_cast<char*>(sram), loadGbaRom, const_cast<char*>(gbaRom), const_cast<char*>(gbaSram));
|
||||
MelonDSAndroid::RomGbaSlotConfig* gbaSlotConfig = buildGbaSlotConfig((GbaSlotType) gbaSlotType, gbaRom, gbaSram);
|
||||
int result = MelonDSAndroid::loadRom(const_cast<char*>(rom), const_cast<char*>(sram), gbaSlotConfig);
|
||||
delete gbaSlotConfig;
|
||||
|
||||
if (isCopy == JNI_TRUE) {
|
||||
if (romPath) env->ReleaseStringUTFChars(romPath, rom);
|
||||
@ -467,6 +477,26 @@ Java_me_magnum_melonds_MelonEmulator_updateEmulatorConfiguration(JNIEnv* env, jo
|
||||
}
|
||||
}
|
||||
|
||||
MelonDSAndroid::RomGbaSlotConfig* buildGbaSlotConfig(GbaSlotType slotType, const char* romPath, const char* savePath)
|
||||
{
|
||||
if (slotType == GbaSlotType::GBA_ROM)
|
||||
{
|
||||
MelonDSAndroid::RomGbaSlotConfigGbaRom* gbaSlotConfigGbaRom = new MelonDSAndroid::RomGbaSlotConfigGbaRom {
|
||||
.romPath = std::string(romPath),
|
||||
.savePath = std::string(savePath)
|
||||
};
|
||||
return (MelonDSAndroid::RomGbaSlotConfig*) gbaSlotConfigGbaRom;
|
||||
}
|
||||
else if (slotType == GbaSlotType::MEMORY_EXPANSION)
|
||||
{
|
||||
return (MelonDSAndroid::RomGbaSlotConfig*) new MelonDSAndroid::RomGbaSlotConfigMemoryExpansion;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (MelonDSAndroid::RomGbaSlotConfig*) new MelonDSAndroid::RomGbaSlotConfigNone;
|
||||
}
|
||||
}
|
||||
|
||||
double getCurrentMillis() {
|
||||
timespec now;
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
|
@ -37,6 +37,12 @@ object MelonEmulator {
|
||||
DSI_NAND_BAD
|
||||
}
|
||||
|
||||
enum class GbaSlotType {
|
||||
NONE,
|
||||
GBA_ROM,
|
||||
MEMORY_EXPANSION,
|
||||
}
|
||||
|
||||
external fun setupEmulator(emulatorConfiguration: EmulatorConfiguration, assetManager: AssetManager?, dsiCameraSource: DSiCameraSource?, retroAchievementsCallback: RetroAchievementsCallback, textureBuffer: ByteBuffer)
|
||||
|
||||
external fun setupCheats(cheats: Array<Cheat>)
|
||||
@ -47,8 +53,8 @@ object MelonEmulator {
|
||||
|
||||
external fun getRichPresenceStatus(): String?
|
||||
|
||||
fun loadRom(romUri: Uri, sramUri: Uri, loadGbaRom: Boolean, gbaRomUri: Uri?, gbaSramUri: Uri?): LoadResult {
|
||||
val loadResult = loadRomInternal(romUri.toString(), sramUri.toString(), loadGbaRom, gbaRomUri?.toString(), gbaSramUri?.toString())
|
||||
fun loadRom(romUri: Uri, sramUri: Uri, gbaSlotType: GbaSlotType, gbaRomUri: Uri?, gbaSramUri: Uri?): LoadResult {
|
||||
val loadResult = loadRomInternal(romUri.toString(), sramUri.toString(), gbaSlotType.ordinal, gbaRomUri?.toString(), gbaSramUri?.toString())
|
||||
return when (loadResult) {
|
||||
0 -> LoadResult.SUCCESS
|
||||
1 -> LoadResult.SUCCESS_GBA_FAILED
|
||||
@ -63,7 +69,7 @@ object MelonEmulator {
|
||||
return FirmwareLoadResult.entries[loadResult]
|
||||
}
|
||||
|
||||
private external fun loadRomInternal(romPath: String, sramPath: String, loadGbaRom: Boolean, gbaRomPath: String?, gbaSramPath: String?): Int
|
||||
private external fun loadRomInternal(romPath: String, sramPath: String, gbaSlotType: Int, gbaRomPath: String?, gbaSramPath: String?): Int
|
||||
|
||||
private external fun bootFirmwareInternal(): Int
|
||||
|
||||
|
@ -6,6 +6,8 @@ import android.net.Uri
|
||||
import io.reactivex.Single
|
||||
import me.magnum.melonds.common.uridelegates.UriHandler
|
||||
import me.magnum.melonds.domain.model.*
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.rom.config.RomConfig
|
||||
import me.magnum.melonds.extensions.isBlank
|
||||
import me.magnum.melonds.extensions.nameWithoutExtension
|
||||
import me.magnum.melonds.impl.NdsRomCache
|
||||
|
@ -5,8 +5,8 @@ import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import io.reactivex.Single
|
||||
import me.magnum.melonds.common.uridelegates.UriHandler
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.RomConfig
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.rom.config.RomConfig
|
||||
import me.magnum.melonds.domain.model.RomInfo
|
||||
import me.magnum.melonds.domain.model.RomMetadata
|
||||
import me.magnum.melonds.extensions.isBlank
|
||||
|
@ -3,7 +3,7 @@ package me.magnum.melonds.common.romprocessors
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import io.reactivex.Single
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.RomInfo
|
||||
|
||||
interface RomFileProcessor {
|
||||
|
@ -47,6 +47,7 @@ object MigrationModule {
|
||||
registerMigration(Migration21to22(context, gson, uriHandler))
|
||||
registerMigration(Migration24to25(genericJsonArrayMigrationHelper, context))
|
||||
registerMigration(Migration25to26(genericJsonArrayMigrationHelper))
|
||||
registerMigration(Migration30to31(genericJsonArrayMigrationHelper))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package me.magnum.melonds.domain.model
|
||||
|
||||
import android.net.Uri
|
||||
import java.util.*
|
||||
|
||||
data class RomConfig(
|
||||
var runtimeConsoleType: RuntimeConsoleType = RuntimeConsoleType.DEFAULT,
|
||||
var runtimeMicSource: RuntimeMicSource = RuntimeMicSource.DEFAULT,
|
||||
var layoutId: UUID? = null,
|
||||
var loadGbaCart: Boolean = false,
|
||||
var gbaCartPath: Uri? = null,
|
||||
var gbaSavePath: Uri? = null
|
||||
) {
|
||||
fun mustLoadGbaCart() = loadGbaCart && gbaCartPath != null
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package me.magnum.melonds.domain.model
|
||||
package me.magnum.melonds.domain.model.rom
|
||||
|
||||
import android.net.Uri
|
||||
import me.magnum.melonds.domain.model.rom.config.RomConfig
|
||||
import java.util.*
|
||||
|
||||
data class Rom(
|
@ -0,0 +1,10 @@
|
||||
package me.magnum.melonds.domain.model.rom.config
|
||||
|
||||
import java.util.*
|
||||
|
||||
data class RomConfig(
|
||||
var runtimeConsoleType: RuntimeConsoleType = RuntimeConsoleType.DEFAULT,
|
||||
var runtimeMicSource: RuntimeMicSource = RuntimeMicSource.DEFAULT,
|
||||
var layoutId: UUID? = null,
|
||||
val gbaSlotConfig: RomGbaSlotConfig = RomGbaSlotConfig.None,
|
||||
)
|
@ -0,0 +1,9 @@
|
||||
package me.magnum.melonds.domain.model.rom.config
|
||||
|
||||
import android.net.Uri
|
||||
|
||||
sealed class RomGbaSlotConfig {
|
||||
data object None : RomGbaSlotConfig()
|
||||
data class GbaRom(val romPath: Uri?, val savePath: Uri?) : RomGbaSlotConfig()
|
||||
data object MemoryExpansion : RomGbaSlotConfig()
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
package me.magnum.melonds.domain.model
|
||||
package me.magnum.melonds.domain.model.rom.config
|
||||
|
||||
import me.magnum.melonds.domain.model.ConsoleType
|
||||
|
||||
enum class RuntimeConsoleType(val targetConsoleType: ConsoleType?) : RuntimeEnum<RuntimeConsoleType, ConsoleType> {
|
||||
DEFAULT(null),
|
@ -1,4 +1,4 @@
|
||||
package me.magnum.melonds.domain.model
|
||||
package me.magnum.melonds.domain.model.rom.config
|
||||
|
||||
interface RuntimeEnum<T, U> {
|
||||
fun getDefault(): T
|
@ -1,4 +1,6 @@
|
||||
package me.magnum.melonds.domain.model
|
||||
package me.magnum.melonds.domain.model.rom.config
|
||||
|
||||
import me.magnum.melonds.domain.model.MicSource
|
||||
|
||||
enum class RuntimeMicSource(val micSource: MicSource?) : RuntimeEnum<RuntimeMicSource, MicSource> {
|
||||
DEFAULT(null),
|
@ -3,8 +3,8 @@ package me.magnum.melonds.domain.repositories
|
||||
import android.net.Uri
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.RomConfig
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.rom.config.RomConfig
|
||||
import me.magnum.melonds.domain.model.RomScanningStatus
|
||||
import java.util.*
|
||||
|
||||
|
@ -2,8 +2,7 @@ package me.magnum.melonds.domain.repositories
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import io.reactivex.Single
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.SaveStateSlot
|
||||
|
||||
interface SaveStatesRepository {
|
||||
|
@ -5,6 +5,7 @@ import io.reactivex.Observable
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import me.magnum.melonds.domain.model.*
|
||||
import me.magnum.melonds.domain.model.camera.DSiCameraSourceType
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.ui.Theme
|
||||
import java.util.*
|
||||
|
||||
|
@ -4,7 +4,7 @@ import android.net.Uri
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import me.magnum.melonds.domain.model.Cheat
|
||||
import me.magnum.melonds.domain.model.ConsoleType
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.emulator.FirmwareLaunchResult
|
||||
import me.magnum.melonds.domain.model.emulator.RomLaunchResult
|
||||
import me.magnum.melonds.domain.model.retroachievements.GameAchievementData
|
||||
|
@ -13,13 +13,13 @@ import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.magnum.melonds.common.romprocessors.RomFileProcessorFactory
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.RomConfig
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.rom.config.RomConfig
|
||||
import me.magnum.melonds.domain.model.RomScanningStatus
|
||||
import me.magnum.melonds.domain.repositories.RomsRepository
|
||||
import me.magnum.melonds.domain.repositories.SettingsRepository
|
||||
import me.magnum.melonds.extensions.addTo
|
||||
import me.magnum.melonds.impl.dtos.RomDto
|
||||
import me.magnum.melonds.impl.dtos.rom.RomDto
|
||||
import me.magnum.melonds.utils.FileUtils
|
||||
import me.magnum.melonds.utils.SubjectSharedFlow
|
||||
import java.io.File
|
||||
|
@ -4,7 +4,7 @@ import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import me.magnum.melonds.common.uridelegates.UriHandler
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.SaveStateSlot
|
||||
import me.magnum.melonds.domain.repositories.SaveStatesRepository
|
||||
import me.magnum.melonds.domain.repositories.SettingsRepository
|
||||
|
@ -5,7 +5,7 @@ import android.net.Uri
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.SizeUnit
|
||||
import me.magnum.melonds.domain.repositories.SettingsRepository
|
||||
import java.io.File
|
||||
|
@ -7,7 +7,7 @@ import androidx.documentfile.provider.DocumentFile
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.magnum.melonds.common.romprocessors.RomFileProcessorFactory
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
@ -5,7 +5,7 @@ import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import com.squareup.picasso.Picasso
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.SaveStateSlot
|
||||
import java.io.File
|
||||
|
||||
|
@ -36,7 +36,7 @@ import me.magnum.melonds.domain.model.LayoutConfiguration
|
||||
import me.magnum.melonds.domain.model.MacAddress
|
||||
import me.magnum.melonds.domain.model.MicSource
|
||||
import me.magnum.melonds.domain.model.RendererConfiguration
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.RomIconFiltering
|
||||
import me.magnum.melonds.domain.model.SaveStateLocation
|
||||
import me.magnum.melonds.domain.model.SizeUnit
|
||||
|
@ -1,11 +1,10 @@
|
||||
package me.magnum.melonds.impl.dtos
|
||||
package me.magnum.melonds.impl.dtos.rom
|
||||
|
||||
import android.net.Uri
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import me.magnum.melonds.domain.model.RomConfig
|
||||
import me.magnum.melonds.domain.model.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.model.RuntimeMicSource
|
||||
import java.util.*
|
||||
import me.magnum.melonds.domain.model.rom.config.RomConfig
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeMicSource
|
||||
import java.util.UUID
|
||||
|
||||
data class RomConfigDto(
|
||||
@SerializedName("runtimeConsoleType")
|
||||
@ -14,12 +13,8 @@ data class RomConfigDto(
|
||||
val runtimeMicSource: RuntimeMicSource,
|
||||
@SerializedName("layoutId")
|
||||
val layoutId: String?,
|
||||
@SerializedName("loadGbaCart")
|
||||
val loadGbaCart: Boolean,
|
||||
@SerializedName("gbaCartPath")
|
||||
val gbaCartPath: String?,
|
||||
@SerializedName("gbaSavePath")
|
||||
val gbaSavePath: String?,
|
||||
@SerializedName("gbaSlotConfig")
|
||||
val gbaSlotConfig: RomGbaSlotConfigDto,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
@ -28,9 +23,7 @@ data class RomConfigDto(
|
||||
romConfig.runtimeConsoleType,
|
||||
romConfig.runtimeMicSource,
|
||||
romConfig.layoutId?.toString(),
|
||||
romConfig.loadGbaCart,
|
||||
romConfig.gbaCartPath?.toString(),
|
||||
romConfig.gbaSavePath?.toString(),
|
||||
RomGbaSlotConfigDto.fromModel(romConfig.gbaSlotConfig),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -40,9 +33,7 @@ data class RomConfigDto(
|
||||
runtimeConsoleType,
|
||||
runtimeMicSource,
|
||||
layoutId?.let { UUID.fromString(it) },
|
||||
loadGbaCart,
|
||||
gbaCartPath?.let { Uri.parse(it) },
|
||||
gbaSavePath?.let { Uri.parse(it) },
|
||||
gbaSlotConfig.toModel(),
|
||||
)
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
package me.magnum.melonds.impl.dtos
|
||||
package me.magnum.melonds.impl.dtos.rom
|
||||
|
||||
import android.net.Uri
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import java.util.*
|
||||
|
||||
data class RomDto(
|
@ -0,0 +1,51 @@
|
||||
package me.magnum.melonds.impl.dtos.rom
|
||||
|
||||
import android.net.Uri
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import me.magnum.melonds.domain.model.rom.config.RomGbaSlotConfig
|
||||
|
||||
/**
|
||||
* GBA slot config DTO holds information about all possible configs in a single object. This makes (de)serialization easier since it avoids dealing with polymorphism.
|
||||
*/
|
||||
data class RomGbaSlotConfigDto(
|
||||
@SerializedName("type")
|
||||
val type: Type,
|
||||
@SerializedName("gbaRomPath")
|
||||
val gbaRomPath: String?,
|
||||
@SerializedName("gbaSavePath")
|
||||
val gbaSavePath: String?,
|
||||
) {
|
||||
|
||||
enum class Type {
|
||||
None, GbaRom, MemoryExpansion
|
||||
}
|
||||
|
||||
fun toModel(): RomGbaSlotConfig {
|
||||
return when (type) {
|
||||
Type.None -> RomGbaSlotConfig.None
|
||||
Type.GbaRom -> RomGbaSlotConfig.GbaRom(
|
||||
romPath = gbaRomPath?.let { Uri.parse(it) },
|
||||
savePath = gbaSavePath?.let { Uri.parse(it) },
|
||||
)
|
||||
Type.MemoryExpansion -> RomGbaSlotConfig.MemoryExpansion
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromModel(romGbaSlotConfig: RomGbaSlotConfig): RomGbaSlotConfigDto {
|
||||
return RomGbaSlotConfigDto(
|
||||
type = romGbaSlotConfig.dtoType(),
|
||||
gbaRomPath = (romGbaSlotConfig as? RomGbaSlotConfig.GbaRom)?.romPath?.toString(),
|
||||
gbaSavePath = (romGbaSlotConfig as? RomGbaSlotConfig.GbaRom)?.savePath?.toString(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun RomGbaSlotConfig.dtoType(): Type {
|
||||
return when (this) {
|
||||
is RomGbaSlotConfig.None -> Type.None
|
||||
is RomGbaSlotConfig.GbaRom -> Type.GbaRom
|
||||
is RomGbaSlotConfig.MemoryExpansion -> Type.MemoryExpansion
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -18,14 +18,15 @@ import me.magnum.melonds.domain.model.Cheat
|
||||
import me.magnum.melonds.domain.model.ConsoleType
|
||||
import me.magnum.melonds.domain.model.EmulatorConfiguration
|
||||
import me.magnum.melonds.domain.model.MicSource
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.model.RuntimeEnum
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeEnum
|
||||
import me.magnum.melonds.domain.model.emulator.FirmwareLaunchResult
|
||||
import me.magnum.melonds.domain.model.emulator.RomLaunchResult
|
||||
import me.magnum.melonds.domain.model.retroachievements.GameAchievementData
|
||||
import me.magnum.melonds.domain.model.retroachievements.RAEvent
|
||||
import me.magnum.melonds.domain.model.retroachievements.RASimpleAchievement
|
||||
import me.magnum.melonds.domain.model.rom.config.RomGbaSlotConfig
|
||||
import me.magnum.melonds.domain.repositories.SettingsRepository
|
||||
import me.magnum.melonds.domain.services.EmulatorManager
|
||||
import me.magnum.melonds.impl.camera.DSiCameraSourceMultiplexer
|
||||
@ -60,7 +61,20 @@ class AndroidEmulatorManager(
|
||||
return@withContext RomLaunchResult.LaunchFailedSramProblem(exception)
|
||||
}
|
||||
|
||||
val loadResult = MelonEmulator.loadRom(romUri, sram, rom.config.mustLoadGbaCart(), rom.config.gbaCartPath, rom.config.gbaSavePath)
|
||||
val gbaSlotRomConfig = rom.config.gbaSlotConfig
|
||||
val gbaSlotType = when (gbaSlotRomConfig) {
|
||||
RomGbaSlotConfig.None -> MelonEmulator.GbaSlotType.NONE
|
||||
is RomGbaSlotConfig.GbaRom -> MelonEmulator.GbaSlotType.GBA_ROM
|
||||
RomGbaSlotConfig.MemoryExpansion -> MelonEmulator.GbaSlotType.MEMORY_EXPANSION
|
||||
}
|
||||
|
||||
val loadResult = MelonEmulator.loadRom(
|
||||
romUri = romUri,
|
||||
sramUri = sram,
|
||||
gbaSlotType = gbaSlotType,
|
||||
gbaRomUri = (gbaSlotRomConfig as? RomGbaSlotConfig.GbaRom)?.romPath,
|
||||
gbaSramUri = (gbaSlotRomConfig as? RomGbaSlotConfig.GbaRom)?.savePath
|
||||
)
|
||||
if (loadResult.isTerminal || !isActive) {
|
||||
cameraManager.stopCurrentCameraSource()
|
||||
RomLaunchResult.LaunchFailed(loadResult)
|
||||
|
@ -1,7 +1,7 @@
|
||||
package me.magnum.melonds.impl.emulator
|
||||
|
||||
import me.magnum.melonds.domain.model.ConsoleType
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.emulator.EmulatorSessionUpdateAction
|
||||
import me.magnum.melonds.domain.model.retroachievements.GameAchievementData
|
||||
|
||||
|
@ -2,7 +2,7 @@ package me.magnum.melonds.impl.emulator
|
||||
|
||||
import android.net.Uri
|
||||
import me.magnum.melonds.common.uridelegates.UriHandler
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.repositories.SettingsRepository
|
||||
|
||||
class SramProvider(
|
||||
|
@ -0,0 +1,49 @@
|
||||
package me.magnum.melonds.migrations
|
||||
|
||||
import me.magnum.melonds.migrations.helper.GenericJsonArrayMigrationHelper
|
||||
import me.magnum.melonds.migrations.legacy.RomConfigDto25
|
||||
import me.magnum.melonds.migrations.legacy.RomConfigDto31
|
||||
import me.magnum.melonds.migrations.legacy.RomDto25
|
||||
import me.magnum.melonds.migrations.legacy.RomDto31
|
||||
import me.magnum.melonds.migrations.legacy.RomGbaSlotConfigDto31
|
||||
|
||||
class Migration30to31(
|
||||
private val romMigrationHelper: GenericJsonArrayMigrationHelper,
|
||||
) : Migration {
|
||||
|
||||
override val from = 30
|
||||
override val to = 31
|
||||
|
||||
override fun migrate() {
|
||||
romMigrationHelper.migrateJsonArrayData(ROM_DATA_FILE, RomDto25::class.java) {
|
||||
RomDto31(
|
||||
it.name,
|
||||
it.developerName,
|
||||
it.fileName,
|
||||
it.uri,
|
||||
it.parentTreeUri,
|
||||
RomConfigDto31(
|
||||
it.config.runtimeConsoleType,
|
||||
it.config.runtimeMicSource,
|
||||
it.config.layoutId,
|
||||
createGbaSlotConfigDto(it.config),
|
||||
),
|
||||
it.lastPlayed,
|
||||
it.isDsiWareTitle,
|
||||
it.retroAchievementsHash,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createGbaSlotConfigDto(oldConfig: RomConfigDto25): RomGbaSlotConfigDto31 {
|
||||
return RomGbaSlotConfigDto31(
|
||||
type = if (oldConfig.loadGbaCart) RomGbaSlotConfigDto31.Type.GbaRom else RomGbaSlotConfigDto31.Type.None,
|
||||
gbaRomPath = oldConfig.gbaCartPath,
|
||||
gbaSavePath = oldConfig.gbaSavePath,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ROM_DATA_FILE = "rom_data.json"
|
||||
}
|
||||
}
|
@ -2,8 +2,8 @@ package me.magnum.melonds.migrations.legacy
|
||||
|
||||
import android.net.Uri
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import me.magnum.melonds.domain.model.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.model.RuntimeMicSource
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeMicSource
|
||||
import java.util.*
|
||||
|
||||
data class RomConfig1(
|
||||
|
@ -1,9 +1,8 @@
|
||||
package me.magnum.melonds.migrations.legacy
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import me.magnum.melonds.domain.model.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.model.RuntimeMicSource
|
||||
import java.util.*
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeMicSource
|
||||
|
||||
data class RomConfigDto25(
|
||||
@SerializedName("runtimeConsoleType")
|
||||
|
@ -0,0 +1,16 @@
|
||||
package me.magnum.melonds.migrations.legacy
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeMicSource
|
||||
|
||||
data class RomConfigDto31(
|
||||
@SerializedName("runtimeConsoleType")
|
||||
val runtimeConsoleType: RuntimeConsoleType,
|
||||
@SerializedName("runtimeMicSource")
|
||||
val runtimeMicSource: RuntimeMicSource,
|
||||
@SerializedName("layoutId")
|
||||
val layoutId: String?,
|
||||
@SerializedName("gbaSlotConfig")
|
||||
val gbaSlotConfig: RomGbaSlotConfigDto31,
|
||||
)
|
@ -0,0 +1,28 @@
|
||||
package me.magnum.melonds.migrations.legacy
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import java.util.Date
|
||||
|
||||
/**
|
||||
* ROM DTO used from app version 27.
|
||||
*/
|
||||
data class RomDto31(
|
||||
@SerializedName("name")
|
||||
val name: String,
|
||||
@SerializedName("developerName")
|
||||
val developerName: String,
|
||||
@SerializedName("fileName")
|
||||
val fileName: String,
|
||||
@SerializedName("uri")
|
||||
val uri: String,
|
||||
@SerializedName("parentTreeUri")
|
||||
val parentTreeUri: String,
|
||||
@SerializedName("config")
|
||||
var config: RomConfigDto31,
|
||||
@SerializedName("lastPlayed")
|
||||
var lastPlayed: Date? = null,
|
||||
@SerializedName("isDsiWareTitle")
|
||||
val isDsiWareTitle: Boolean,
|
||||
@SerializedName("retroAchievementsHash")
|
||||
val retroAchievementsHash: String,
|
||||
)
|
@ -0,0 +1,17 @@
|
||||
package me.magnum.melonds.migrations.legacy
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
class RomGbaSlotConfigDto31(
|
||||
@SerializedName("type")
|
||||
val type: Type,
|
||||
@SerializedName("gbaRomPath")
|
||||
val gbaRomPath: String?,
|
||||
@SerializedName("gbaSavePath")
|
||||
val gbaSavePath: String?,
|
||||
) {
|
||||
|
||||
enum class Type {
|
||||
None, GbaRom, MemoryExpansion
|
||||
}
|
||||
}
|
@ -2,11 +2,11 @@ package me.magnum.melonds.parcelables
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import androidx.core.net.toUri
|
||||
import me.magnum.melonds.domain.model.RomConfig
|
||||
import me.magnum.melonds.domain.model.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.model.RuntimeMicSource
|
||||
import java.util.*
|
||||
import me.magnum.melonds.domain.model.rom.config.RomConfig
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeMicSource
|
||||
import me.magnum.melonds.extensions.parcelable
|
||||
import java.util.UUID
|
||||
|
||||
class RomConfigParcelable : Parcelable {
|
||||
val romConfig: RomConfig
|
||||
@ -16,22 +16,19 @@ class RomConfigParcelable : Parcelable {
|
||||
}
|
||||
|
||||
private constructor(parcel: Parcel) {
|
||||
romConfig = RomConfig()
|
||||
romConfig.runtimeConsoleType = RuntimeConsoleType.entries[parcel.readInt()]
|
||||
romConfig.runtimeMicSource = RuntimeMicSource.entries[parcel.readInt()]
|
||||
romConfig.layoutId = parcel.readString()?.let { UUID.fromString(it) }
|
||||
romConfig.loadGbaCart = parcel.readInt() == 1
|
||||
romConfig.gbaCartPath = parcel.readString()?.toUri()
|
||||
romConfig.gbaSavePath = parcel.readString()?.toUri()
|
||||
romConfig = RomConfig(
|
||||
runtimeConsoleType = RuntimeConsoleType.entries[parcel.readInt()],
|
||||
runtimeMicSource = RuntimeMicSource.entries[parcel.readInt()],
|
||||
layoutId = parcel.readString()?.let { UUID.fromString(it) },
|
||||
gbaSlotConfig = parcel.parcelable<RomGbaSlotConfigParcelable>()!!.gbaSlotConfig,
|
||||
)
|
||||
}
|
||||
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
dest.writeInt(romConfig.runtimeConsoleType.ordinal)
|
||||
dest.writeInt(romConfig.runtimeMicSource.ordinal)
|
||||
dest.writeString(romConfig.layoutId?.toString())
|
||||
dest.writeInt(if (romConfig.loadGbaCart) 1 else 0)
|
||||
dest.writeString(romConfig.gbaCartPath?.toString())
|
||||
dest.writeString(romConfig.gbaSavePath?.toString())
|
||||
dest.writeParcelable(RomGbaSlotConfigParcelable(romConfig.gbaSlotConfig), 0)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
|
@ -0,0 +1,54 @@
|
||||
package me.magnum.melonds.parcelables
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import androidx.core.net.toUri
|
||||
import me.magnum.melonds.domain.model.rom.config.RomGbaSlotConfig
|
||||
|
||||
class RomGbaSlotConfigParcelable : Parcelable {
|
||||
val gbaSlotConfig: RomGbaSlotConfig
|
||||
|
||||
constructor(gbaSlotConfig: RomGbaSlotConfig) {
|
||||
this.gbaSlotConfig = gbaSlotConfig
|
||||
}
|
||||
|
||||
private constructor(parcel: Parcel) {
|
||||
val type = parcel.readInt()
|
||||
gbaSlotConfig = when (type) {
|
||||
TYPE_NONE -> RomGbaSlotConfig.None
|
||||
TYPE_GBA_ROM -> RomGbaSlotConfig.GbaRom(parcel.readString()?.toUri(), parcel.readString()?.toUri())
|
||||
TYPE_MEMORY_EXPANSION -> RomGbaSlotConfig.MemoryExpansion
|
||||
else -> throw UnsupportedOperationException("Unsupported GBA slot type: $type")
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
when (gbaSlotConfig) {
|
||||
is RomGbaSlotConfig.None -> parcel.writeInt(TYPE_NONE)
|
||||
is RomGbaSlotConfig.GbaRom -> {
|
||||
parcel.writeInt(TYPE_GBA_ROM)
|
||||
parcel.writeString(gbaSlotConfig.romPath?.toString())
|
||||
parcel.writeString(gbaSlotConfig.savePath?.toString())
|
||||
}
|
||||
is RomGbaSlotConfig.MemoryExpansion -> parcel.writeInt(TYPE_MEMORY_EXPANSION)
|
||||
}
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<RomGbaSlotConfigParcelable> {
|
||||
private const val TYPE_NONE = 0
|
||||
private const val TYPE_GBA_ROM = 1
|
||||
private const val TYPE_MEMORY_EXPANSION = 2
|
||||
|
||||
override fun createFromParcel(parcel: Parcel): RomGbaSlotConfigParcelable {
|
||||
return RomGbaSlotConfigParcelable(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<RomGbaSlotConfigParcelable?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ package me.magnum.melonds.parcelables
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import androidx.core.net.toUri
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.extensions.parcelable
|
||||
import java.util.*
|
||||
|
||||
|
@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.retroachievements.RAUserAchievement
|
||||
import me.magnum.melonds.domain.repositories.RetroAchievementsRepository
|
||||
import me.magnum.melonds.domain.repositories.SettingsRepository
|
||||
|
@ -4,7 +4,7 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.*
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.repositories.RomsRepository
|
||||
import me.magnum.melonds.domain.repositories.SettingsRepository
|
||||
import me.magnum.melonds.impl.RomIconProvider
|
||||
|
@ -1,6 +1,6 @@
|
||||
package me.magnum.melonds.ui.dsiwaremanager.model
|
||||
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
|
||||
sealed class DSiWareMangerRomListUiState {
|
||||
object Loading : DSiWareMangerRomListUiState()
|
||||
|
@ -41,7 +41,7 @@ import me.magnum.melonds.common.Permission
|
||||
import me.magnum.melonds.common.contracts.FilePickerContract
|
||||
import me.magnum.melonds.domain.model.ConfigurationDirResult
|
||||
import me.magnum.melonds.domain.model.DSiWareTitle
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.RomIconFiltering
|
||||
import me.magnum.melonds.domain.model.dsinand.DSiWareTitleFileType
|
||||
import me.magnum.melonds.ui.common.FabActionItem
|
||||
|
@ -43,8 +43,8 @@ import androidx.core.graphics.createBitmap
|
||||
import androidx.core.graphics.set
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import me.magnum.melonds.R
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.RomConfig
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.rom.config.RomConfig
|
||||
import me.magnum.melonds.domain.model.RomIconFiltering
|
||||
import me.magnum.melonds.ui.common.FullScreen
|
||||
import me.magnum.melonds.ui.dsiwaremanager.DSiWareRomListViewModel
|
||||
|
@ -24,8 +24,8 @@ import androidx.compose.ui.unit.sp
|
||||
import androidx.core.graphics.createBitmap
|
||||
import androidx.core.graphics.set
|
||||
import me.magnum.melonds.R
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.RomConfig
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.rom.config.RomConfig
|
||||
import me.magnum.melonds.domain.model.RomIconFiltering
|
||||
import me.magnum.melonds.ui.common.component.text.CaptionText
|
||||
import me.magnum.melonds.ui.romlist.RomIcon
|
||||
|
@ -69,7 +69,7 @@ import me.magnum.melonds.domain.model.ConsoleType
|
||||
import me.magnum.melonds.domain.model.FpsCounterPosition
|
||||
import me.magnum.melonds.domain.model.LayoutComponent
|
||||
import me.magnum.melonds.domain.model.Orientation
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.SaveStateSlot
|
||||
import me.magnum.melonds.domain.repositories.SettingsRepository
|
||||
import me.magnum.melonds.extensions.insetsControllerCompat
|
||||
|
@ -1,7 +1,7 @@
|
||||
package me.magnum.melonds.ui.emulator
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.repositories.RetroAchievementsRepository
|
||||
import me.magnum.melonds.domain.repositories.SettingsRepository
|
||||
import me.magnum.melonds.impl.emulator.EmulatorSession
|
||||
|
@ -41,7 +41,7 @@ import me.magnum.melonds.domain.model.ConsoleType
|
||||
import me.magnum.melonds.domain.model.FpsCounterPosition
|
||||
import me.magnum.melonds.domain.model.LayoutConfiguration
|
||||
import me.magnum.melonds.domain.model.Orientation
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.RomInfo
|
||||
import me.magnum.melonds.domain.model.RuntimeBackground
|
||||
import me.magnum.melonds.domain.model.SaveStateSlot
|
||||
|
@ -2,7 +2,7 @@ package me.magnum.melonds.ui.emulator.model
|
||||
|
||||
import me.magnum.melonds.MelonEmulator
|
||||
import me.magnum.melonds.domain.model.ConsoleType
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
|
||||
sealed class EmulatorState {
|
||||
data object Uninitialized : EmulatorState()
|
||||
|
@ -34,7 +34,7 @@ class RomDetailsActivity : AppCompatActivity() {
|
||||
val romRetroAchievementsViewModel by viewModels<RomDetailsRetroAchievementsViewModel>()
|
||||
|
||||
val rom by romDetailsViewModel.rom.collectAsState()
|
||||
val romConfig by romDetailsViewModel.romConfig.collectAsState()
|
||||
val romConfig by romDetailsViewModel.romConfigUiState.collectAsState()
|
||||
|
||||
val retroAchievementsUiState by romRetroAchievementsViewModel.uiState.collectAsState()
|
||||
|
||||
|
@ -2,7 +2,7 @@ package me.magnum.melonds.ui.romdetails
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.repositories.RetroAchievementsRepository
|
||||
import me.magnum.melonds.domain.repositories.SettingsRepository
|
||||
import me.magnum.melonds.parcelables.RomParcelable
|
||||
|
@ -1,11 +1,13 @@
|
||||
package me.magnum.melonds.ui.romdetails
|
||||
|
||||
import android.content.Context
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import kotlinx.coroutines.rx2.awaitSingleOrNull
|
||||
import me.magnum.melonds.domain.model.RomConfig
|
||||
import me.magnum.melonds.domain.model.rom.config.RomConfig
|
||||
import me.magnum.melonds.domain.model.rom.config.RomGbaSlotConfig
|
||||
import me.magnum.melonds.domain.repositories.LayoutsRepository
|
||||
import me.magnum.melonds.ui.romdetails.model.RomConfigUiModel
|
||||
import me.magnum.melonds.utils.FileUtils
|
||||
import me.magnum.melonds.ui.romdetails.model.RomGbaSlotConfigUiModel
|
||||
|
||||
class RomDetailsUiMapper(
|
||||
private val context: Context,
|
||||
@ -18,9 +20,19 @@ class RomDetailsUiMapper(
|
||||
runtimeMicSource = romConfig.runtimeMicSource,
|
||||
layoutId = romConfig.layoutId,
|
||||
layoutName = romConfig.layoutId?.let { layoutsRepository.getLayout(it).awaitSingleOrNull()?.name } ?: layoutsRepository.getGlobalLayoutPlaceholder().name,
|
||||
loadGbaCart = romConfig.loadGbaCart,
|
||||
gbaCartPath = FileUtils.getAbsolutePathFromSAFUri(context, romConfig.gbaCartPath),
|
||||
gbaSavePath = FileUtils.getAbsolutePathFromSAFUri(context, romConfig.gbaSavePath),
|
||||
gbaSlotConfig = mapGbaSlotConfigToUi(romConfig.gbaSlotConfig),
|
||||
)
|
||||
}
|
||||
|
||||
private fun mapGbaSlotConfigToUi(gbaSlotConfig: RomGbaSlotConfig): RomGbaSlotConfigUiModel {
|
||||
return when (gbaSlotConfig) {
|
||||
is RomGbaSlotConfig.None -> RomGbaSlotConfigUiModel(type = RomGbaSlotConfigUiModel.Type.None)
|
||||
is RomGbaSlotConfig.GbaRom -> RomGbaSlotConfigUiModel(
|
||||
type = RomGbaSlotConfigUiModel.Type.GbaRom,
|
||||
gbaRomPath = gbaSlotConfig.romPath?.let { DocumentFile.fromSingleUri(context, it)?.name },
|
||||
gbaSavePath = gbaSlotConfig.savePath?.let { DocumentFile.fromSingleUri(context, it)?.name },
|
||||
)
|
||||
is RomGbaSlotConfig.MemoryExpansion -> RomGbaSlotConfigUiModel(type = RomGbaSlotConfigUiModel.Type.MemoryExpansion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,18 +6,20 @@ import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import me.magnum.melonds.common.Permission
|
||||
import me.magnum.melonds.common.UriPermissionManager
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.RomConfig
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.rom.config.RomConfig
|
||||
import me.magnum.melonds.domain.model.rom.config.RomGbaSlotConfig
|
||||
import me.magnum.melonds.domain.repositories.RomsRepository
|
||||
import me.magnum.melonds.domain.repositories.SettingsRepository
|
||||
import me.magnum.melonds.impl.RomIconProvider
|
||||
import me.magnum.melonds.parcelables.RomParcelable
|
||||
import me.magnum.melonds.ui.romdetails.model.RomConfigUiState
|
||||
import me.magnum.melonds.ui.romdetails.model.RomConfigUpdateEvent
|
||||
import me.magnum.melonds.ui.romdetails.model.RomGbaSlotConfigUiModel
|
||||
import me.magnum.melonds.ui.romlist.RomIcon
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -34,25 +36,51 @@ class RomDetailsViewModel @Inject constructor(
|
||||
private val _rom = MutableStateFlow(savedStateHandle.get<RomParcelable>(RomDetailsActivity.KEY_ROM)!!.rom)
|
||||
val rom = _rom.asStateFlow()
|
||||
|
||||
private val _romConfig = MutableStateFlow<RomConfigUiState>(RomConfigUiState.Loading)
|
||||
val romConfig by lazy {
|
||||
updateRomConfigState()
|
||||
_romConfig.asStateFlow()
|
||||
private val _romConfig = MutableStateFlow(_rom.value.config)
|
||||
|
||||
val romConfigUiState by lazy {
|
||||
val uiStateFlow = MutableStateFlow<RomConfigUiState>(RomConfigUiState.Loading)
|
||||
viewModelScope.launch {
|
||||
_romConfig.map {
|
||||
romDetailsUiMapper.mapRomConfigToUi(it)
|
||||
}.collect {
|
||||
uiStateFlow.value = RomConfigUiState.Ready(it)
|
||||
}
|
||||
}
|
||||
|
||||
uiStateFlow.asStateFlow()
|
||||
}
|
||||
|
||||
fun onRomConfigUpdateEvent(event: RomConfigUpdateEvent) {
|
||||
val newConfig = when(event) {
|
||||
is RomConfigUpdateEvent.RuntimeConsoleUpdate -> _rom.value.config.copy(runtimeConsoleType = event.newRuntimeConsole)
|
||||
is RomConfigUpdateEvent.RuntimeMicSourceUpdate -> _rom.value.config.copy(runtimeMicSource = event.newRuntimeMicSource)
|
||||
is RomConfigUpdateEvent.LayoutUpdate -> _rom.value.config.copy(layoutId = event.newLayoutId)
|
||||
is RomConfigUpdateEvent.LoadGbaRomUpdate -> _rom.value.config.copy(loadGbaCart = event.shouldLoadRom)
|
||||
is RomConfigUpdateEvent.GbaRomPathUpdate -> _rom.value.config.copy(gbaCartPath = event.gbaRomPath)
|
||||
is RomConfigUpdateEvent.GbaSavePathUpdate -> _rom.value.config.copy(gbaSavePath = event.gbaSavePath)
|
||||
val currentRomConfig = _romConfig.value
|
||||
val newRomConfigUiModel = when(event) {
|
||||
is RomConfigUpdateEvent.RuntimeConsoleUpdate -> currentRomConfig.copy(runtimeConsoleType = event.newRuntimeConsole)
|
||||
is RomConfigUpdateEvent.RuntimeMicSourceUpdate -> currentRomConfig.copy(runtimeMicSource = event.newRuntimeMicSource)
|
||||
is RomConfigUpdateEvent.LayoutUpdate -> currentRomConfig.copy(layoutId = event.newLayoutId)
|
||||
is RomConfigUpdateEvent.GbaSlotTypeUpdated -> currentRomConfig.let {
|
||||
val newGbaSlotConfig = when (event.type) {
|
||||
RomGbaSlotConfigUiModel.Type.None -> RomGbaSlotConfig.None
|
||||
RomGbaSlotConfigUiModel.Type.GbaRom -> RomGbaSlotConfig.GbaRom(null, null)
|
||||
RomGbaSlotConfigUiModel.Type.MemoryExpansion -> RomGbaSlotConfig.MemoryExpansion
|
||||
}
|
||||
it.copy(gbaSlotConfig = newGbaSlotConfig)
|
||||
}
|
||||
is RomConfigUpdateEvent.GbaRomPathUpdate -> currentRomConfig.let {
|
||||
(currentRomConfig.gbaSlotConfig as? RomGbaSlotConfig.GbaRom)?.let { gbaConfig ->
|
||||
it.copy(gbaSlotConfig = gbaConfig.copy(romPath = event.gbaRomPath))
|
||||
}
|
||||
}
|
||||
is RomConfigUpdateEvent.GbaSavePathUpdate -> currentRomConfig.let {
|
||||
(currentRomConfig.gbaSlotConfig as? RomGbaSlotConfig.GbaRom)?.let { gbaConfig ->
|
||||
it.copy(gbaSlotConfig = gbaConfig.copy(savePath = event.gbaSavePath))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_rom.update { it.copy(config = newConfig) }
|
||||
saveRomConfig(newConfig)
|
||||
updateRomConfigState()
|
||||
newRomConfigUiModel?.let {
|
||||
_romConfig.value = it
|
||||
saveRomConfig(it)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getRomIcon(rom: Rom): RomIcon {
|
||||
@ -61,16 +89,11 @@ class RomDetailsViewModel @Inject constructor(
|
||||
return RomIcon(romIconBitmap, iconFiltering)
|
||||
}
|
||||
|
||||
private fun updateRomConfigState() {
|
||||
viewModelScope.launch {
|
||||
val romConfigUiModel = romDetailsUiMapper.mapRomConfigToUi(_rom.value.config)
|
||||
_romConfig.value = RomConfigUiState.Ready(romConfigUiModel)
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveRomConfig(newConfig: RomConfig) {
|
||||
newConfig.gbaCartPath?.let { uriPermissionManager.persistFilePermissions(it, Permission.READ) }
|
||||
newConfig.gbaSavePath?.let { uriPermissionManager.persistFilePermissions(it, Permission.READ_WRITE) }
|
||||
if (newConfig.gbaSlotConfig is RomGbaSlotConfig.GbaRom) {
|
||||
newConfig.gbaSlotConfig.romPath?.let { uriPermissionManager.persistFilePermissions(it, Permission.READ) }
|
||||
newConfig.gbaSlotConfig.savePath?.let { uriPermissionManager.persistFilePermissions(it, Permission.READ_WRITE) }
|
||||
}
|
||||
romsRepository.updateRomConfig(_rom.value, newConfig)
|
||||
}
|
||||
}
|
@ -1,18 +1,13 @@
|
||||
package me.magnum.melonds.ui.romdetails.model
|
||||
|
||||
import android.net.Uri
|
||||
import me.magnum.melonds.domain.model.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.model.RuntimeMicSource
|
||||
import java.util.*
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeMicSource
|
||||
import java.util.UUID
|
||||
|
||||
data class RomConfigUiModel(
|
||||
val runtimeConsoleType: RuntimeConsoleType = RuntimeConsoleType.DEFAULT,
|
||||
val runtimeMicSource: RuntimeMicSource = RuntimeMicSource.DEFAULT,
|
||||
val layoutId: UUID? = null,
|
||||
val layoutName: String? = null,
|
||||
val loadGbaCart: Boolean = false,
|
||||
val gbaCartPath: String? = null,
|
||||
val gbaCartUri: Uri? = null,
|
||||
val gbaSavePath: String? = null,
|
||||
val gbaSaveUri: Uri? = null,
|
||||
val gbaSlotConfig: RomGbaSlotConfigUiModel = RomGbaSlotConfigUiModel()
|
||||
)
|
||||
|
@ -1,15 +1,15 @@
|
||||
package me.magnum.melonds.ui.romdetails.model
|
||||
|
||||
import android.net.Uri
|
||||
import me.magnum.melonds.domain.model.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.model.RuntimeMicSource
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeMicSource
|
||||
import java.util.UUID
|
||||
|
||||
sealed class RomConfigUpdateEvent {
|
||||
data class RuntimeConsoleUpdate(val newRuntimeConsole: RuntimeConsoleType) : RomConfigUpdateEvent()
|
||||
data class RuntimeMicSourceUpdate(val newRuntimeMicSource: RuntimeMicSource) : RomConfigUpdateEvent()
|
||||
data class LayoutUpdate(val newLayoutId: UUID?) : RomConfigUpdateEvent()
|
||||
data class LoadGbaRomUpdate(val shouldLoadRom: Boolean) : RomConfigUpdateEvent()
|
||||
data class GbaSlotTypeUpdated(val type: RomGbaSlotConfigUiModel.Type) : RomConfigUpdateEvent()
|
||||
data class GbaRomPathUpdate(val gbaRomPath: Uri?) : RomConfigUpdateEvent()
|
||||
data class GbaSavePathUpdate(val gbaSavePath: Uri?) : RomConfigUpdateEvent()
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
package me.magnum.melonds.ui.romdetails.model
|
||||
|
||||
data class RomGbaSlotConfigUiModel(
|
||||
val type: Type = Type.None,
|
||||
val gbaRomPath: String? = null,
|
||||
val gbaSavePath: String? = null,
|
||||
) {
|
||||
|
||||
enum class Type {
|
||||
None, GbaRom, MemoryExpansion
|
||||
}
|
||||
}
|
@ -4,10 +4,14 @@ import android.app.Activity
|
||||
import android.content.Intent
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.CircularProgressIndicator
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -17,18 +21,18 @@ import androidx.compose.ui.res.stringResource
|
||||
import me.magnum.melonds.R
|
||||
import me.magnum.melonds.common.Permission
|
||||
import me.magnum.melonds.common.contracts.FilePickerContract
|
||||
import me.magnum.melonds.domain.model.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.model.RuntimeMicSource
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeMicSource
|
||||
import me.magnum.melonds.ui.common.MelonPreviewSet
|
||||
import me.magnum.melonds.ui.common.preference.ActionLauncherItem
|
||||
import me.magnum.melonds.ui.common.preference.SingleChoiceItem
|
||||
import me.magnum.melonds.ui.common.preference.SwitchItem
|
||||
import me.magnum.melonds.ui.layouts.LayoutSelectorActivity
|
||||
import me.magnum.melonds.ui.romdetails.model.RomConfigUiModel
|
||||
import me.magnum.melonds.ui.romdetails.model.RomConfigUiState
|
||||
import me.magnum.melonds.ui.romdetails.model.RomConfigUpdateEvent
|
||||
import me.magnum.melonds.ui.romdetails.model.RomGbaSlotConfigUiModel
|
||||
import me.magnum.melonds.ui.theme.MelonTheme
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
|
||||
@Composable
|
||||
fun RomConfigUi(
|
||||
@ -105,12 +109,15 @@ private fun Content(
|
||||
}
|
||||
)
|
||||
|
||||
SwitchItem(
|
||||
name = stringResource(id = R.string.label_rom_config_load_gba_rom),
|
||||
isOn = romConfig.loadGbaCart,
|
||||
onToggle = {
|
||||
onConfigUpdate(RomConfigUpdateEvent.LoadGbaRomUpdate(it))
|
||||
},
|
||||
val gbaSlotOptions = stringArrayResource(id = R.array.gba_slot_options)
|
||||
SingleChoiceItem(
|
||||
name = stringResource(id = R.string.label_rom_config_gba_slot),
|
||||
value = gbaSlotOptions[romConfig.gbaSlotConfig.type.ordinal],
|
||||
items = gbaSlotOptions.toList(),
|
||||
selectedItemIndex = romConfig.gbaSlotConfig.type.ordinal,
|
||||
onItemSelected = {
|
||||
onConfigUpdate(RomConfigUpdateEvent.GbaSlotTypeUpdated(RomGbaSlotConfigUiModel.Type.entries[it]))
|
||||
}
|
||||
)
|
||||
|
||||
val gbaRomSelectorLauncher = rememberLauncherForActivityResult(FilePickerContract(Permission.READ)) { result ->
|
||||
@ -118,28 +125,33 @@ private fun Content(
|
||||
onConfigUpdate(RomConfigUpdateEvent.GbaRomPathUpdate(result))
|
||||
}
|
||||
}
|
||||
ActionLauncherItem(
|
||||
name = stringResource(id = R.string.label_rom_config_gba_rom_path),
|
||||
value = romConfig.gbaCartPath ?: stringResource(id = R.string.not_set),
|
||||
enabled = romConfig.loadGbaCart,
|
||||
onLaunchAction = {
|
||||
gbaRomSelectorLauncher.launch(Pair(romConfig.gbaCartUri, null))
|
||||
}
|
||||
)
|
||||
|
||||
val gbaSaveSelectorLauncher = rememberLauncherForActivityResult(FilePickerContract(Permission.READ_WRITE)) { result ->
|
||||
if (result != null) {
|
||||
onConfigUpdate(RomConfigUpdateEvent.GbaSavePathUpdate(result))
|
||||
}
|
||||
}
|
||||
ActionLauncherItem(
|
||||
name = stringResource(id = R.string.label_rom_config_gba_save_path),
|
||||
value = romConfig.gbaSavePath ?: stringResource(id = R.string.not_set),
|
||||
enabled = romConfig.loadGbaCart,
|
||||
onLaunchAction = {
|
||||
gbaSaveSelectorLauncher.launch(Pair(romConfig.gbaSaveUri, null))
|
||||
|
||||
AnimatedVisibility(visible = romConfig.gbaSlotConfig.type == RomGbaSlotConfigUiModel.Type.GbaRom) {
|
||||
Column {
|
||||
ActionLauncherItem(
|
||||
name = stringResource(id = R.string.label_rom_config_gba_rom_path),
|
||||
value = romConfig.gbaSlotConfig.gbaRomPath ?: stringResource(id = R.string.not_set),
|
||||
enabled = true,
|
||||
onLaunchAction = {
|
||||
gbaRomSelectorLauncher.launch(Pair(null, null))
|
||||
}
|
||||
)
|
||||
|
||||
ActionLauncherItem(
|
||||
name = stringResource(id = R.string.label_rom_config_gba_save_path),
|
||||
value = romConfig.gbaSlotConfig.gbaSavePath ?: stringResource(id = R.string.not_set),
|
||||
enabled = true,
|
||||
onLaunchAction = {
|
||||
gbaSaveSelectorLauncher.launch(Pair(null, null))
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,6 +164,7 @@ private fun PreviewRomConfigUi() {
|
||||
romConfigUiState = RomConfigUiState.Ready(
|
||||
RomConfigUiModel(
|
||||
layoutName = "Default",
|
||||
gbaSlotConfig = RomGbaSlotConfigUiModel(type = RomGbaSlotConfigUiModel.Type.GbaRom)
|
||||
),
|
||||
),
|
||||
onConfigUpdate = { },
|
||||
|
@ -10,8 +10,8 @@ import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import kotlinx.coroutines.launch
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.RomConfig
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.rom.config.RomConfig
|
||||
import me.magnum.melonds.ui.common.MelonPreviewSet
|
||||
import me.magnum.melonds.ui.romdetails.model.*
|
||||
import me.magnum.melonds.ui.theme.MelonTheme
|
||||
|
@ -33,8 +33,8 @@ import com.google.accompanist.pager.ExperimentalPagerApi
|
||||
import com.google.accompanist.pager.pagerTabIndicatorOffset
|
||||
import kotlinx.coroutines.launch
|
||||
import me.magnum.melonds.R
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.RomConfig
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.rom.config.RomConfig
|
||||
import me.magnum.melonds.ui.common.MelonPreviewSet
|
||||
import me.magnum.melonds.ui.romdetails.model.RomDetailsTab
|
||||
import me.magnum.melonds.ui.theme.MelonTheme
|
||||
|
@ -1,222 +0,0 @@
|
||||
package me.magnum.melonds.ui.romlist
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import io.reactivex.disposables.Disposable
|
||||
import me.magnum.melonds.R
|
||||
import me.magnum.melonds.common.Permission
|
||||
import me.magnum.melonds.databinding.DialogRomConfigBinding
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.RomConfig
|
||||
import me.magnum.melonds.domain.model.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.model.RuntimeMicSource
|
||||
import me.magnum.melonds.extensions.setViewEnabledRecursive
|
||||
import me.magnum.melonds.parcelables.RomConfigParcelable
|
||||
import me.magnum.melonds.parcelables.RomParcelable
|
||||
import me.magnum.melonds.ui.layouts.LayoutSelectorActivity
|
||||
import me.magnum.melonds.common.contracts.FilePickerContract
|
||||
import me.magnum.melonds.extensions.isMicrophonePermissionGranted
|
||||
import me.magnum.melonds.extensions.parcelable
|
||||
import me.magnum.melonds.utils.FileUtils
|
||||
import java.util.*
|
||||
|
||||
class RomConfigDialog : DialogFragment() {
|
||||
companion object {
|
||||
private const val KEY_TITLE = "title"
|
||||
private const val KEY_ROM = "rom"
|
||||
private const val KEY_ROM_CONFIG = "rom_config"
|
||||
|
||||
fun newInstance(title: String, rom: Rom): RomConfigDialog {
|
||||
return RomConfigDialog().apply {
|
||||
arguments = bundleOf(
|
||||
KEY_TITLE to title,
|
||||
KEY_ROM to RomParcelable(rom)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val romListViewModel: RomListViewModel by activityViewModels()
|
||||
private lateinit var binding: DialogRomConfigBinding
|
||||
private var layoutNameDisposable: Disposable? = null
|
||||
|
||||
private lateinit var rom: Rom
|
||||
private lateinit var romConfig: RomConfig
|
||||
|
||||
private val layoutPickerLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
val layoutId = result.data?.getStringExtra(LayoutSelectorActivity.KEY_SELECTED_LAYOUT_ID)?.let { UUID.fromString(it) }
|
||||
onLayoutIdSelected(layoutId)
|
||||
}
|
||||
}
|
||||
private val gbaRomFilePicker = registerForActivityResult(FilePickerContract(Permission.READ)) {
|
||||
if (it != null) {
|
||||
onGbaRomPathSelected(it)
|
||||
}
|
||||
}
|
||||
private val gbaSramFilePicker = registerForActivityResult(FilePickerContract(Permission.READ_WRITE)) {
|
||||
if (it != null) {
|
||||
onGbaSavePathSelected(it)
|
||||
}
|
||||
}
|
||||
private val microphonePermissionLauncher =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
|
||||
if (granted) {
|
||||
onRuntimeMicSourceSelected(RuntimeMicSource.DEVICE)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// ROM is immutable so we can use the arguments' one. Only the ROM config needs to be saved if the fragment is rebuilt
|
||||
rom = arguments?.parcelable<RomParcelable>(KEY_ROM)?.rom ?: return
|
||||
romConfig = if (savedInstanceState != null) {
|
||||
savedInstanceState.parcelable<RomConfigParcelable>(KEY_ROM_CONFIG)?.romConfig ?: return
|
||||
} else {
|
||||
rom.config.copy()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
binding = DialogRomConfigBinding.inflate(layoutInflater)
|
||||
|
||||
return AlertDialog.Builder(requireContext())
|
||||
.setView(binding.root)
|
||||
.setCancelable(true)
|
||||
.create()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
isCancelable = true
|
||||
|
||||
val title = arguments?.getString(KEY_TITLE)
|
||||
|
||||
binding.layoutPrefSystem.setOnClickListener {
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.label_rom_config_console)
|
||||
.setSingleChoiceItems(R.array.game_runtime_console_type_options, romConfig.runtimeConsoleType.ordinal) { dialog, which ->
|
||||
val newConsoleType = RuntimeConsoleType.entries[which]
|
||||
onRuntimeConsoleTypeSelected(newConsoleType)
|
||||
dialog.dismiss()
|
||||
}
|
||||
.setNegativeButton(R.string.cancel) { dialog, _ ->
|
||||
dialog.cancel()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
binding.layoutPrefRuntimeMicSource.setOnClickListener {
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.microphone_source)
|
||||
.setSingleChoiceItems(R.array.game_runtime_mic_source_options, romConfig.runtimeMicSource.ordinal) { dialog, which ->
|
||||
val newMicSource = RuntimeMicSource.entries[which]
|
||||
// Request mic permission if required
|
||||
if (newMicSource == RuntimeMicSource.DEVICE && !requireContext().isMicrophonePermissionGranted()) {
|
||||
microphonePermissionLauncher.launch(Manifest.permission.RECORD_AUDIO)
|
||||
} else {
|
||||
onRuntimeMicSourceSelected(newMicSource)
|
||||
}
|
||||
|
||||
dialog.dismiss()
|
||||
}
|
||||
.setNegativeButton(R.string.cancel) { dialog, _ ->
|
||||
dialog.cancel()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
binding.layoutPrefLayout.setOnClickListener {
|
||||
val intent = Intent(requireContext(), LayoutSelectorActivity::class.java).apply {
|
||||
putExtra(LayoutSelectorActivity.KEY_SELECTED_LAYOUT_ID, romConfig.layoutId?.toString())
|
||||
}
|
||||
layoutPickerLauncher.launch(intent)
|
||||
}
|
||||
binding.layoutPrefLoadGbaRom.setOnClickListener { binding.switchLoadGbaRom.toggle() }
|
||||
binding.layoutPrefGbaRomPath.setOnClickListener {
|
||||
gbaRomFilePicker.launch(Pair(romConfig.gbaCartPath, null))
|
||||
}
|
||||
layoutNameDisposable = romListViewModel.getLayout(romConfig.layoutId).subscribe { layout ->
|
||||
binding.textPrefLayout.text = layout.name
|
||||
}
|
||||
binding.layoutPrefGbaSavePath.setOnClickListener {
|
||||
gbaSramFilePicker.launch(Pair(romConfig.gbaSavePath, null))
|
||||
}
|
||||
binding.switchLoadGbaRom.setOnCheckedChangeListener { _, isChecked -> setLoadGbaRom(isChecked) }
|
||||
binding.textRomConfigTitle.text = title
|
||||
|
||||
onRuntimeConsoleTypeSelected(romConfig.runtimeConsoleType)
|
||||
onRuntimeMicSourceSelected(romConfig.runtimeMicSource)
|
||||
|
||||
binding.switchLoadGbaRom.isChecked = romConfig.loadGbaCart
|
||||
binding.textPrefGbaRomPath.text = getUriPathOrDefault(romConfig.gbaCartPath)
|
||||
binding.textPrefGbaSavePath.text = getUriPathOrDefault(romConfig.gbaSavePath)
|
||||
|
||||
binding.layoutPrefGbaRomPath.setViewEnabledRecursive(romConfig.loadGbaCart)
|
||||
binding.layoutPrefGbaSavePath.setViewEnabledRecursive(romConfig.loadGbaCart)
|
||||
|
||||
binding.buttonRomConfigOk.setOnClickListener {
|
||||
romListViewModel.updateRomConfig(rom, romConfig)
|
||||
dismiss()
|
||||
}
|
||||
binding.buttonRomConfigCancel.setOnClickListener { dismiss() }
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putParcelable(KEY_ROM_CONFIG, RomConfigParcelable(romConfig))
|
||||
}
|
||||
|
||||
private fun setLoadGbaRom(loadGbaRom: Boolean) {
|
||||
romConfig.loadGbaCart = loadGbaRom
|
||||
binding.layoutPrefGbaRomPath.setViewEnabledRecursive(loadGbaRom)
|
||||
binding.layoutPrefGbaSavePath.setViewEnabledRecursive(loadGbaRom)
|
||||
}
|
||||
|
||||
private fun onRuntimeConsoleTypeSelected(consoleType: RuntimeConsoleType) {
|
||||
val options = resources.getStringArray(R.array.game_runtime_console_type_options)
|
||||
romConfig.runtimeConsoleType = consoleType
|
||||
binding.textPrefRuntimeConsoleType.text = options[consoleType.ordinal]
|
||||
}
|
||||
|
||||
private fun onRuntimeMicSourceSelected(micSource: RuntimeMicSource) {
|
||||
val options = resources.getStringArray(R.array.game_runtime_mic_source_options)
|
||||
romConfig.runtimeMicSource = micSource
|
||||
binding.textPrefRuntimeMicSource.text = options[micSource.ordinal]
|
||||
}
|
||||
|
||||
private fun onLayoutIdSelected(layoutId: UUID?) {
|
||||
romConfig.layoutId = layoutId
|
||||
layoutNameDisposable?.dispose()
|
||||
layoutNameDisposable = romListViewModel.getLayout(layoutId).subscribe { layout ->
|
||||
binding.textPrefLayout.text = layout.name
|
||||
}
|
||||
}
|
||||
|
||||
private fun onGbaRomPathSelected(romFileUri: Uri) {
|
||||
romConfig.gbaCartPath = romFileUri
|
||||
binding.textPrefGbaRomPath.text = getUriPathOrDefault(romFileUri)
|
||||
}
|
||||
|
||||
private fun onGbaSavePathSelected(saveFileUri: Uri) {
|
||||
romConfig.gbaSavePath = saveFileUri
|
||||
binding.textPrefGbaSavePath.text = getUriPathOrDefault(saveFileUri)
|
||||
}
|
||||
|
||||
private fun getUriPathOrDefault(uri: Uri?): String {
|
||||
return FileUtils.getAbsolutePathFromSAFUri(requireContext(), uri) ?: getString(R.string.not_set)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
layoutNameDisposable?.dispose()
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ import me.magnum.melonds.databinding.ActivityRomListBinding
|
||||
import me.magnum.melonds.domain.model.ConfigurationDirResult
|
||||
import me.magnum.melonds.domain.model.ConsoleType
|
||||
import me.magnum.melonds.domain.model.DownloadProgress
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.SortingMode
|
||||
import me.magnum.melonds.domain.model.Version
|
||||
import me.magnum.melonds.domain.model.appupdate.AppUpdate
|
||||
|
@ -34,7 +34,7 @@ import me.magnum.melonds.R
|
||||
import me.magnum.melonds.databinding.ItemRomConfigurableBinding
|
||||
import me.magnum.melonds.databinding.ItemRomSimpleBinding
|
||||
import me.magnum.melonds.databinding.RomListFragmentBinding
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.RomIconFiltering
|
||||
import me.magnum.melonds.domain.model.RomScanningStatus
|
||||
import me.magnum.melonds.extensions.setViewEnabledRecursive
|
||||
|
@ -7,14 +7,24 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.magnum.melonds.common.DirectoryAccessValidator
|
||||
import me.magnum.melonds.common.Permission
|
||||
import me.magnum.melonds.common.UriPermissionManager
|
||||
import me.magnum.melonds.common.uridelegates.UriHandler
|
||||
import me.magnum.melonds.domain.model.*
|
||||
import me.magnum.melonds.domain.model.ConfigurationDirResult
|
||||
import me.magnum.melonds.domain.model.ConsoleType
|
||||
import me.magnum.melonds.domain.model.LayoutConfiguration
|
||||
import me.magnum.melonds.domain.model.SortingMode
|
||||
import me.magnum.melonds.domain.model.SortingOrder
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.rom.config.RuntimeConsoleType
|
||||
import me.magnum.melonds.domain.repositories.LayoutsRepository
|
||||
import me.magnum.melonds.domain.repositories.RomsRepository
|
||||
import me.magnum.melonds.domain.repositories.SettingsRepository
|
||||
@ -24,7 +34,8 @@ import me.magnum.melonds.impl.RomIconProvider
|
||||
import me.magnum.melonds.utils.EventSharedFlow
|
||||
import me.magnum.melonds.utils.SubjectSharedFlow
|
||||
import java.text.Normalizer
|
||||
import java.util.*
|
||||
import java.util.Calendar
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
@ -101,12 +112,6 @@ class RomListViewModel @Inject constructor(
|
||||
romsRepository.rescanRoms()
|
||||
}
|
||||
|
||||
fun updateRomConfig(rom: Rom, newConfig: RomConfig) {
|
||||
newConfig.gbaCartPath?.let { uriPermissionManager.persistFilePermissions(it, Permission.READ) }
|
||||
newConfig.gbaSavePath?.let { uriPermissionManager.persistFilePermissions(it, Permission.READ_WRITE) }
|
||||
romsRepository.updateRomConfig(rom, newConfig)
|
||||
}
|
||||
|
||||
fun getLayout(layoutId: UUID?): Single<LayoutConfiguration> {
|
||||
return if (layoutId == null) {
|
||||
Single.just(layoutsRepository.getGlobalLayoutPlaceholder())
|
||||
|
@ -16,7 +16,7 @@ import androidx.lifecycle.lifecycleScope
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
import me.magnum.melonds.R
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.rom.Rom
|
||||
import me.magnum.melonds.domain.model.RomIconFiltering
|
||||
import me.magnum.melonds.ui.emulator.EmulatorActivity
|
||||
import me.magnum.melonds.ui.romlist.RomIcon
|
||||
|
@ -1,231 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_rom_config_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="start"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="?attr/dialogPreferredPadding"
|
||||
android:paddingRight="?attr/dialogPreferredPadding"
|
||||
android:paddingTop="18dp"
|
||||
android:paddingBottom="8dp"
|
||||
app:layout_constraintTop_toTopOf="parent" >
|
||||
|
||||
<androidx.appcompat.widget.DialogTitle
|
||||
android:id="@+id/textRomConfigTitle"
|
||||
style="?android:attr/windowTitleStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
tools:text="Super Mario 64 DS"/>
|
||||
</LinearLayout>
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/layout_rom_config_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:fillViewport="true"
|
||||
android:orientation="vertical"
|
||||
android:scrollbars="vertical"
|
||||
android:layout_weight="1">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutPrefSystem"
|
||||
style="@style/Layout.RomSetting">
|
||||
|
||||
<LinearLayout
|
||||
style="@style/Layout.RomSettingContent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Text.RomSetting.Title"
|
||||
android:text="@string/label_rom_config_console"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textPrefRuntimeConsoleType"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Text.RomSetting.Summary"
|
||||
android:singleLine="true"
|
||||
tools:text="Default" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutPrefRuntimeMicSource"
|
||||
style="@style/Layout.RomSetting">
|
||||
|
||||
<LinearLayout
|
||||
style="@style/Layout.RomSettingContent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Text.RomSetting.Title"
|
||||
android:text="@string/microphone_source"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textPrefRuntimeMicSource"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Text.RomSetting.Summary"
|
||||
android:singleLine="true"
|
||||
tools:text="Default" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutPrefLayout"
|
||||
style="@style/Layout.RomSetting">
|
||||
|
||||
<LinearLayout
|
||||
style="@style/Layout.RomSettingContent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Text.RomSetting.Title"
|
||||
android:text="@string/controller_layout" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textPrefLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Text.RomSetting.Summary"
|
||||
android:singleLine="true"
|
||||
tools:text="Default" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutPrefLoadGbaRom"
|
||||
style="@style/Layout.RomSetting"
|
||||
android:minHeight="48dp">
|
||||
|
||||
<RelativeLayout
|
||||
style="@style/Layout.RomSettingContent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/label_preference_load_gba_cart"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Text.RomSetting.Title"
|
||||
android:text="@string/label_rom_config_load_gba_rom"/>
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/switchLoadGbaRom"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="false"
|
||||
android:clickable="false"
|
||||
android:background="@null" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutPrefGbaRomPath"
|
||||
style="@style/Layout.RomSetting" >
|
||||
|
||||
<LinearLayout
|
||||
style="@style/Layout.RomSettingContent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/label_preference_gba_cart_path"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Text.RomSetting.Title"
|
||||
android:text="@string/label_rom_config_gba_rom_path"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textPrefGbaRomPath"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Text.RomSetting.Summary"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="start"
|
||||
tools:text="/storage/emulated/0/Emulators/GBA/Mario.gba" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutPrefGbaSavePath"
|
||||
style="@style/Layout.RomSetting" >
|
||||
|
||||
<LinearLayout
|
||||
style="@style/Layout.RomSettingContent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/label_preference_gba_save_path"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Text.RomSetting.Title"
|
||||
android:text="@string/label_rom_config_gba_save_path"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textPrefGbaSavePath"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Text.RomSetting.Summary"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="start"
|
||||
tools:text="/storage/emulated/0/Emulators/GBA/Mario.sav" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_rom_config_controls"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="bottom"
|
||||
android:layoutDirection="locale"
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="4dp"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingRight="12dp"
|
||||
android:paddingTop="4dp">
|
||||
|
||||
<Space
|
||||
android:id="@+id/spacer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:visibility="invisible"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_rom_config_cancel"
|
||||
style="?attr/buttonBarNegativeButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/cancel" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_rom_config_ok"
|
||||
style="?attr/buttonBarPositiveButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/ok" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
@ -238,7 +238,6 @@
|
||||
|
||||
<string name="rom_settings">Configuración de Rom</string> <!-- Cadena de accesibilidad. No debe usar acrónimos -->
|
||||
<string name="label_rom_config_console">Sistema de arranque</string>
|
||||
<string name="label_rom_config_load_gba_rom">Cargar ROM de GBA</string>
|
||||
<string name="label_rom_config_gba_rom_path">Ruta de acceso de la ROM de GBA</string>
|
||||
<string name="label_rom_config_gba_save_path">Ruta de guardado de GBA</string>
|
||||
<string name="rom_details_configuration_tab">Configuración</string>
|
||||
|
@ -239,7 +239,6 @@
|
||||
|
||||
<string name="rom_settings">Paramètres de la ROM</string>
|
||||
<string name="label_rom_config_console">Démarrer le système</string>
|
||||
<string name="label_rom_config_load_gba_rom">Charger la ROM GBA</string>
|
||||
<string name="label_rom_config_gba_rom_path">Chemin de la ROM GBA</string>
|
||||
<string name="label_rom_config_gba_save_path">Chemin de la sauvegarde GBA</string>
|
||||
<string name="rom_details_configuration_tab">Configuration</string>
|
||||
|
@ -184,7 +184,6 @@
|
||||
|
||||
<string name="rom_settings">Pengaturan rom</string> <!-- Accessibility string. Should not use acronyms -->
|
||||
<string name="label_rom_config_console">Boot sistem</string>
|
||||
<string name="label_rom_config_load_gba_rom">Muat ROM GBA</string>
|
||||
<string name="label_rom_config_gba_rom_path">Direktori ROM GBA</string>
|
||||
<string name="label_rom_config_gba_save_path">Direktori simpanan GBA</string>
|
||||
|
||||
|
@ -218,7 +218,6 @@
|
||||
|
||||
<string name="rom_settings">Configurações da ROM</string> <!-- Cadeia de caracteres de acessibilidade. Não se deve usar acrônimos (siglas) -->
|
||||
<string name="label_rom_config_console">Sistema Inicializador</string>
|
||||
<string name="label_rom_config_load_gba_rom">Carregar ROM de GBA</string>
|
||||
<string name="label_rom_config_gba_rom_path">Caminho da ROM de GBA</string>
|
||||
<string name="label_rom_config_gba_save_path">Caminho do arquivo de salvamento GBA</string>
|
||||
|
||||
|
@ -244,7 +244,6 @@
|
||||
|
||||
<string name="rom_settings">Настройки игры</string> <!-- Accessibility string. Should not use acronyms -->
|
||||
<string name="label_rom_config_console">Система загрузки</string>
|
||||
<string name="label_rom_config_load_gba_rom">Загрузить образ GBA</string>
|
||||
<string name="label_rom_config_gba_rom_path">Путь к образу GBA</string>
|
||||
<string name="label_rom_config_gba_save_path">Путь к сохранению GBA</string>
|
||||
<string name="rom_details_configuration_tab">Конфигурация</string>
|
||||
|
@ -252,7 +252,7 @@
|
||||
|
||||
<string name="rom_settings">Rom settings</string> <!-- Accessibility string. Should not use acronyms -->
|
||||
<string name="label_rom_config_console">Boot system</string>
|
||||
<string name="label_rom_config_load_gba_rom">Load GBA ROM</string>
|
||||
<string name="label_rom_config_gba_slot">GBA slot</string>
|
||||
<string name="label_rom_config_gba_rom_path">GBA ROM path</string>
|
||||
<string name="label_rom_config_gba_save_path">GBA save path</string>
|
||||
<string name="rom_details_configuration_tab">Configuration</string>
|
||||
@ -390,6 +390,12 @@
|
||||
<item>Device microphone</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="gba_slot_options">
|
||||
<item>None</item>
|
||||
<item>GBA ROM</item>
|
||||
<item>Memory expansion</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="console_type_options">
|
||||
<item>@string/console_ds</item>
|
||||
<item>@string/console_dsi</item>
|
||||
|
@ -4,6 +4,6 @@ object AppConfig {
|
||||
const val minSdkVersion = 24
|
||||
const val ndkVersion = "25.1.8937393"
|
||||
|
||||
const val versionCode = 30
|
||||
const val versionCode = 31
|
||||
const val versionName = "Beta 1.9.3"
|
||||
}
|
@ -1 +1 @@
|
||||
Subproject commit fe50a17109d18f20459e6fb8fb70a87bc8375c7b
|
||||
Subproject commit dc44fa17f9160798772cd22fcce9d3450250e2af
|
Loading…
Reference in New Issue
Block a user