forked from crimera/piko
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Twitter): Added
Custom translator
patch
- Loading branch information
Showing
6 changed files
with
259 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
122 changes: 122 additions & 0 deletions
122
...lin/crimera/patches/twitter/misc/shareMenu/nativeTranslator/NativeTranslatorHooksPatch.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package crimera.patches.twitter.misc.shareMenu.nativeTranslator | ||
|
||
import app.revanced.patcher.data.BytecodeContext | ||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction | ||
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions | ||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction | ||
import app.revanced.patcher.fingerprint.MethodFingerprint | ||
import app.revanced.patcher.patch.BytecodePatch | ||
import app.revanced.patcher.patch.PatchException | ||
import app.revanced.patcher.patch.annotation.CompatiblePackage | ||
import app.revanced.patcher.patch.annotation.Patch | ||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod | ||
import com.android.tools.smali.dexlib2.Opcode | ||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction | ||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference | ||
|
||
val MethodFingerprint.exception: PatchException | ||
get() = PatchException("${this.javaClass.name} is not found") | ||
|
||
internal abstract class NativeTranslatorMethodFingerprint( | ||
private val methodName: String, | ||
) : MethodFingerprint(customFingerprint = { methodDef, classDef -> | ||
methodDef.name == methodName && classDef.toString() == "Lapp/revanced/integrations/twitter/patches/translator/Constants;" | ||
}) | ||
|
||
internal object ShortTweetObjectFingerprint : MethodFingerprint(strings = listOf("wrap(...)", "NONE")) | ||
|
||
internal object LongTweetObjectFingerprint : MethodFingerprint(strings = listOf("NoteTweet(id=", ", text=")) | ||
|
||
internal object TweetObjectFingerprint : MethodFingerprint(strings = listOf("https://x.com/%1\$s/status/%2\$d")) | ||
|
||
internal object TweetinfoObjectFingerprint : MethodFingerprint( | ||
strings = | ||
listOf( | ||
"flags", | ||
"lang", | ||
"supplemental_language", | ||
), | ||
customFingerprint = { methodDef, _ -> | ||
methodDef.parameters.size == 2 | ||
}, | ||
) | ||
|
||
internal object GetShortTextMtdFingerprint : NativeTranslatorMethodFingerprint("getShortTextMethodName") | ||
|
||
internal object GetLongTextMtdFingerprint : NativeTranslatorMethodFingerprint("getLongTextMethodName") | ||
|
||
internal object GetLongTextFldFingerprint : NativeTranslatorMethodFingerprint("getLongTextFieldName") | ||
|
||
internal object GetTweetInfoFldFingerprint : NativeTranslatorMethodFingerprint("getTweetInfoField") | ||
|
||
internal object GetLangFldFingerprint : NativeTranslatorMethodFingerprint("getLangField") | ||
|
||
@Patch( | ||
compatiblePackages = [CompatiblePackage("com.twitter.android")], | ||
) | ||
class NativeTranslatorHooksPatch : | ||
BytecodePatch( | ||
setOf( | ||
GetShortTextMtdFingerprint, | ||
GetLongTextMtdFingerprint, | ||
GetLongTextFldFingerprint, | ||
TweetObjectFingerprint, | ||
TweetinfoObjectFingerprint, | ||
ShortTweetObjectFingerprint, | ||
LongTweetObjectFingerprint, | ||
GetTweetInfoFldFingerprint, | ||
GetLangFldFingerprint, | ||
), | ||
) { | ||
private fun MutableMethod.changeFirstString(value: String) { | ||
this.getInstructions().firstOrNull { it.opcode == Opcode.CONST_STRING }?.let { instruction -> | ||
this.replaceInstruction(instruction.location.index, "const-string v0, \"$value\"") | ||
} ?: throw PatchException("const-string not found for method: ${this.name}") | ||
} | ||
|
||
override fun execute(context: BytecodeContext) { | ||
val getTweetObjectFingerprint = TweetObjectFingerprint.result ?: throw PatchException("TweetObjectFingerprint not found") | ||
val shortTweetObjectFingerprint = | ||
ShortTweetObjectFingerprint.result ?: throw PatchException("ShortTweetObjectFingerprint not found") | ||
val longTweetObjectFingerprint = LongTweetObjectFingerprint.result ?: throw PatchException("LongTweetObjectFingerprint not found") | ||
|
||
val tweetObjectClass = getTweetObjectFingerprint.classDef | ||
val methods = tweetObjectClass.methods | ||
|
||
methods.forEach { | ||
if (it.returnType.contains(shortTweetObjectFingerprint.classDef)) { | ||
GetShortTextMtdFingerprint.result?.mutableMethod?.changeFirstString(it.name) | ||
?: throw GetShortTextMtdFingerprint.exception | ||
} else if (it.returnType.contains(longTweetObjectFingerprint.classDef)) { | ||
GetLongTextMtdFingerprint.result?.mutableMethod?.changeFirstString(it.name) | ||
?: throw GetLongTextMtdFingerprint.exception | ||
} | ||
} | ||
val longTweetTextField = | ||
longTweetObjectFingerprint.classDef.fields | ||
.first { it.type == "Ljava/lang/String;" } | ||
.name | ||
|
||
GetLongTextFldFingerprint.result?.mutableMethod?.changeFirstString(longTweetTextField) | ||
?: throw GetLongTextFldFingerprint.exception | ||
|
||
TweetinfoObjectFingerprint.result ?.let { | ||
it.scanResult.stringsScanResult!!.matches.forEach { match -> | ||
if (match.string == "lang") { | ||
val ref = (it.mutableMethod.getInstruction<ReferenceInstruction>(match.index + 1).reference as FieldReference) | ||
GetLangFldFingerprint.result?.mutableMethod?.changeFirstString(ref.name) | ||
?: throw GetLangFldFingerprint.exception | ||
|
||
GetTweetInfoFldFingerprint.result?.mutableMethod?.changeFirstString( | ||
tweetObjectClass.fields | ||
.first { | ||
it.type == | ||
ref.definingClass | ||
}.name, | ||
) | ||
?: throw GetTweetInfoFldFingerprint.exception | ||
} | ||
} | ||
} | ||
} | ||
} |
104 changes: 104 additions & 0 deletions
104
...n/kotlin/crimera/patches/twitter/misc/shareMenu/nativeTranslator/NativeTranslatorPatch.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package crimera.patches.twitter.misc.shareMenu.nativeTranslator | ||
|
||
import app.revanced.patcher.data.BytecodeContext | ||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions | ||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction | ||
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions | ||
import app.revanced.patcher.patch.BytecodePatch | ||
import app.revanced.patcher.patch.PatchException | ||
import app.revanced.patcher.patch.annotation.CompatiblePackage | ||
import app.revanced.patcher.patch.annotation.Patch | ||
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch | ||
import com.android.tools.smali.dexlib2.Opcode | ||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction22c | ||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction | ||
import crimera.patches.twitter.misc.settings.SettingsPatch | ||
import crimera.patches.twitter.misc.settings.fingerprints.SettingsStatusLoadFingerprint | ||
import crimera.patches.twitter.misc.shareMenu.fingerprints.ShareMenuButtonFuncCallFingerprint | ||
import crimera.patches.twitter.misc.shareMenu.hooks.ShareMenuButtonAddHook | ||
import crimera.patches.twitter.misc.shareMenu.hooks.ShareMenuButtonHook | ||
import crimera.patches.twitter.misc.shareMenu.hooks.ShareMenuButtonInitHook | ||
import crimera.patches.twitter.misc.shareMenu.nativeDownloader.NativeDownloaderPatch | ||
|
||
@Patch( | ||
name = "Custom translator", | ||
description = "", | ||
dependencies = [SettingsPatch::class, NativeTranslatorHooksPatch::class, ResourceMappingPatch::class], | ||
compatiblePackages = [CompatiblePackage("com.twitter.android")], | ||
use = true, | ||
) | ||
@Suppress("unused") | ||
object NativeTranslatorPatch : BytecodePatch( | ||
setOf( | ||
ShareMenuButtonFuncCallFingerprint, | ||
ShareMenuButtonInitHook, | ||
SettingsStatusLoadFingerprint, | ||
ShareMenuButtonAddHook, | ||
ShareMenuButtonHook, | ||
), | ||
) { | ||
override fun execute(context: BytecodeContext) { | ||
val result = | ||
ShareMenuButtonFuncCallFingerprint.result | ||
?: throw PatchException("ShareMenuButtonFuncCallFingerprint not found") | ||
|
||
val DD = "${SettingsPatch.PATCHES_DESCRIPTOR}/translator/NativeTranslator;" | ||
|
||
val method = result.mutableMethod | ||
val instructions = method.getInstructions() | ||
|
||
// one click func | ||
var targetIndex: Int = 0 | ||
var refReg: Int = 0 | ||
result.scanResult.stringsScanResult!!.matches.forEach { stringMatch -> | ||
val str = stringMatch.string | ||
if (str.contains("click") && refReg == 0) { | ||
val movObj = method.getInstruction<TwoRegisterInstruction>(stringMatch.index - 1) | ||
refReg = movObj.registerA | ||
} else if (str.contains("spaces?id=")) { | ||
targetIndex = | ||
instructions.last { it.location.index < stringMatch.index && it.opcode == Opcode.CHECK_CAST }.location.index + 1 | ||
return@forEach | ||
} | ||
} | ||
if (targetIndex == 0 || refReg == 0) { | ||
throw PatchException("hook not found") | ||
} | ||
|
||
// inject func | ||
val postObj = method.getInstruction<BuilderInstruction22c>(targetIndex) | ||
|
||
val postObjReg = postObj.registerA | ||
val ctxReg = postObj.registerB | ||
|
||
method.addInstructions( | ||
targetIndex + 1, | ||
""" | ||
invoke-virtual/range{v$refReg .. v$refReg}, Ljava/lang/ref/Reference;->get()Ljava/lang/Object; | ||
move-result-object v$ctxReg | ||
check-cast v$ctxReg, Landroid/app/Activity; | ||
invoke-static {v$ctxReg, v$postObjReg}, $DD->translate(Landroid/content/Context;Ljava/lang/Object;)V | ||
return-void | ||
""".trimIndent(), | ||
) | ||
|
||
// show icon always | ||
val buttonReference = | ||
ShareMenuButtonHook.buttonReference("SendToSpacesSandbox") | ||
?: throw PatchException("ShareMenuButtonHook not found") | ||
|
||
ShareMenuButtonAddHook.addButton(buttonReference, "enableNativeTranslator") | ||
|
||
// text func | ||
var offset = 0 | ||
if (NativeDownloaderPatch.offset) { | ||
offset = 3 | ||
} | ||
ShareMenuButtonInitHook.setButtonText("View in Spaces Sandbox", "translate_tweet_show", offset) | ||
|
||
// icon | ||
ShareMenuButtonInitHook.setButtonIcon(buttonReference, "ic_vector_birdwatch", 0) | ||
|
||
SettingsStatusLoadFingerprint.enableSettings("nativeTranslator") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters