Skip to content

Commit e6aeaec

Browse files
committed
feat: first attempt at an actual extra slot
1 parent c9b2258 commit e6aeaec

5 files changed

Lines changed: 62 additions & 112 deletions

File tree

extensions/extension/src/main/java/app/revanced/extension/ExampleExtension.java

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package app.revanced.extension.customfilters;
2+
3+
import java.lang.reflect.Constructor;
4+
import java.lang.reflect.Method;
5+
6+
public class TintFieldHook {
7+
public static void installTintField(Object table) {
8+
try {
9+
ClassLoader cl = table.getClass().getClassLoader();
10+
11+
Class<?> appClass = Class.forName("org.fortheloss.sticknodes.App", false, cl);
12+
Class<?> colorClass = Class.forName("com.badlogic.gdx.graphics.Color", false, cl);
13+
Class<?> labelFieldClass = Class.forName("org.fortheloss.framework.LabelColorInputIncrementField", false, cl);
14+
Class<?> figureFiltersClass = Class.forName("org.fortheloss.sticknodes.animationscreen.modules.tooltables.FigureFiltersToolTable", false, cl);
15+
16+
Constructor<?> ctor = labelFieldClass.getConstructor(
17+
android.content.Context.class,
18+
String.class, String.class, int.class,
19+
float.class, float.class, boolean.class
20+
);
21+
22+
Method getModule = figureFiltersClass.getMethod("getModule");
23+
Object module = getModule.invoke(table);
24+
Method getContext = module.getClass().getMethod("getContext");
25+
Object context = getContext.invoke(module);
26+
27+
Method localize = appClass.getMethod("localize", String.class);
28+
String tintLabel = (String) localize.invoke(null, "tint");
29+
30+
Object field = ctor.newInstance(context, tintLabel, "0.00", 4, 0.0f, 1.0f, true);
31+
32+
Method registerWidget = figureFiltersClass.getMethod("registerWidget", Object.class, int.class);
33+
registerWidget.invoke(table, field, 107);
34+
35+
Method setHighFidelity = labelFieldClass.getMethod("setHighFidelity", boolean.class);
36+
setHighFidelity.invoke(field, true);
37+
38+
Object whiteColor = colorClass.getField("WHITE").get(null);
39+
Method setValue = labelFieldClass.getMethod("setValue", colorClass);
40+
setValue.invoke(field, whiteColor);
41+
42+
} catch (Throwable t) {
43+
t.printStackTrace();
44+
}
45+
}
46+
}

patches/api/patches.api

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
public final class com/HZ/CustomFilters/CustomFiltersKt {
22
public static final fun getAddCustomFilterSlot ()Lapp/revanced/patcher/patch/BytecodePatch;
3-
public static final fun getCustomFilterFingerprint ()Lapp/revanced/patcher/Fingerprint;
43
}
54

