Skip to content

Commit b944fb7

Browse files
LemonatedCatoSumAtrIXBenCat07
authored
feat(SoundCloud): Add Enable offline sync patch (#3407)
Co-authored-by: bewzusore <bewzusore> Co-authored-by: oSumAtrIX <[email protected]> Co-authored-by: BenCat07 <[email protected]>
1 parent dfd46d8 commit b944fb7

File tree

6 files changed

+132
-2
lines changed

6 files changed

+132
-2
lines changed

api/revanced-patches.api

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,12 @@ public final class app/revanced/patches/soundcloud/analytics/DisableTelemetryPat
10501050
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
10511051
}
10521052

1053+
public final class app/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch : app/revanced/patcher/patch/BytecodePatch {
1054+
public static final field INSTANCE Lapp/revanced/patches/soundcloud/offlinesync/EnableOfflineSyncPatch;
1055+
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
1056+
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
1057+
}
1058+
10531059
public final class app/revanced/patches/spotify/layout/theme/CustomThemePatch : app/revanced/patcher/patch/ResourcePatch {
10541060
public static final field INSTANCE Lapp/revanced/patches/spotify/layout/theme/CustomThemePatch;
10551061
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V

src/main/kotlin/app/revanced/patches/soundcloud/ad/HideAdsPatch.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import app.revanced.patcher.patch.annotation.CompatiblePackage
1010
import app.revanced.patcher.patch.annotation.Patch
1111
import app.revanced.patcher.util.smali.ExternalLabel
1212
import app.revanced.patches.soundcloud.ad.fingerprints.InterceptFingerprint
13-
import app.revanced.patches.soundcloud.ad.fingerprints.FeatureConstructorFingerprint
13+
import app.revanced.patches.soundcloud.shared.fingerprints.FeatureConstructorFingerprint
1414
import app.revanced.patches.soundcloud.ad.fingerprints.UserConsumerPlanConstructorFingerprint
1515
import app.revanced.util.resultOrThrow
1616

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package app.revanced.patches.soundcloud.offlinesync
2+
3+
import app.revanced.patcher.data.BytecodeContext
4+
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
5+
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
6+
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
7+
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
8+
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
9+
import app.revanced.patcher.patch.BytecodePatch
10+
import app.revanced.patcher.patch.annotation.CompatiblePackage
11+
import app.revanced.patcher.patch.annotation.Patch
12+
import app.revanced.patcher.util.smali.ExternalLabel
13+
import app.revanced.patches.soundcloud.offlinesync.fingerprints.DownloadOperationsHeaderVerificationFingerprint
14+
import app.revanced.patches.soundcloud.offlinesync.fingerprints.DownloadOperationsURLBuilderFingerprint
15+
import app.revanced.patches.soundcloud.shared.fingerprints.FeatureConstructorFingerprint
16+
import app.revanced.util.getReference
17+
import app.revanced.util.resultOrThrow
18+
import com.android.tools.smali.dexlib2.Opcode
19+
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
20+
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
21+
22+
@Patch(
23+
name = "Enable offline sync",
24+
compatiblePackages = [CompatiblePackage("com.soundcloud.android")],
25+
)
26+
@Suppress("unused")
27+
object EnableOfflineSyncPatch : BytecodePatch(
28+
setOf(
29+
FeatureConstructorFingerprint, DownloadOperationsURLBuilderFingerprint,
30+
DownloadOperationsHeaderVerificationFingerprint
31+
),
32+
) {
33+
override fun execute(context: BytecodeContext) {
34+
// Enable the feature to allow offline track syncing by modifying the JSON server response.
35+
// This method is the constructor of a class representing a "Feature" object parsed from JSON data.
36+
// p1 is the name of the feature.
37+
// p2 is true if the feature is enabled, false otherwise.
38+
FeatureConstructorFingerprint.resultOrThrow().mutableMethod.apply {
39+
val afterCheckNotNullIndex = 2
40+
41+
addInstructionsWithLabels(
42+
afterCheckNotNullIndex,
43+
"""
44+
const-string v0, "offline_sync"
45+
invoke-virtual { p1, v0 }, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
46+
move-result v0
47+
if-eqz v0, :skip
48+
const/4 p2, 0x1
49+
""",
50+
ExternalLabel("skip", getInstruction(afterCheckNotNullIndex)),
51+
)
52+
}
53+
54+
// Patch the URL builder to use the HTTPS_STREAM endpoint
55+
// instead of the offline sync endpoint to downloading the track.
56+
DownloadOperationsURLBuilderFingerprint.resultOrThrow().mutableMethod.apply {
57+
val getEndpointsEnumFieldIndex = 1
58+
val getEndpointsEnumFieldInstruction = getInstruction<OneRegisterInstruction>(getEndpointsEnumFieldIndex)
59+
60+
val targetRegister = getEndpointsEnumFieldInstruction.registerA
61+
val endpointsType = getEndpointsEnumFieldInstruction.getReference<FieldReference>()!!.type
62+
63+
replaceInstruction(
64+
getEndpointsEnumFieldIndex,
65+
"sget-object v$targetRegister, $endpointsType->HTTPS_STREAM:$endpointsType"
66+
)
67+
}
68+
69+
// The HTTPS_STREAM endpoint does not return the necessary headers for offline sync.
70+
// Mock the headers to prevent the app from crashing by setting them to empty strings.
71+
// The headers are all cosmetic and do not affect the functionality of the app.
72+
DownloadOperationsHeaderVerificationFingerprint.resultOrThrow().mutableMethod.apply {
73+
// The first three null checks need to be patched.
74+
getInstructions().asSequence().filter {
75+
it.opcode == Opcode.IF_EQZ
76+
}.take(3).map { it.location.index }.forEach { nullCheckIndex ->
77+
val headerStringRegister = getInstruction<OneRegisterInstruction>(nullCheckIndex).registerA
78+
79+
addInstruction(nullCheckIndex, "const-string v$headerStringRegister, \"\"")
80+
}
81+
}
82+
}
83+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package app.revanced.patches.soundcloud.offlinesync.fingerprints
2+
3+
import app.revanced.patcher.extensions.or
4+
import app.revanced.patcher.fingerprint.MethodFingerprint
5+
import com.android.tools.smali.dexlib2.AccessFlags
6+
import com.android.tools.smali.dexlib2.Opcode
7+
8+
internal object DownloadOperationsHeaderVerificationFingerprint : MethodFingerprint(
9+
returnType = "V",
10+
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
11+
parameters = listOf("L","L"),
12+
opcodes = listOf(
13+
Opcode.CONST_STRING,
14+
Opcode.INVOKE_VIRTUAL,
15+
Opcode.MOVE_RESULT_OBJECT,
16+
Opcode.CONST_STRING
17+
),
18+
customFingerprint = { _, classDef ->
19+
classDef.sourceFile == "DownloadOperations.kt"
20+
}
21+
)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package app.revanced.patches.soundcloud.offlinesync.fingerprints
2+
3+
import app.revanced.patcher.extensions.or
4+
import app.revanced.patcher.fingerprint.MethodFingerprint
5+
import com.android.tools.smali.dexlib2.AccessFlags
6+
import com.android.tools.smali.dexlib2.Opcode
7+
8+
internal object DownloadOperationsURLBuilderFingerprint : MethodFingerprint(
9+
returnType = "Ljava/lang/String",
10+
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
11+
parameters = listOf("L","L"),
12+
opcodes = listOf(
13+
Opcode.IGET_OBJECT,
14+
Opcode.SGET_OBJECT,
15+
Opcode.FILLED_NEW_ARRAY
16+
),
17+
customFingerprint = { _, classDef ->
18+
classDef.sourceFile == "DownloadOperations.kt"
19+
}
20+
)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package app.revanced.patches.soundcloud.ad.fingerprints
1+
package app.revanced.patches.soundcloud.shared.fingerprints
22

33
import app.revanced.patcher.extensions.or
44
import app.revanced.patcher.fingerprint.MethodFingerprint

0 commit comments

Comments
 (0)