package org.openrs2.deob.ast.transform import com.github.javaparser.ast.CompilationUnit import com.github.javaparser.ast.expr.AssignExpr import com.github.javaparser.ast.expr.BinaryExpr import com.github.javaparser.ast.expr.BinaryExpr.Operator.EQUALS import com.github.javaparser.ast.expr.BinaryExpr.Operator.GREATER import com.github.javaparser.ast.expr.BinaryExpr.Operator.GREATER_EQUALS import com.github.javaparser.ast.expr.BinaryExpr.Operator.LESS import com.github.javaparser.ast.expr.BinaryExpr.Operator.LESS_EQUALS import com.github.javaparser.ast.expr.BinaryExpr.Operator.NOT_EQUALS import com.github.javaparser.ast.expr.CharLiteralExpr import com.github.javaparser.ast.expr.Expression import com.github.javaparser.ast.expr.IntegerLiteralExpr import com.github.javaparser.ast.expr.UnaryExpr import com.github.javaparser.resolution.types.ResolvedPrimitiveType import org.openrs2.deob.ast.Library import org.openrs2.deob.ast.LibraryGroup import org.openrs2.deob.ast.util.checkedAsInt import org.openrs2.deob.ast.util.walk import java.lang.Character.CONTROL import java.lang.Character.FORMAT import java.lang.Character.LINE_SEPARATOR import java.lang.Character.PARAGRAPH_SEPARATOR import java.lang.Character.PRIVATE_USE import java.lang.Character.SURROGATE import java.lang.Character.UNASSIGNED import javax.inject.Singleton @Singleton public class CharLiteralTransformer : Transformer() { override fun transformUnit(group: LibraryGroup, library: Library, unit: CompilationUnit) { unit.walk { expr: BinaryExpr -> if (expr.operator in COMPARISON_OPERATORS) { convertToCharLiteral(expr.left, expr.right) convertToCharLiteral(expr.right, expr.left) } } unit.walk { expr: AssignExpr -> convertToCharLiteral(expr.target, expr.value) } } private fun convertToCharLiteral(a: Expression, b: Expression) { if (b !is IntegerLiteralExpr) { return } else if (a.calculateResolvedType() != ResolvedPrimitiveType.CHAR) { return } val n = b.checkedAsInt() if (n < 0) { val char = (-n).toChar() b.replace(UnaryExpr(CharLiteralExpr(escape(char)), UnaryExpr.Operator.MINUS)) } else { val char = n.toChar() b.replace(CharLiteralExpr(escape(char))) } } private fun escape(c: Char): String { // compatible with Fernflower's character escape code return when (c) { '\b' -> "\\b" '\t' -> "\\t" '\n' -> "\\n" '\u000c' -> "\\f" 'r' -> "\\r" '\'' -> "'" '\\' -> "\\" else -> { val type = Character.getType(c).toByte() if (type in UNPRINTABLE_TYPES) { "\\u" + Integer.toHexString(c.code).padStart(4, '0') } else { c.toString() } } } } private companion object { private val COMPARISON_OPERATORS = setOf(EQUALS, NOT_EQUALS, LESS, GREATER, LESS_EQUALS, GREATER_EQUALS) private val UNPRINTABLE_TYPES = setOf( UNASSIGNED, LINE_SEPARATOR, PARAGRAPH_SEPARATOR, CONTROL, FORMAT, PRIVATE_USE, SURROGATE ) } }