65
public final class com/HZ/CustomFilters/FingerprintsKt {

patches/src/main/kotlin/sticknodes/Fingerprints.kt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,19 @@ import com.android.tools.smali.dexlib2.Opcode
66

77
val figureFiltersInitFingerprint = fingerprint {
88
accessFlags(AccessFlags.PUBLIC)
9-
returns("V") // constructor/initializer methods are void
10-
parameters()
11-
// The method we’re after has many `INVOKE_DIRECT` and `INVOKE_VIRTUAL`
12-
// calls for LabelInputIncrementField and LabelColorInputIncrementField
9+
returns("V")
10+
parameters("Lcom/badlogic/gdx/graphics/g2d/TextureAtlas;", "Lcom/badlogic/gdx/graphics/g2d/TextureAtlas;", "Lcom/badlogic/gdx/scenes/scene2d/utils/Drawable")
1311
opcodes(
1412
Opcode.NEW_INSTANCE,
1513
Opcode.INVOKE_DIRECT,
1614
Opcode.IPUT_OBJECT,
17-
Opcode.INVOKE_VIRTUAL
15+
Opcode.INVOKE_VIRTUAL,
16+
Opcode.IF_LT
1817
)
1918
custom { method, classDef ->
2019
classDef.type == "Lorg/fortheloss/sticknodes/animationscreen/modules/tooltables/FigureFiltersToolTable;" &&
21-
method.name == "<init>" // constructor is where UI widgets are built
20+
method.name == "initialize"
2221
}
2322
}
23+
24+

patches/src/main/kotlin/sticknodes/customFilters.kt

Lines changed: 9 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,14 @@ package com.HZ.CustomFilters
33
import app.revanced.patcher.fingerprint
44
import app.revanced.patcher.patch.bytecodePatch
55
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
6-
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
7-
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
8-
import com.HZ.CustomFilters.figureFiltersInitFingerprint
6+
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
97

10-
// Fingerprint: tweak class/method checks to match the real target in your APK
11-
val customFilterFingerprint = fingerprint {
12-
// match a void method (likely constructor or init method that creates the UI)
13-
returns("V")
14-
parameters() // no params — change if real target has params
158

16-
// Narrow by class + method name. Replace with the real class and method from JADX.
17-
custom { method, classDef ->
18-
classDef.type == "Lorg/fortheloss/sticknodes/animationscreen/modules/tooltables/FigureFiltersToolTable;"
19-
&& (method.name == "<init>" || method.name == "initFilters" || method.name == "createUI")
20-
}
21-
}
229

2310
@Suppress("unused")
2411
val AddCustomFilterSlot = bytecodePatch(
2512
name = "Custom Filter slot",
26-
description = "Adds a placeholder injection so patch project compiles; replace nop with real smali later."
13+
description = "Adds a placeholder slot for now"
2714
) {
2815
compatibleWith(
2916
"org.fortheloss.sticknodes"("4.2.5"),
@@ -32,94 +19,18 @@ val AddCustomFilterSlot = bytecodePatch(
3219
)
3320

3421
// If you have an extension file, keep this; otherwise remove
35-
// extendWith("extensions/extension.rve")
22+
extendWith("extensions/extension.rve")
3623

3724
// inside your bytecodePatch { execute { ... } } block
3825

3926
execute {
40-
val fpObj = figureFiltersInitFingerprint as Any
27+
val figureFilterInit = figureFiltersInitFingerprint.patternMatch!!.endIndex;
28+
figureFiltersInitFingerprint.method.addInstruction(0,
29+
"""
30+
invoke-static {p0}, Lapp/revanced/extension/customfilters/TintFieldHook;->installTintField(Ljava/lang/Object;)V
31+
""".trimIndent()
4132

42-
fun tryInvokeNoArg(obj: Any, names: List<String>): Any? {
43-
for (n in names) {
44-
try {
45-
val m = obj::class.java.getMethod(n)
46-
if (m.parameterCount == 0) return m.invoke(obj)
47-
} catch (_: Throwable) {
48-
}
49-
}
50-
return null
51-
}
52-
53-
fun tryGetField(obj: Any, names: List<String>): Any? {
54-
for (n in names) {
55-
try {
56-
val f = obj::class.java.getDeclaredField(n)
57-
f.isAccessible = true
58-
return f.get(obj)
59-
} catch (_: Throwable) {
60-
}
61-
}
62-
return null
63-
}
64-
65-
val match =
66-
tryInvokeNoArg(fpObj, listOf("invoke", "match", "getMatch", "get"))
67-
?: tryGetField(fpObj, listOf("result", "match", "_result"))
68-
?: tryInvokeNoArg(fpObj, listOf("get"))
69-
70-
if (match == null) return@execute
71-
72-
val methodObj = tryInvokeNoArg(
73-
match,
74-
listOf("getMutableMethod", "toMutableMethod", "toMutable", "mutableMethod", "getMethod", "method")
75-
) ?: tryGetField(match, listOf("mutableMethod", "method"))
76-
77-
if (methodObj == null) return@execute
78-
79-
// --- SMALI instructions to add ---
80-
val smaliInstructions = listOf(
81-
// --- mGlowColorField ---
82-
"new-instance v0, Lorg/fortheloss/framework/LabelColorInputIncrementField;",
83-
"invoke-virtual {p0}, Lcom/HZ/CustomFilters/FigureFiltersToolTable;->getModule()Lorg/fortheloss/framework/Module;",
84-
"move-result-object v1",
85-
"invoke-virtual {v1}, Lorg/fortheloss/framework/Module;->getContext()Landroid/content/Context;",
86-
"move-result-object v2",
87-
"const-string v3, \"glow\"",
88-
"const-string v4, \"0.00\"",
89-
"const/4 v5, 0x4",
90-
"const/high16 v6, 0x0",
91-
"const/high16 v7, 0x40000000", // float 2.0f
92-
"const/4 v8, 0x1", // boolean true
93-
"invoke-direct {v0, v2, v3, v4, v5, v6, v7, v8}, Lorg/fortheloss/framework/LabelColorInputIncrementField;-><init>(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;IFFZ)V",
94-
"iput-object v0, p0, Lcom/HZ/CustomFilters/FigureFiltersToolTable;->mGlowColorField:Lorg/fortheloss/framework/LabelColorInputIncrementField;",
95-
"const/16 v9, 113",
96-
"invoke-virtual {p0, v0, v9}, Lcom/HZ/CustomFilters/FigureFiltersToolTable;->registerWidget(Lorg/fortheloss/framework/LabelColorInputIncrementField;I)V",
97-
"invoke-virtual {v0, 0x1}, Lorg/fortheloss/framework/LabelColorInputIncrementField;->setHighFidelity(Z)V",
98-
99-
// --- mGlowIntensityField ---
100-
"new-instance v10, Lorg/fortheloss/framework/LabelColorInputIncrementField;",
101-
"invoke-virtual {p0}, Lcom/HZ/CustomFilters/FigureFiltersToolTable;->getModule()Lorg/fortheloss/framework/Module;",
102-
"move-result-object v11",
103-
"invoke-virtual {v11}, Lorg/fortheloss/framework/Module;->getContext()Landroid/content/Context;",
104-
"move-result-object v12",
105-
"const-string v13, \"intensity\"",
106-
"const-string v14, \"0\"",
107-
"const/4 v15, 0x4",
108-
"const/high16 v16, 0x0",
109-
"const/high16 v17, 0x40000000", //float 2.0f
110-
"const/4 v18, 0x1", //boolean true
111-
"invoke-direct {v10, v12, v13, v14, v15, v16, v17, v18}, Lorg/fortheloss/framework/LabelColorInputIncrementField;-><init>(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;IFFZ)V",
112-
"iput-object v10, p0, Lcom/HZ/CustomFilters/FigureFiltersToolTable;->mGlowIntensityField:Lorg/fortheloss/framework/LabelColorInputIncrementField;",
113-
"const/16 v19, 114",
114-
"invoke-virtual {p0, v10, v19}, Lcom/HZ/CustomFilters/FigureFiltersToolTable;->registerWidget(Lorg/fortheloss/framework/LabelColorInputIncrementField;I)V",
115-
"invoke-virtual {v10, 0x1}, Lorg/fortheloss/framework/LabelColorInputIncrementField;->setHighFidelity(Z)V"
11633
)
117-
118-
val mutableMethod = methodObj as? MutableMethod ?: return@execute
119-
120-
for (instr in smaliInstructions) {
121-
mutableMethod.addInstruction(instr) // now uses the correct overload
12234
}
12335

124-
}
125-
}
36+
}

0 commit comments

Comments
 (0)