From 08baa19b4a62e62bd103d177c3f4454de199cf16 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Thu, 23 Oct 2025 00:21:30 +0400 Subject: [PATCH] feat(Duolingo - Enable debug menu): Support latest app target (#6163) --- .../duolingo/debug/EnableDebugMenuPatch.kt | 31 ++++++++++------- .../patches/duolingo/debug/Fingerprints.kt | 33 ++++++++++++------- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/EnableDebugMenuPatch.kt b/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/EnableDebugMenuPatch.kt index 833f9dd7d..a507352e8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/EnableDebugMenuPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/EnableDebugMenuPatch.kt @@ -1,26 +1,35 @@ package app.revanced.patches.duolingo.debug -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction +import app.revanced.util.returnEarly +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @Suppress("unused") val enableDebugMenuPatch = bytecodePatch( name = "Enable debug menu", - use = false, + use = false ) { - compatibleWith("com.duolingo"("5.158.4")) + compatibleWith("com.duolingo") execute { - initializeBuildConfigProviderFingerprint.method.apply { - val insertIndex = initializeBuildConfigProviderFingerprint.patternMatch!!.startIndex - val register = getInstruction(insertIndex).registerA + // It seems all categories are allowed on release. Force this on anyway. + debugCategoryAllowOnReleaseBuildsFingerprint.method.returnEarly(true) - addInstructions( - insertIndex, - "const/4 v$register, 0x1", - ) + // Change build config debug build flag. + buildConfigProviderConstructorFingerprint.match( + buildConfigProviderToStringFingerprint.classDef + ).let { + val index = it.patternMatch!!.startIndex + + it.method.apply { + val register = getInstruction(index).registerA + addInstruction( + index + 1, + "const/4 v$register, 0x1" + ) + } } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/Fingerprints.kt index 543e40b43..c3d663c83 100644 --- a/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/duolingo/debug/Fingerprints.kt @@ -4,16 +4,25 @@ import app.revanced.patcher.fingerprint import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -/** - * The `BuildConfigProvider` class has two booleans: - * - * - `isChina`: (usually) compares "play" with "china"...except for builds in China - * - `isDebug`: compares "release" with "debug" <-- we want to force this to `true` - */ - -internal val initializeBuildConfigProviderFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) - returns("V") - opcodes(Opcode.IPUT_BOOLEAN) - strings("debug", "release", "china") +internal val debugCategoryAllowOnReleaseBuildsFingerprint = fingerprint { + returns("Z") + parameters() + custom { method, classDef -> + method.name == "getAllowOnReleaseBuilds" && classDef.type == "Lcom/duolingo/debug/DebugCategory;" + } +} + +internal val buildConfigProviderConstructorFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + parameters() + opcodes(Opcode.CONST_4) +} + +internal val buildConfigProviderToStringFingerprint = fingerprint { + parameters() + returns("Ljava/lang/String;") + strings("BuildConfigProvider(") // Partial string match. + custom { method, _ -> + method.name == "toString" + } }