diff --git a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/patcher/steps/patch/SmaliPatchStep.kt b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/patcher/steps/patch/SmaliPatchStep.kt index d78f9d3..6e76762 100644 --- a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/patcher/steps/patch/SmaliPatchStep.kt +++ b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/patcher/steps/patch/SmaliPatchStep.kt @@ -41,6 +41,7 @@ class SmaliPatchStep( val patches = mutableListOf() val localsBumps = mutableMapOf, Int>() + val disabledFiles = options.disabledPatchFiles() // Load and parse all the patches from the smali archive. container.log("Loading patches from smali patch archive: ${patchesZip.absolutePath}") @@ -71,7 +72,7 @@ class SmaliPatchStep( if (!patchFile.endsWith(".patch")) continue val basename = patchFile.substringAfterLast('/') - if (basename in options.disabledPatches) { + if (basename in disabledFiles) { container.log("Skipping disabled patch $patchFile") continue } diff --git a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/patchopts/KnownPatch.kt b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/patchopts/KnownPatch.kt index 60d0eeb..1471ed6 100644 --- a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/patchopts/KnownPatch.kt +++ b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/patchopts/KnownPatch.kt @@ -2,6 +2,8 @@ package com.meowarex.rlmobile.ui.screens.patchopts import androidx.annotation.StringRes import com.meowarex.rlmobile.R +import com.meowarex.rlmobile.ui.screens.patchopts.PatchDefault.Disabled +import com.meowarex.rlmobile.ui.screens.patchopts.PatchDefault.Enabled data class PatchVariant( @StringRes val titleRes: Int, @@ -10,30 +12,22 @@ data class PatchVariant( enum class KnownPatch( - /** - * Numeric display order in the patch options list. Lower = higher up. - * - * Convention: main patches use multiples of 10 (10, 20, 30, …). Patches - * that act as helpers/dependencies of a main patch get offsets adjacent to - * the requirer (e.g. main at 40, helpers at 41, 42, 43). DebugMenuUnlock - * is pinned to 100 to keep it at the bottom of the list. - */ - val order: Int, + val order: Int, // Patch order in the UI List (lower = higher up) [Main Patches: multiples of 10 | Sub Patches: multiples of 1] val fileNames: List, @StringRes val titleRes: Int, @StringRes val descRes: Int, + val default: PatchDefault, // Default state of the patch in the UI List (enabled/disabled) val requires: List = emptyList(), val disables: List = emptyList(), val variants: List = emptyList(), val defaultVariantIndex: Int = 0, ) { - // Dependency-first order (later refs need backward resolution). - // The `order` field controls display order; declaration order doesn't matter. LyricsDisableCover( order = 41, fileNames = listOf("lyrics-disable-cover.patch"), titleRes = R.string.patch_lyrics_disable_cover_title, descRes = R.string.patch_lyrics_disable_cover_desc, + default = Enabled, ), LyricsReplaceLyricsButton( order = 42, @@ -43,12 +37,14 @@ enum class KnownPatch( ), titleRes = R.string.patch_lyrics_replace_button_title, descRes = R.string.patch_lyrics_replace_button_desc, + default = Enabled, ), LyricsReplaceShareButton( order = 43, fileNames = listOf("lyrics-replace-share-button.patch"), titleRes = R.string.patch_lyrics_replace_share_button_title, descRes = R.string.patch_lyrics_replace_share_button_desc, + default = Enabled, ), LyricsRlApi( order = 20, @@ -58,30 +54,35 @@ enum class KnownPatch( ), titleRes = R.string.patch_lyrics_rl_api_title, descRes = R.string.patch_lyrics_rl_api_desc, + default = Disabled, ), LyricsKeepControlsVisible( order = 60, fileNames = listOf("lyrics-keep-controls-visible.patch"), titleRes = R.string.patch_lyrics_keep_controls_title, descRes = R.string.patch_lyrics_keep_controls_desc, + default = Enabled, ), PlayerBackdrop( order = 30, fileNames = listOf("player-backdrop.patch"), titleRes = R.string.patch_player_backdrop_title, descRes = R.string.patch_player_backdrop_desc, + default = Enabled, ), QualityBadgeColors( order = 36, fileNames = listOf("player-quality-badge-colors.patch"), titleRes = R.string.patch_quality_badge_colors_title, descRes = R.string.patch_quality_badge_colors_desc, + default = Enabled, ), PlayerOneHanded( order = 37, fileNames = listOf("player-one-handed.patch"), titleRes = R.string.patch_player_one_handed_title, descRes = R.string.patch_player_one_handed_desc, + default = Disabled, ), CoverEverywhere( order = 35, @@ -92,12 +93,14 @@ enum class KnownPatch( ), titleRes = R.string.patch_cover_everywhere_title, descRes = R.string.patch_cover_everywhere_desc, + default = Disabled, ), DebugMenuUnlock( order = 100, fileNames = listOf("debug-menu-unlock.patch"), titleRes = R.string.patch_debug_menu_unlock_title, descRes = R.string.patch_debug_menu_unlock_desc, + default = Disabled, ), LyricsProgressPill( order = 40, @@ -107,6 +110,7 @@ enum class KnownPatch( ), titleRes = R.string.patch_lyrics_progress_pill_title, descRes = R.string.patch_lyrics_progress_pill_desc, + default = Enabled, requires = listOf(LyricsDisableCover, LyricsReplaceLyricsButton, LyricsReplaceShareButton), ), MiniPlayerRedesign( @@ -114,6 +118,7 @@ enum class KnownPatch( fileNames = emptyList(), titleRes = R.string.patch_mini_player_redesign_title, descRes = R.string.patch_mini_player_redesign_desc, + default = Disabled, defaultVariantIndex = 2, variants = listOf( PatchVariant( @@ -135,6 +140,7 @@ enum class KnownPatch( fileNames = listOf("enable-legacy-ui.patch"), titleRes = R.string.patch_enable_legacy_ui_title, descRes = R.string.patch_enable_legacy_ui_desc, + default = Disabled, requires = listOf(DebugMenuUnlock), disables = listOf( LyricsDisableCover, @@ -149,14 +155,7 @@ enum class KnownPatch( ), ); - val allVariantFileNames: Set - get() = variants.flatMapTo(mutableSetOf()) { it.fileNames } - companion object { - /** - * Sorted by `order` ascending. Tie-breaks fall back to the first filename - * (alphabetical) so the order is always deterministic. - */ val All: List = entries.sortedWith( compareBy({ it.order }, { it.fileNames.firstOrNull() ?: it.name }) ) diff --git a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/patchopts/PatchDefault.kt b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/patchopts/PatchDefault.kt new file mode 100644 index 0000000..4a44372 --- /dev/null +++ b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/patchopts/PatchDefault.kt @@ -0,0 +1,6 @@ +package com.meowarex.rlmobile.ui.screens.patchopts + +enum class PatchDefault(val isEnabled: Boolean) { + Enabled(true), + Disabled(false), +} diff --git a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/patchopts/PatchOptions.kt b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/patchopts/PatchOptions.kt index 6a6de7f..36955a2 100644 --- a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/patchopts/PatchOptions.kt +++ b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/patchopts/PatchOptions.kt @@ -32,28 +32,34 @@ data class PatchOptions( */ val customPatches: PatchComponent? = null, - val disabledPatches: Set = emptySet(), + val patchStates: Map = emptyMap(), val selectedVariants: Map = emptyMap(), ) : Parcelable { - companion object { - val Default: PatchOptions = run { - val miniPlayerFiles = KnownPatch.MiniPlayerRedesign.allVariantFileNames - val disabled = ( - KnownPatch.DebugMenuUnlock.fileNames + - KnownPatch.EnableLegacyUi.fileNames + - KnownPatch.PlayerOneHanded.fileNames + - miniPlayerFiles - ).toSet() - PatchOptions( - appName = "TIDAL", - packageName = "com.aspiro.tidal", - debuggable = false, - customTidalApk = null, - customPatches = null, - disabledPatches = disabled, - ) + fun isEnabled(patch: KnownPatch): Boolean = + patchStates[patch.name] ?: patch.default.isEnabled + + fun disabledPatchFiles(): Set = buildSet { + for (patch in KnownPatch.All) { + val enabled = isEnabled(patch) + if (patch.variants.isEmpty()) { + if (!enabled) addAll(patch.fileNames) + } else { + val selected = (selectedVariants[patch.name] ?: patch.defaultVariantIndex) + .coerceIn(0, patch.variants.lastIndex) + patch.variants.forEachIndexed { index, variant -> + if (!enabled || index != selected) addAll(variant.fileNames) + } + } } } + + companion object { + val Default: PatchOptions = PatchOptions( + appName = "TIDAL", + packageName = "com.aspiro.tidal", + debuggable = false, + ) + } } diff --git a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/patchopts/PatchOptionsModel.kt b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/patchopts/PatchOptionsModel.kt index a06a21f..78b90fd 100644 --- a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/patchopts/PatchOptionsModel.kt +++ b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/patchopts/PatchOptionsModel.kt @@ -47,7 +47,7 @@ class PatchOptionsModel( debuggable = value } - var disabledPatches by mutableStateOf(prefilledOptions.disabledPatches) + var patchStates by mutableStateOf(prefilledOptions.patchStates) private set var selectedVariants by mutableStateOf(prefilledOptions.selectedVariants) @@ -57,25 +57,10 @@ class PatchOptionsModel( ?.coerceIn(0, patch.variants.lastIndex.coerceAtLeast(0)) ?: patch.defaultVariantIndex.coerceIn(0, patch.variants.lastIndex.coerceAtLeast(0)) - fun isPatchEnabled(patch: KnownPatch): Boolean = if (patch.variants.isNotEmpty()) { - val v = patch.variants[variantIndex(patch)] - v.fileNames.isNotEmpty() && v.fileNames.none { it in disabledPatches } - } else { - patch.fileNames.isNotEmpty() && patch.fileNames.none { it in disabledPatches } - } + fun isPatchEnabled(patch: KnownPatch): Boolean = + patchStates[patch.name] ?: patch.default.isEnabled fun setPatchEnabled(patch: KnownPatch, enabled: Boolean) { - if (patch.variants.isNotEmpty()) { - val all = patch.allVariantFileNames - val selected = patch.variants[variantIndex(patch)].fileNames.toSet() - disabledPatches = if (enabled) { - (disabledPatches + all) - selected - } else { - disabledPatches + all - } - return - } - fun closure(seed: KnownPatch, step: (KnownPatch) -> List): Set = buildSet { fun walk(p: KnownPatch) { if (add(p)) step(p).forEach(::walk) } @@ -95,16 +80,15 @@ class PatchOptionsModel( disableUnits = closure(patch) { p -> KnownPatch.All.filter { p in it.requires } } } - val enableFiles = enableUnits.flatMap { it.fileNames }.toSet() - val disableFiles = disableUnits.flatMap { it.fileNames }.toSet() - disabledPatches = (disabledPatches - enableFiles) + disableFiles + patchStates = patchStates.toMutableMap().apply { + enableUnits.forEach { this[it.name] = true } + disableUnits.forEach { this[it.name] = false } + } } fun selectVariant(patch: KnownPatch, index: Int) { if (patch.variants.isEmpty() || index !in patch.variants.indices) return - val wasOn = isPatchEnabled(patch) selectedVariants = selectedVariants + (patch.name to index) - if (wasOn) setPatchEnabled(patch, true) } fun lockState(patch: KnownPatch): PatchLock { @@ -175,7 +159,7 @@ class PatchOptionsModel( debuggable = debuggable, customTidalApk = customTidalApk, customPatches = customPatches, - disabledPatches = disabledPatches, + patchStates = patchStates, selectedVariants = selectedVariants, ) }