[Main] Make downloading significantly better

This commit is contained in:
Beef 2023-05-11 23:39:41 +01:00
parent 123b0c1af9
commit 20cc346334
3 changed files with 46 additions and 57 deletions

View file

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<runningDeviceTargetSelectedWithDropDown>
<Target>
<type value="RUNNING_DEVICE_TARGET" />
<deviceKey>
<Key>
<type value="SERIAL_NUMBER" />
<value value="29171FDH200B0M" />
</Key>
</deviceKey>
</Target>
</runningDeviceTargetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2023-05-08T22:05:30.827281100Z" />
</component>
</project>

View file

@ -38,6 +38,7 @@ android {
dependencies {
compileOnly("de.robv.android.xposed:api:82")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9")
implementation("io.ktor:ktor-client-core:2.3.0")
implementation("io.ktor:ktor-client-cio:2.3.0")
}

View file

@ -1,35 +1,24 @@
package dev.beefers.vendetta.xposed
// Java
import java.io.File
// Kotlin
import kotlinx.coroutines.runBlocking
// Android
import java.lang.Exception
import kotlinx.coroutines.*
import android.content.res.AssetManager
import android.content.res.XModuleResources
import android.util.Log
// Xposed
import android.os.Build
import de.robv.android.xposed.IXposedHookLoadPackage
import de.robv.android.xposed.IXposedHookZygoteInit
import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XposedBridge
import de.robv.android.xposed.callbacks.XC_LoadPackage
// Ktor
import io.ktor.client.*
import io.ktor.client.call.body
import io.ktor.client.engine.cio.*
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.client.engine.cio.*
import io.ktor.client.statement.*
import io.ktor.client.plugins.*
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.http.isSuccess
import io.ktor.utils.io.reader
import java.lang.Exception
import io.ktor.http.*
class Main : IXposedHookZygoteInit, IXposedHookLoadPackage {
private lateinit var resources: XModuleResources
@ -44,34 +33,44 @@ class Main : IXposedHookZygoteInit, IXposedHookLoadPackage {
override fun handleLoadPackage(param: XC_LoadPackage.LoadPackageParam) = with(param) {
// Stop executing when we aren't in Discord's React Native context
val catalystInstance = try { classLoader.loadClass("com.facebook.react.bridge.CatalystInstanceImpl") } catch(e: ClassNotFoundException) { return }
val cache = File(appInfo.dataDir, "cache").also { it.mkdirs() }
val bundle = File(cache, "vendetta.js")
val etag = File(cache, "vendetta_etag.txt")
val scope = MainScope()
val httpJob = scope.async(Dispatchers.IO) {
try {
val client = HttpClient(CIO) {
install(HttpTimeout) { requestTimeoutMillis = 1000 }
// TODO: This should follow proper User Agent format
install(UserAgent) { agent = "VendettaXposed on ${Build.MODEL} (${Build.DEVICE}), Android ${Build.VERSION.RELEASE} (SDK ${Build.VERSION.SDK_INT})" }
}
val response: HttpResponse = client.get("https://raw.githubusercontent.com/vendetta-mod/builds/master/vendetta.js") {
headers { if (etag.exists() && bundle.exists()) append(HttpHeaders.IfNoneMatch, etag.readText()) }
}
if (response.status.value == 200) {
bundle.writeBytes(response.body())
if (response.headers.contains("Etag")) etag.writeText(response.headers["Etag"] as String)
}
return@async
} catch (e: Exception) {
Log.e("Vendetta", "Failed to download Vendetta")
}
}
val loadScriptFromAssets = catalystInstance.getDeclaredMethod("jniLoadScriptFromAssets", AssetManager::class.java, String::class.java, Boolean::class.javaPrimitiveType)
val loadScriptFromFile = catalystInstance.getDeclaredMethod("jniLoadScriptFromFile", String::class.java, String::class.java, Boolean::class.javaPrimitiveType)
val cache = File(appInfo.dataDir, "cache").also { it.mkdirs() }
val bundle = File(cache, "vendetta.js")
val etag = File(cache, "vendetta_etag.txt")
// TODO: loader config
// TODO: make fast.
// TODO: Loader config
// TODO: It's fast now, but we have the Hindi timestamps issue. Probably requires a JS side fix.
val hook = object : XC_MethodHook() {
override fun beforeHookedMethod(param: MethodHookParam): Unit = runBlocking {
Log.i("Vendetta", "beforeHookedMethod called")
try {
val client = HttpClient(CIO) { install(HttpTimeout) { requestTimeoutMillis = 3000 } }
val response: HttpResponse = client.get("https://raw.githubusercontent.com/vendetta-mod/builds/master/vendetta.js") {
headers { if (etag.exists() && bundle.exists()) append(HttpHeaders.IfNoneMatch, etag.readText()) }
}
if (response.status.isSuccess()) {
bundle.writeText(response.bodyAsText())
if (response.headers.contains("Etag")) etag.writeText(response.headers["Etag"] as String)
}
} catch (e: Exception) {
Log.e("Vendetta", "Failed to download Vendetta")
}
Log.i("Vendetta", "Exfiltrating Metro modules")
XposedBridge.invokeOriginalMethod(loadScriptFromAssets,param.thisObject, arrayOf(resources.assets, "assets://js/modules.js", true))
Log.i("Vendetta", "Executing identity snippet")
@ -82,8 +81,14 @@ class Main : IXposedHookZygoteInit, IXposedHookLoadPackage {
Log.i("Vendetta", "afterHookedMethod called")
try {
Log.i("Vendetta", "Executing Vendetta")
XposedBridge.invokeOriginalMethod(loadScriptFromFile, param.thisObject, arrayOf(bundle.absolutePath, bundle.absolutePath, param.args[2]))
scope.launch(scope.coroutineContext) {
Log.i("Vendetta", "Waiting for HTTP")
httpJob.await()
delay(500) // Magic hacky bullshit
Log.i("Vendetta", "Executing Vendetta")
XposedBridge.invokeOriginalMethod(loadScriptFromFile, param.thisObject, arrayOf(bundle.absolutePath, bundle.absolutePath, param.args[2]))
}
} catch(_: Exception) {}
}
}
@ -91,6 +96,6 @@ class Main : IXposedHookZygoteInit, IXposedHookLoadPackage {
XposedBridge.hookMethod(loadScriptFromAssets, hook)
XposedBridge.hookMethod(loadScriptFromFile, hook)
return@with;
return@with
}
}