Created
June 16, 2021 12:02
-
-
Save Geolykt/d2af470e97ee5575de5187077495ea71 to your computer and use it in GitHub Desktop.
Starloader ASM mod that enables the use of .ogg and .wav files. Tested with Galimulator 4.8 to Galimulator 4.9-beta.7
This file contains hidden or 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
package de.geolykt.moremusic; | |
import java.lang.reflect.Method; | |
import org.jetbrains.annotations.NotNull; | |
import org.jetbrains.annotations.Nullable; | |
import org.objectweb.asm.Opcodes; | |
import org.objectweb.asm.tree.AbstractInsnNode; | |
import org.objectweb.asm.tree.ClassNode; | |
import org.objectweb.asm.tree.InsnList; | |
import org.objectweb.asm.tree.InsnNode; | |
import org.objectweb.asm.tree.JumpInsnNode; | |
import org.objectweb.asm.tree.LabelNode; | |
import org.objectweb.asm.tree.LdcInsnNode; | |
import org.objectweb.asm.tree.MethodNode; | |
import org.objectweb.asm.tree.TypeInsnNode; | |
import org.objectweb.asm.tree.VarInsnNode; | |
import de.geolykt.starloader.mod.Extension; | |
import net.minestom.server.extras.selfmodification.CodeModifier; | |
public class MMFCodeModifier extends CodeModifier { | |
private final @NotNull Object logger; | |
public MMFCodeModifier(@NotNull Extension ext) { | |
try { | |
Method method = Extension.class.getMethod("getLogger"); | |
logger = method.invoke(ext); | |
} catch (Exception e1) { | |
throw new RuntimeException(e1); | |
} | |
} | |
@Override | |
public @Nullable String getNamespace() { | |
return "snoddasmannen"; | |
} | |
protected boolean isInjectionTarget(MethodNode node) { | |
InsnList instructions = node.instructions; | |
for (AbstractInsnNode instruction : instructions) { | |
if (instruction instanceof LdcInsnNode) { | |
LdcInsnNode ldcInsnNode = (LdcInsnNode) instruction; | |
if (ldcInsnNode.cst.equals("data/music")) { | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
protected void logError(String message) { | |
try { | |
logger.getClass().getMethod("error", String.class).invoke(logger, message); | |
} catch (Exception e) { | |
throw new RuntimeException(e); | |
} | |
} | |
@Override | |
public boolean transform(ClassNode source) { | |
if (!source.name.equals("snoddasmannen/galimulator/AudioManager")) { | |
return false; | |
} | |
MethodNode foundNode = null; | |
for (MethodNode node : source.methods) { | |
if (isInjectionTarget(node)) { | |
if (foundNode != null) { | |
logError("Found multiple candidates for modification!"); | |
} | |
foundNode = node; | |
} | |
} | |
if (foundNode == null) { | |
logError("Unable to find correct method node!"); | |
return false; | |
} | |
try { | |
transformMethod(foundNode); | |
} catch (Throwable e) { | |
e.printStackTrace(); | |
return false; | |
} | |
return true; | |
} | |
protected void transformMethod(@NotNull MethodNode foundNode) { | |
AbstractInsnNode currentInstruction = foundNode.instructions.getFirst(); | |
InsnList instructions = foundNode.instructions; | |
// obtain the loop entrypoint | |
while (true) { | |
if (currentInstruction instanceof InsnNode && currentInstruction.getOpcode() == Opcodes.ARRAYLENGTH) { | |
break; | |
} | |
currentInstruction = currentInstruction.getNext(); | |
if (currentInstruction == null) { | |
logError("Unable to find loop start."); | |
return; | |
} | |
} | |
// obtain loop start label | |
LabelNode loopStart = null; | |
while (true) { | |
if (currentInstruction instanceof LabelNode) { | |
loopStart = (LabelNode) currentInstruction; | |
break; | |
} | |
currentInstruction = currentInstruction.getNext(); | |
if (currentInstruction == null) { | |
logError("Unable to find loop start label."); | |
return; | |
} | |
} | |
// Obtain the end of the interesting section | |
VarInsnNode aloadInsn = null; | |
LabelNode endInterestingLabel = new LabelNode(); | |
currentInstruction = loopStart; // reset pointer as the instructions after the loop end do not interest us | |
// Obtain ALOAD 0; NEW class instructions | |
while (true) { | |
if (currentInstruction == null) { | |
throw new NullPointerException("Unable to transform method. Issue not expected to actually happen."); | |
} | |
// obtain ALOAD instruction | |
if (currentInstruction instanceof VarInsnNode && currentInstruction.getOpcode() == Opcodes.ALOAD && | |
currentInstruction.getNext() != null && currentInstruction.getNext() instanceof TypeInsnNode | |
&& currentInstruction.getNext().getOpcode() == Opcodes.NEW) { | |
aloadInsn = (VarInsnNode) currentInstruction; | |
break; | |
} | |
currentInstruction = currentInstruction.getNext(); | |
if (currentInstruction == instructions.getLast()) { | |
logError("Unable to find end of interesting section. (Instructions exhausted)"); | |
return; | |
} | |
} | |
instructions.insertBefore(aloadInsn, endInterestingLabel); | |
currentInstruction = loopStart; | |
LdcInsnNode ldcMp3; // The LDC "mp3" instruction | |
while (true) { | |
if (currentInstruction == null) { | |
throw new InternalError(); // impossible to happen | |
} | |
if (currentInstruction instanceof LdcInsnNode && ((LdcInsnNode) currentInstruction).cst.equals("mp3")) { | |
ldcMp3 = (LdcInsnNode) currentInstruction; | |
break; | |
} | |
currentInstruction = currentInstruction.getNext(); | |
if (currentInstruction == instructions.getLast()) { | |
logError("Unable to find ldcMp3 instruction. (Instructions exhausted)"); | |
return; | |
} | |
} | |
// The actual modifications happen now | |
AbstractInsnNode getExtension = ldcMp3.getPrevious(); | |
AbstractInsnNode loadFile = getExtension.getPrevious(); | |
AbstractInsnNode compare = ldcMp3.getNext(); | |
JumpInsnNode ifEq = (JumpInsnNode) compare.getNext(); | |
// Replace the jump operator | |
AbstractInsnNode currentHead = new JumpInsnNode(Opcodes.IFNE, endInterestingLabel); | |
instructions.insert(compare, currentHead); | |
instructions.insert(currentHead, new JumpInsnNode(Opcodes.GOTO, ifEq.label)); | |
instructions.remove(ifEq); | |
var loadFileIsn = loadFile.clone(null); | |
instructions.insert(currentHead, loadFileIsn); | |
var getExtIsn = getExtension.clone(null); | |
instructions.insert(loadFileIsn, getExtIsn); | |
var ldcisn = new LdcInsnNode("ogg"); | |
instructions.insert(getExtIsn, ldcisn); | |
var cmpIsn = compare.clone(null); | |
instructions.insert(ldcisn, cmpIsn); | |
var ifneIsn = new JumpInsnNode(Opcodes.IFNE, endInterestingLabel); | |
instructions.insert(cmpIsn, ifneIsn); | |
currentHead = ifneIsn; | |
loadFileIsn = loadFile.clone(null); | |
instructions.insert(currentHead, loadFileIsn); | |
getExtIsn = getExtension.clone(null); | |
instructions.insert(loadFileIsn, getExtIsn); | |
ldcisn = new LdcInsnNode("wav"); | |
instructions.insert(getExtIsn, ldcisn); | |
cmpIsn = compare.clone(null); | |
instructions.insert(ldcisn, cmpIsn); | |
ifneIsn = new JumpInsnNode(Opcodes.IFNE, endInterestingLabel); | |
instructions.insert(cmpIsn, ifneIsn); | |
currentHead = ifneIsn; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment