2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@@ -54,7 +54,7 @@ Before you submit your pull request consider the following guidelines:
|
||||
* After cloning, set a new remote [upstream](https://help.github.com/articles/configuring-a-remote-for-a-fork/) (this helps to keep your fork up to date)
|
||||
|
||||
```shell
|
||||
git remote add upstream https://github.com/runelite-extended/runelite.git
|
||||
git remote add upstream https://github.com/open-osrs/runelite.git
|
||||
```
|
||||
|
||||
* Make your changes in a new git branch:
|
||||
|
||||
25
.github/release-drafter.yml
vendored
Normal file
25
.github/release-drafter.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
version-template: '$MAJOR.$MINOR.$PATCH.0'
|
||||
name-template: 'v$NEXT_PATCH_VERSION'
|
||||
tag-template: 'v$NEXT_PATCH_VERSION'
|
||||
exclude-labels:
|
||||
- 'skip-changelog'
|
||||
- 'automated-pull-request'
|
||||
categories:
|
||||
- title: '🚀 Features'
|
||||
labels:
|
||||
- 'feature'
|
||||
- 'enhancement'
|
||||
- title: '🐛 Bug Fixes'
|
||||
labels:
|
||||
- 'fix'
|
||||
- 'bugfix'
|
||||
- 'bug'
|
||||
- title: '🧰 Maintenance'
|
||||
label: 'chore'
|
||||
change-template: '* $TITLE (#$NUMBER) @$AUTHOR'
|
||||
template: |
|
||||
Thanks to: $CONTRIBUTORS
|
||||
|
||||
## Changes
|
||||
|
||||
$CHANGES
|
||||
72
.github/workflows/build.yml
vendored
72
.github/workflows/build.yml
vendored
@@ -1,72 +0,0 @@
|
||||
name: OpenOSRS - CI
|
||||
|
||||
on: [pull_request, push]
|
||||
|
||||
jobs:
|
||||
pr-lint:
|
||||
name: PR title
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: PR title lint
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: seferov/pr-lint-action@master
|
||||
with:
|
||||
title-regex: '^([\w-/]+): \w+'
|
||||
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
name: Build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@master
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Assembling
|
||||
run: gradlew assemble --console=plain
|
||||
- name: Building
|
||||
run: gradlew build --stacktrace -x test -x checkstyleMain --console=plain
|
||||
|
||||
test:
|
||||
runs-on: windows-latest
|
||||
name: Test
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@master
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Assembling
|
||||
run: gradlew assemble --console=plain
|
||||
- name: Testing
|
||||
run: gradlew test --stacktrace --console=plain
|
||||
|
||||
checkstyle:
|
||||
name: Checkstyle
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@master
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Assembling
|
||||
run: gradlew assemble --console=plain
|
||||
- name: Checking code conventions
|
||||
run: gradlew checkstyleMain --console=plain
|
||||
|
||||
approve:
|
||||
name: Approve
|
||||
needs: [build, test, checkstyle]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Approve pull request
|
||||
if: github.event_name == 'pull_request' && github.actor == 'OpenOSRS'
|
||||
uses: hmarr/auto-approve-action@master
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
32
.github/workflows/gradle.yml
vendored
32
.github/workflows/gradle.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/checkout@v1
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x ./gradlew
|
||||
- name: Update Gradle Wrapper
|
||||
@@ -22,23 +22,23 @@ jobs:
|
||||
PULL_REQUEST_TITLE: 'project: Update gradle wrapper'
|
||||
PULL_REQUEST_BODY: This is an auto-generated PR with an updated gradle version
|
||||
COMMIT_MESSAGE: 'project: Update gradle wrapper'
|
||||
PULL_REQUEST_LABELS: automated pull request, gradle
|
||||
PULL_REQUEST_LABELS: automated-pull-request, gradle
|
||||
|
||||
update-dependencies:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x ./gradlew
|
||||
- name: Update Gradle dependencies
|
||||
run: ./gradlew useLatestVersions --console=plain
|
||||
- name: Create Gradle dependencies update Pull Request
|
||||
uses: Owain94/create-pull-request@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.OpenOSRS }}
|
||||
PULL_REQUEST_BRANCH: GRADLE-DEPENDENCY-UPDATE
|
||||
PULL_REQUEST_TITLE: 'project: Update gradle dependencies'
|
||||
PULL_REQUEST_BODY: This is an auto-generated PR with an updated gradle dependencies versions
|
||||
COMMIT_MESSAGE: 'project: Update gradle dependencies'
|
||||
PULL_REQUEST_LABELS: automated pull request, gradle
|
||||
- uses: actions/checkout@v1
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x ./gradlew
|
||||
- name: Update Gradle dependencies
|
||||
run: ./gradlew useLatestVersions --console=plain
|
||||
- name: Create Gradle dependencies update Pull Request
|
||||
uses: Owain94/create-pull-request@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.OpenOSRS }}
|
||||
PULL_REQUEST_BRANCH: GRADLE-DEPENDENCY-UPDATE
|
||||
PULL_REQUEST_TITLE: 'project: Update gradle dependencies'
|
||||
PULL_REQUEST_BODY: This is an auto-generated PR with an updated gradle dependencies versions
|
||||
COMMIT_MESSAGE: 'project: Update gradle dependencies'
|
||||
PULL_REQUEST_LABELS: automated-pull-request, gradle
|
||||
15
.github/workflows/greetings.yml
vendored
Normal file
15
.github/workflows/greetings.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
name: OpenOSRS - First interaction
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/first-interaction@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-message: 'Thank you for opening your first issue! Please make sure to join our [Discord](https://discord.gg/openosrs).'
|
||||
37
.github/workflows/merge.yml
vendored
Normal file
37
.github/workflows/merge.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: OpenOSRS - Auto merge
|
||||
|
||||
on:
|
||||
pull_request_review:
|
||||
types:
|
||||
- submitted
|
||||
|
||||
jobs:
|
||||
automerge:
|
||||
name: Auto merge
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: automerge
|
||||
uses: pascalgn/automerge-action@f84dd310ea4a19890c70a4ff11ab282a872fb94b
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
MERGE_FORKS: false
|
||||
LABELS: automated-pull-request
|
||||
AUTOMERGE: automated-pull-request
|
||||
|
||||
cleanup:
|
||||
name: Cleanup
|
||||
needs: automerge
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Extract branch name
|
||||
shell: bash
|
||||
run: echo "##[set-output name=branch;]$(echo $(jq --raw-output .pull_request.head.ref "$GITHUB_EVENT_PATH"))"
|
||||
id: extract_branch
|
||||
- name: Delete PR head branch
|
||||
uses: dawidd6/action-delete-branch@master
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: ${{ steps.extract_branch.outputs.branch }}
|
||||
be_kind: true
|
||||
78
.github/workflows/pr.yml
vendored
Normal file
78
.github/workflows/pr.yml
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
name: OpenOSRS - CI (PR)
|
||||
|
||||
on: pull_request
|
||||
|
||||
jobs:
|
||||
pr-lint:
|
||||
name: PR title
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: PR title lint
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: seferov/pr-lint-action@master
|
||||
with:
|
||||
title-regex: '^([\w-/]+): \w+'
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x ./gradlew
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Assembling
|
||||
run: ./gradlew assemble --console=plain
|
||||
- name: Building
|
||||
run: ./gradlew build --stacktrace -x test -x checkstyleMain --console=plain
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x ./gradlew
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Assembling
|
||||
run: ./gradlew assemble --console=plain
|
||||
- name: Testing
|
||||
run: ./gradlew test --stacktrace --console=plain
|
||||
|
||||
checkstyle:
|
||||
name: Checkstyle
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x ./gradlew
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Assembling
|
||||
run: ./gradlew assemble --console=plain
|
||||
- name: Checking code conventions
|
||||
run: ./gradlew checkstyleMain --console=plain
|
||||
|
||||
approve:
|
||||
name: Approve
|
||||
needs: [build, test, checkstyle]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Approve pull request
|
||||
if: github.event_name == 'pull_request' && github.actor == 'OpenOSRS'
|
||||
uses: hmarr/auto-approve-action@v2.0.0
|
||||
with:
|
||||
github-token: ${{ secrets.Owain }}
|
||||
67
.github/workflows/push.yml
vendored
Normal file
67
.github/workflows/push.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
name: OpenOSRS - CI (push)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x ./gradlew
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Assembling
|
||||
run: ./gradlew assemble --console=plain
|
||||
- name: Building
|
||||
run: ./gradlew build --stacktrace -x test -x checkstyleMain --console=plain
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x ./gradlew
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Assembling
|
||||
run: ./gradlew assemble --console=plain
|
||||
- name: Testing
|
||||
run: ./gradlew test --stacktrace --console=plain
|
||||
|
||||
checkstyle:
|
||||
name: Checkstyle
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x ./gradlew
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Assembling
|
||||
run: ./gradlew assemble --console=plain
|
||||
- name: Checking code conventions
|
||||
run: ./gradlew checkstyleMain --console=plain
|
||||
|
||||
update_draft_release:
|
||||
name: Draft release
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: toolmantim/release-drafter@v5.2.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
8
.github/workflows/scraper.yml
vendored
8
.github/workflows/scraper.yml
vendored
@@ -9,9 +9,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/checkout@v1
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@master
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Make gradlew executable
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
- name: Building scraper
|
||||
run: ./gradlew :wiki-scraper:build --console=plain
|
||||
- name: Downloading jagex cache
|
||||
run: ./gradlew :cache-client:update --console=plain
|
||||
run: ./gradlew :cache-client:download --console=plain
|
||||
- name: Scraping NPC stats
|
||||
run: ./gradlew :wiki-scraper:npcStatsScrape --console=plain
|
||||
- name: Create NPC stats Pull Request
|
||||
@@ -36,4 +36,4 @@ jobs:
|
||||
PULL_REQUEST_TITLE: 'Client: Update NPC stats'
|
||||
PULL_REQUEST_BODY: This is an auto-generated PR with changes from the OSRS wiki
|
||||
COMMIT_MESSAGE: 'Client: Update NPC stats'
|
||||
PULL_REQUEST_LABELS: automated pull request, NPC stats
|
||||
PULL_REQUEST_LABELS: automated-pull-request, NPC stats
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
|
||||
# OpenOSRS
|
||||
[](https://github.com/open-osrs/runelite/actions)
|
||||
[/badge.svg)](https://github.com/open-osrs/runelite/actions?query=workflow%3A%22OpenOSRS+-+CI+%28push%29%22)
|
||||
[](http://hits.dwyl.io/open-osrs/runelite)
|
||||
[OpenOSRS](https://openosrs.com) is an extended version of [RuneLite](https://github.com/runelite/runelite) that provides more functionality and less restrictions while staying more open-source. We are not affiliated with RuneLite.
|
||||
|
||||
@@ -54,3 +54,7 @@ OpenOSRS is licensed under the BSD 2-clause license. See the license header in t
|
||||
## Contribute and Develop
|
||||
|
||||
We've set up a separate document for our [contribution guidelines](https://github.com/open-osrs/runelite/blob/master/.github/CONTRIBUTING.md).
|
||||
|
||||
## Supported By
|
||||
|
||||
OpenOSRS uses profiling tools provided by [](https://www.yourkit.com/java/profiler/)
|
||||
|
||||
207
build.gradle
207
build.gradle
@@ -1,207 +0,0 @@
|
||||
import org.ajoberstar.grgit.Grgit
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
maven {
|
||||
url "https://plugins.gradle.org/m2/"
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.ajoberstar.grgit:grgit-gradle:3.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.adarshr.test-logger' version '1.7.0' apply false
|
||||
id "com.github.ben-manes.versions" version "0.22.0"
|
||||
id "com.gradle.build-scan" version "2.4"
|
||||
id 'se.patrikerdes.use-latest-versions' version '0.2.8'
|
||||
}
|
||||
|
||||
apply plugin: 'application'
|
||||
|
||||
ext {
|
||||
grgit = Grgit.open(dir: "${rootProject.projectDir}")
|
||||
localGitCommit = grgit.head().id
|
||||
localGitCommitShort = grgit.head().getAbbreviatedId(7)
|
||||
localGitDirty = !grgit.status().clean
|
||||
|
||||
// sets the minimum launcher version that is output for the bootstrapper
|
||||
launcherVersion = '2.0.3'
|
||||
|
||||
// Dependencies versions
|
||||
annotations = '17.0.0'
|
||||
antlr = '4.7.2'
|
||||
apacheCommonsCompress = '1.19'
|
||||
apacheCommonsCsv = '1.7'
|
||||
apacheCommonsText = '1.8'
|
||||
asm = '7.2'
|
||||
commonsCli = '1.4'
|
||||
discord = '1.1'
|
||||
fernflower = '07082019'
|
||||
findbugs = '3.0.2'
|
||||
gson = '2.8.6'
|
||||
guava = '28.1-jre'
|
||||
guice = '4.2.2'
|
||||
h2 = '1.4.200'
|
||||
hamcrest = '2.2'
|
||||
httpcore = '4.4.12'
|
||||
httpmime = '4.5.10'
|
||||
javassist = '3.26.0-GA'
|
||||
javax = '1.3.2'
|
||||
javaxInject = '1'
|
||||
jbsdiff = '1.0'
|
||||
jclCore = '2.8'
|
||||
jedis = '3.1.0'
|
||||
jfoenix = '9.0.9'
|
||||
jna = '5.4.0'
|
||||
jogamp = '2.3.2'
|
||||
jopt = '5.0.4'
|
||||
jooq = '3.12.1'
|
||||
junit = '4.12'
|
||||
jupiter = '5.5.2'
|
||||
logback = '1.2.3'
|
||||
lombok = '1.18.10'
|
||||
mapstruct = '1.3.1.Final'
|
||||
mariadbJdbc = '2.5.1'
|
||||
mavenPluginAnnotations = '3.6.0'
|
||||
mavenPluginApi = '3.6.2'
|
||||
minio = '6.0.11'
|
||||
mockito = '3.1.0'
|
||||
mongodbDriverSync = '3.11.1'
|
||||
mysqlConnectorJava = '8.0.18'
|
||||
netty = '4.1.42.Final'
|
||||
okhttp3 = '4.2.2'
|
||||
orangeExtensions = '1.0'
|
||||
petitparser = '2.2.0'
|
||||
plexus = '3.3.0'
|
||||
rxjava = '2.2.13'
|
||||
rxrelay = '2.1.1'
|
||||
scribejava = '6.9.0'
|
||||
sisu = '0.3.3'
|
||||
slf4j = '1.7.28'
|
||||
springJdbc = '5.2.0.RELEASE'
|
||||
springboot = '2.2.0.RELEASE'
|
||||
sql2o = '1.6.0'
|
||||
substance = '8.0.02'
|
||||
trident = '1.5.00'
|
||||
}
|
||||
|
||||
allprojects {
|
||||
apply plugin: 'maven'
|
||||
if (this.name != 'rs-client') apply plugin: 'checkstyle'
|
||||
|
||||
group = 'com.openosrs'
|
||||
version = '1.5.37-SNAPSHOT'
|
||||
|
||||
ext {
|
||||
rsversion = 184
|
||||
cacheversion = 165
|
||||
plusVersion = '2.1.8.0'
|
||||
|
||||
gitCommit = localGitCommit
|
||||
gitCommitShort = localGitCommitShort
|
||||
gitDirty = localGitDirty
|
||||
|
||||
|
||||
rootPath = rootDir.toString().replace('\\', '/')
|
||||
injectedClassesPath = rootPath + "/injector-plugin/out/injected-client/"
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply plugin: 'com.adarshr.test-logger'
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'maven'
|
||||
apply plugin: 'fernflower'
|
||||
apply plugin: 'maven-publish'
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
||||
tasks.withType(GroovyCompile).configureEach {
|
||||
options.incremental = true
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
name = "runelite"
|
||||
url = uri("https://maven.pkg.github.com/open-osrs/runelite")
|
||||
credentials {
|
||||
username = System.getProperty("gpr_user")
|
||||
password = System.getProperty("gpr_key")
|
||||
}
|
||||
}
|
||||
}
|
||||
publications {
|
||||
gpr(MavenPublication) {
|
||||
from(components.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
|
||||
maven { url "http://repo1.maven.org/maven2" }
|
||||
maven { url "http://repo.runelite.net" }
|
||||
maven { url "http://repo.maven.apache.org/maven2" }
|
||||
maven { url "https://raw.githubusercontent.com/open-osrs/hosting/master" }
|
||||
|
||||
if (System.getenv("NEXUS-URL") != null) {
|
||||
maven { url System.getenv("NEXUS-URL") }
|
||||
}
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = '6.4.1'
|
||||
sourceSets = [sourceSets.main]
|
||||
configFile = file("${rootDir}/checkstyle/checkstyle.xml")
|
||||
configProperties = [ "suppressionFile" : file("${rootDir}/checkstyle/suppressions.xml")]
|
||||
showViolations = true
|
||||
ignoreFailures = false
|
||||
maxWarnings = 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
wrapper {
|
||||
gradleVersion = '5.6.2'
|
||||
|
||||
doLast {
|
||||
def optsEnvVar = "DEFAULT_JVM_OPTS"
|
||||
scriptFile.write scriptFile.text.replace("$optsEnvVar='\"-Xmx64m\" \"-Xms64m\"'", "$optsEnvVar='\"-Xmx4g\" \"-Xms2g\" \"-Dfile.encoding=UTF-8\"'")
|
||||
batchScript.write batchScript.text.replace("set $optsEnvVar=\"-Xmx64m\" \"-Xms64m\"", "set $optsEnvVar=\"-Xmx4g\" \"-Xms2g\" \"-Dfile.encoding=UTF-8\"")
|
||||
}
|
||||
}
|
||||
|
||||
run {
|
||||
classpath = childProjects.client.sourceSets.main.runtimeClasspath
|
||||
mainClassName = "net.runelite.client.RuneLite"
|
||||
}
|
||||
|
||||
def isNonStable = { String version ->
|
||||
def unstableKeyword = ['ALPHA', 'BETA', 'RC'].any { it -> version.toUpperCase().contains(it) }
|
||||
|
||||
return unstableKeyword
|
||||
}
|
||||
|
||||
dependencyUpdates {
|
||||
checkForGradleUpdate = false
|
||||
|
||||
resolutionStrategy {
|
||||
componentSelection {
|
||||
all {
|
||||
if (isNonStable(candidate.version)) {
|
||||
reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
157
build.gradle.kts
Normal file
157
build.gradle.kts
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
|
||||
import org.ajoberstar.grgit.Grgit
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
maven(url = "https://plugins.gradle.org/m2/")
|
||||
maven(url = "https://raw.githubusercontent.com/open-osrs/hosting/master")
|
||||
mavenLocal()
|
||||
}
|
||||
dependencies {
|
||||
classpath(Plugins.grgitPlugin)
|
||||
classpath(Plugins.versionsPlugin)
|
||||
classpath(Plugins.injectorPlugin)
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id(Plugins.testLogger.first) version Plugins.testLogger.second apply false
|
||||
id(Plugins.versions.first) version Plugins.versions.second
|
||||
id(Plugins.buildScan.first) version Plugins.buildScan.second
|
||||
id(Plugins.latestVersion.first) version Plugins.latestVersion.second
|
||||
id(Plugins.grgit.first) version Plugins.grgit.second
|
||||
|
||||
application
|
||||
}
|
||||
|
||||
val grgit = Grgit.open(mapOf("dir" to rootProject.projectDir.absolutePath))
|
||||
val localGitCommit = grgit.head().id
|
||||
val localGitCommitShort = grgit.head().getAbbreviatedId(7)
|
||||
|
||||
fun isNonStable(version: String): Boolean {
|
||||
return listOf("ALPHA", "BETA", "RC").any {
|
||||
version.toUpperCase().contains(it)
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
apply<JavaLibraryPlugin>()
|
||||
apply<MavenPlugin>()
|
||||
apply<MavenPublishPlugin>()
|
||||
|
||||
group = "com.openosrs"
|
||||
version = ProjectVersions.rlVersion
|
||||
|
||||
project.extra["gitCommit"] = localGitCommit
|
||||
project.extra["gitCommitShort"] = localGitCommitShort
|
||||
|
||||
project.extra["rootPath"] = rootDir.toString().replace("\\", "/")
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply(plugin = Plugins.testLogger.first)
|
||||
|
||||
if (this.name != "runescape-client") {
|
||||
apply(plugin = "checkstyle")
|
||||
configure<CheckstyleExtension> {
|
||||
sourceSets = setOf(project.sourceSets.main.get())
|
||||
configFile = file("${rootDir}/checkstyle/checkstyle.xml")
|
||||
configProperties = mapOf("suppressionFile" to file("${rootDir}/checkstyle/suppressions.xml"))
|
||||
maxWarnings = 0
|
||||
toolVersion = "6.4.1"
|
||||
isShowViolations = true
|
||||
isIgnoreFailures = false
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
|
||||
maven(url = "http://repo1.maven.org/maven2")
|
||||
maven(url = "http://repo.runelite.net")
|
||||
maven(url = "http://repo.maven.apache.org/maven2")
|
||||
maven(url = "https://raw.githubusercontent.com/open-osrs/hosting/master")
|
||||
|
||||
if (System.getenv("NEXUS-URL") != null) {
|
||||
maven(url = System.getenv("NEXUS-URL"))
|
||||
}
|
||||
}
|
||||
|
||||
configure<PublishingExtension> {
|
||||
repositories {
|
||||
maven {
|
||||
name = "runelite"
|
||||
url = uri("https://maven.pkg.github.com/open-osrs/runelite")
|
||||
credentials {
|
||||
username = System.getProperty("gpr_user")
|
||||
password = System.getProperty("gpr_key")
|
||||
}
|
||||
}
|
||||
}
|
||||
publications {
|
||||
register("gpr", MavenPublication::class) {
|
||||
from(components["java"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
withType<JavaCompile> {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
application {
|
||||
mainClassName = "net.runelite.client.RuneLite"
|
||||
}
|
||||
|
||||
tasks {
|
||||
named<JavaExec>("run") {
|
||||
classpath = project(":runelite-client").sourceSets.main.get().runtimeClasspath
|
||||
}
|
||||
|
||||
named<DependencyUpdatesTask>("dependencyUpdates") {
|
||||
checkForGradleUpdate = false
|
||||
|
||||
resolutionStrategy {
|
||||
componentSelection {
|
||||
all {
|
||||
if (candidate.displayName.contains("fernflower") || isNonStable(candidate.version)) {
|
||||
reject("Non stable")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
repositories {
|
||||
maven { url "https://raw.githubusercontent.com/open-osrs/hosting/master" }
|
||||
}
|
||||
dependencies {
|
||||
compile localGroovy()
|
||||
implementation group: 'net.runelite', name: 'fernflower', version: '07082019'
|
||||
}
|
||||
43
buildSrc/build.gradle.kts
Normal file
43
buildSrc/build.gradle.kts
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven(url = "https://raw.githubusercontent.com/open-osrs/hosting/master")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(gradleApi())
|
||||
implementation(group = "net.runelite", name = "fernflower", version = "07082019")
|
||||
implementation(group = "org.json", name = "json", version = "20190722")
|
||||
}
|
||||
|
||||
kotlinDslPluginOptions {
|
||||
experimentalWarning.set(false)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.StopExecutionException
|
||||
|
||||
class FernflowerPlugin implements Plugin<Project> {
|
||||
void apply(Project project) {
|
||||
project.task('decompile', type: FernflowerTask) {
|
||||
it.dependsOn(project.tasks.jar)
|
||||
it.doFirst {
|
||||
if (!project.tasks.jar.didWork) {
|
||||
throw new StopExecutionException()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler
|
||||
|
||||
class FernflowerTask extends DefaultTask {
|
||||
List<String> extraArgs
|
||||
String inputJar
|
||||
String outputDir
|
||||
|
||||
@InputFile
|
||||
File getInputJar() {
|
||||
project.file(inputJar ?: project.buildDir.toString() + '/libs/' + project.getName() + '-' + project.version + '.jar')
|
||||
}
|
||||
|
||||
@OutputDirectory
|
||||
File getOutputDir() {
|
||||
project.file(outputDir ?: project.buildDir.toString() + '/decompiled-sources')
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void decompile() {
|
||||
getOutputDir().mkdirs()
|
||||
def args = [getInputJar().toString(), getOutputDir().toString()]
|
||||
if (extraArgs) {
|
||||
args.addAll(extraArgs)
|
||||
}
|
||||
|
||||
ConsoleDecompiler.main(args as String[])
|
||||
}
|
||||
}
|
||||
87
buildSrc/src/main/kotlin/BootstrapPlugin.kt
Normal file
87
buildSrc/src/main/kotlin/BootstrapPlugin.kt
Normal file
@@ -0,0 +1,87 @@
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.kotlin.dsl.register
|
||||
import java.io.File
|
||||
|
||||
class BootstrapPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
project.tasks.register<BootstrapTask>("bootstrapStaging") {
|
||||
dependsOn("jar")
|
||||
dependsOn("shadowJar")
|
||||
|
||||
type = "staging"
|
||||
clientJar = project.tasks["jar"].outputs.files.singleFile
|
||||
|
||||
dependsOn(project.parent!!.project(":runelite-api").tasks["jar"])
|
||||
dependsOn(project.parent!!.project(":runescape-api").tasks["jar"])
|
||||
dependsOn(project.parent!!.project(":http-api").tasks["jar"])
|
||||
dependsOn(project.parent!!.project(":injected-client").tasks["jar"])
|
||||
|
||||
doLast {
|
||||
|
||||
project.copy {
|
||||
from(project.tasks["jar"])
|
||||
from(project.parent!!.project(":runelite-api").tasks["jar"])
|
||||
from(project.parent!!.project(":runescape-api").tasks["jar"])
|
||||
from(project.parent!!.project(":http-api").tasks["jar"])
|
||||
from(project.parent!!.project(":injected-client").tasks["jar"])
|
||||
|
||||
into("${project.buildDir}/bootstrap/${type}/")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
project.tasks.register<BootstrapTask>("bootstrapStable") {
|
||||
dependsOn("jar")
|
||||
dependsOn("shadowJar")
|
||||
|
||||
type = "stable"
|
||||
clientJar = project.tasks["jar"].outputs.files.singleFile
|
||||
|
||||
dependsOn(project.parent!!.project(":runelite-api").tasks["jar"])
|
||||
dependsOn(project.parent!!.project(":runescape-api").tasks["jar"])
|
||||
dependsOn(project.parent!!.project(":http-api").tasks["jar"])
|
||||
dependsOn(project.parent!!.project(":injected-client").tasks["jar"])
|
||||
|
||||
doLast {
|
||||
|
||||
project.copy {
|
||||
from(project.tasks["jar"])
|
||||
from(project.parent!!.project(":runelite-api").tasks["jar"])
|
||||
from(project.parent!!.project(":runescape-api").tasks["jar"])
|
||||
from(project.parent!!.project(":http-api").tasks["jar"])
|
||||
from(project.parent!!.project(":injected-client").tasks["jar"])
|
||||
|
||||
into("${project.buildDir}/bootstrap/${type}/")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
project.tasks.register<BootstrapTask>("bootstrapNightly") {
|
||||
dependsOn("jar")
|
||||
dependsOn("shadowJar")
|
||||
|
||||
type = "nightly"
|
||||
clientJar = project.tasks["jar"].outputs.files.singleFile
|
||||
|
||||
dependsOn(project.parent!!.project(":runelite-api").tasks["jar"])
|
||||
dependsOn(project.parent!!.project(":runescape-api").tasks["jar"])
|
||||
dependsOn(project.parent!!.project(":http-api").tasks["jar"])
|
||||
dependsOn(project.parent!!.project(":injected-client").tasks["jar"])
|
||||
|
||||
doLast {
|
||||
|
||||
project.copy {
|
||||
from(project.tasks["jar"])
|
||||
from(project.parent!!.project(":runelite-api").tasks["jar"])
|
||||
from(project.parent!!.project(":runescape-api").tasks["jar"])
|
||||
from(project.parent!!.project(":http-api").tasks["jar"])
|
||||
from(project.parent!!.project(":injected-client").tasks["jar"])
|
||||
|
||||
into("${project.buildDir}/bootstrap/${type}/")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
106
buildSrc/src/main/kotlin/BootstrapTask.kt
Normal file
106
buildSrc/src/main/kotlin/BootstrapTask.kt
Normal file
@@ -0,0 +1,106 @@
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.api.tasks.PathSensitive
|
||||
import org.gradle.api.tasks.PathSensitivity
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.kotlin.dsl.extra
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import java.io.File
|
||||
import java.security.MessageDigest
|
||||
|
||||
open class BootstrapTask : DefaultTask() {
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
var type: String? = "stable"
|
||||
|
||||
@InputFile
|
||||
@PathSensitive(PathSensitivity.ABSOLUTE)
|
||||
var clientJar: File? = null
|
||||
|
||||
@Input
|
||||
val launcherJvm11Arguments = arrayOf("-XX:+DisableAttachMechanism", "-Drunelite.launcher.nojvm=true", "-Xmx512m", "-Xss2m", "-XX:CompileThreshold=1500", "-Djna.nosys=true")
|
||||
|
||||
@Input
|
||||
val launcherArguments = arrayOf("-XX:+DisableAttachMechanism", "-Drunelite.launcher.nojvm=true", "-Xmx512m", "-Xss2m", "-XX:CompileThreshold=1500", "-Xincgc", "-XX:+UseConcMarkSweepGC", "-XX:+UseParNewGC", "-Djna.nosys=true")
|
||||
|
||||
@Input
|
||||
val clientJvmArguments = arrayOf("-XX:+DisableAttachMechanism", "-Xmx512m", "-Xss2m", "-XX:CompileThreshold=1500", "-Xincgc", "-XX:+UseConcMarkSweepGC", "-XX:+UseParNewGC", "-Djna.nosys=true")
|
||||
|
||||
@Input
|
||||
val clientJvm9Arguments = arrayOf("-XX:+DisableAttachMechanism", "-Xmx512m", "-Xss2m", "-XX:CompileThreshold=1500", "-Djna.nosys=true")
|
||||
|
||||
private fun hash(file: ByteArray): String {
|
||||
return MessageDigest.getInstance("SHA-256").digest(file).fold("", { str, it -> str + "%02x".format(it) })
|
||||
}
|
||||
|
||||
private fun getArtifacts(): Array<JsonBuilder> {
|
||||
val artifacts = ArrayList<JsonBuilder>()
|
||||
|
||||
project.configurations["runtimeClasspath"].resolvedConfiguration.resolvedArtifacts.forEach {
|
||||
val module = it.moduleVersion.id.toString()
|
||||
|
||||
val name = module.split(":")[1]
|
||||
val group = module.split(":")[0]
|
||||
val version = module.split(":")[2]
|
||||
var path = ""
|
||||
|
||||
if (it.file.name.contains(ProjectVersions.rlVersion)) {
|
||||
path = "https://github.com/open-osrs/hosting/raw/master/${type}/${it.file.name}"
|
||||
} else if (!group.contains("runelite")) {
|
||||
path = "https://repo.maven.apache.org/maven2/" + group.replace(".", "/") + "/${name}/$version/${name}-$version"
|
||||
if (it.classifier != null && it.classifier != "no_aop") {
|
||||
path += "-${it.classifier}"
|
||||
}
|
||||
path += ".jar"
|
||||
} else if (it.file.name.contains("trident") || it.file.name.contains("discord") || it.file.name.contains("substance")) {
|
||||
path = "https://repo.runelite.net/net/runelite/"
|
||||
if (!it.file.name.contains("discord")) {
|
||||
path += "pushingpixels/"
|
||||
}
|
||||
path += "${name}/$version/${name}-$version.jar"
|
||||
}
|
||||
|
||||
val artifactFile = File(it.file.absolutePath)
|
||||
|
||||
artifacts.add(JsonBuilder(
|
||||
"name" to it.file.name,
|
||||
"path" to path,
|
||||
"size" to artifactFile.length(),
|
||||
"hash" to hash(artifactFile.readBytes())
|
||||
))
|
||||
}
|
||||
|
||||
artifacts.add(JsonBuilder(
|
||||
"name" to clientJar!!.name,
|
||||
"path" to "https://github.com/open-osrs/hosting/raw/master/${type}/${clientJar!!.name}",
|
||||
"size" to clientJar!!.length(),
|
||||
"hash" to hash(clientJar!!.readBytes())
|
||||
))
|
||||
|
||||
return artifacts.toTypedArray()
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun boostrap() {
|
||||
val json = JsonBuilder(
|
||||
"projectVersion" to ProjectVersions.openosrsVersion,
|
||||
"minimumLauncherVersion" to ProjectVersions.launcherVersion,
|
||||
"launcherJvm11Arguments" to launcherJvm11Arguments,
|
||||
"launcherArguments" to launcherArguments,
|
||||
"clientJvmArguments" to clientJvmArguments,
|
||||
"clientJvm9Arguments" to clientJvm9Arguments,
|
||||
"buildCommit" to project.extra["gitCommit"],
|
||||
"artifacts" to getArtifacts()
|
||||
).toString()
|
||||
|
||||
val bootstrapDir = File("${project.buildDir}/bootstrap")
|
||||
bootstrapDir.mkdirs()
|
||||
|
||||
File(bootstrapDir, "bootstrap-${type}.json").printWriter().use { out ->
|
||||
out.println(json)
|
||||
}
|
||||
}
|
||||
}
|
||||
193
buildSrc/src/main/kotlin/Dependencies.kt
Normal file
193
buildSrc/src/main/kotlin/Dependencies.kt
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
const val kotlinVersion = "1.3.50"
|
||||
|
||||
object ProjectVersions {
|
||||
const val launcherVersion = "2.0.4"
|
||||
const val rlVersion = "1.5.39-SNAPSHOT"
|
||||
|
||||
const val openosrsVersion = "2.1.9.1"
|
||||
|
||||
const val rsversion = 185
|
||||
const val cacheversion = 165
|
||||
}
|
||||
|
||||
object Plugins {
|
||||
val grgitPlugin = "org.ajoberstar:grgit:2.3.0"
|
||||
val versionsPlugin = "com.github.ben-manes:gradle-versions-plugin:0.27.0"
|
||||
val injectorPlugin = "com.openosrs:injector-plugin:1.0.2"
|
||||
val testLogger = Pair("com.adarshr.test-logger", "2.0.0")
|
||||
val versions = Pair("com.github.ben-manes.versions", "0.27.0")
|
||||
val buildScan = Pair("com.gradle.build-scan", "3.0")
|
||||
val latestVersion = Pair("se.patrikerdes.use-latest-versions", "0.2.12")
|
||||
val grgit = Pair("org.ajoberstar.grgit", "4.0.0-rc.1")
|
||||
val jarTest = Pair("com.github.hauner.jarTest", "1.0.1")
|
||||
val shadow = Pair("com.github.johnrengelman.shadow", "5.1.0")
|
||||
}
|
||||
|
||||
object Libraries {
|
||||
private object Versions {
|
||||
const val annotations = "17.0.0"
|
||||
const val antlr = "4.7.2"
|
||||
const val apacheCommonsCompress = "1.19"
|
||||
const val apacheCommonsCsv = "1.7"
|
||||
const val apacheCommonsText = "1.8"
|
||||
const val asm = "7.2"
|
||||
const val commonsCli = "1.4"
|
||||
const val discord = "1.1"
|
||||
const val fernflower = "07082019"
|
||||
const val findbugs = "3.0.2"
|
||||
const val gson = "2.8.6"
|
||||
const val guava = "28.1-jre"
|
||||
const val guice = "4.2.2"
|
||||
const val h2 = "1.4.200"
|
||||
const val hamcrest = "2.2"
|
||||
const val httpcore = "4.4.12"
|
||||
const val httpmime = "4.5.10"
|
||||
const val javassist = "3.26.0-GA"
|
||||
const val javax = "1.3.2"
|
||||
const val javaxInject = "1"
|
||||
const val jbsdiff = "1.0"
|
||||
const val jclCore = "2.8"
|
||||
const val jedis = "3.1.0"
|
||||
const val jna = "5.4.0"
|
||||
const val jogamp = "2.3.2"
|
||||
const val jopt = "5.0.4"
|
||||
const val jooq = "3.12.2"
|
||||
const val junit = "4.12"
|
||||
const val jupiter = "5.6.0-M1"
|
||||
const val logback = "1.2.3"
|
||||
const val lombok = "1.18.10"
|
||||
const val mapstruct = "1.3.1.Final"
|
||||
const val mariadbJdbc = "2.5.1"
|
||||
const val mavenPluginAnnotations = "3.6.0"
|
||||
const val mavenPluginApi = "3.6.2"
|
||||
const val minio = "6.0.11"
|
||||
const val mockito = "3.1.0"
|
||||
const val mongodbDriverSync = "3.11.1"
|
||||
const val mysqlConnectorJava = "8.0.18"
|
||||
const val naturalMouse = "2.0.2"
|
||||
const val netty = "4.1.42.Final"
|
||||
const val okhttp3 = "4.2.2"
|
||||
const val orangeExtensions = "1.0"
|
||||
const val petitparser = "2.2.0"
|
||||
const val plexus = "3.3.0"
|
||||
const val rxjava = "2.2.13"
|
||||
const val rxrelay = "2.1.1"
|
||||
const val scribejava = "6.9.0"
|
||||
const val sisu = "0.3.3"
|
||||
const val slf4j = "1.7.28"
|
||||
const val springJdbc = "5.2.0.RELEASE"
|
||||
const val springboot = "2.2.0.RELEASE"
|
||||
const val sql2o = "1.6.0"
|
||||
const val substance = "8.0.02"
|
||||
const val trident = "1.5.00"
|
||||
}
|
||||
|
||||
const val annotations = "org.jetbrains:annotations:${Versions.annotations}"
|
||||
const val antlr = "org.antlr:antlr4-runtime:${Versions.antlr}"
|
||||
const val apacheCommonsCompress = "org.apache.commons:commons-compress:${Versions.apacheCommonsCompress}"
|
||||
const val apacheCommonsCsv = "org.apache.commons:commons-csv:${Versions.apacheCommonsCsv}"
|
||||
const val apacheCommonsText = "org.apache.commons:commons-text:${Versions.apacheCommonsText}"
|
||||
const val asmAll = "org.ow2.asm:asm:${Versions.asm}"
|
||||
const val asmUtil = "org.ow2.asm:asm-util:${Versions.asm}"
|
||||
const val asmTree = "org.ow2.asm:asm-tree:${Versions.asm}"
|
||||
const val commonsCli = "commons-cli:commons-cli:${Versions.commonsCli}"
|
||||
const val discord = "net.runelite:discord:${Versions.discord}"
|
||||
const val fernflower = "net.runelite:fernflower:${Versions.fernflower}"
|
||||
const val findbugs = "com.google.code.findbugs:jsr305:${Versions.findbugs}"
|
||||
const val gson = "com.google.code.gson:gson:${Versions.gson}"
|
||||
const val guava = "com.google.guava:guava:${Versions.guava}"
|
||||
const val guice = "com.google.inject:guice:${Versions.guice}:no_aop"
|
||||
const val guiceGrapher = "com.google.inject.extensions:guice-grapher:${Versions.guice}"
|
||||
const val guiceTestlib = "com.google.inject.extensions:guice-testlib:${Versions.guice}"
|
||||
const val h2 = "com.h2database:h2:${Versions.h2}"
|
||||
const val hamcrest = "org.hamcrest:hamcrest-library:${Versions.hamcrest}"
|
||||
const val httpcore = "org.apache.httpcomponents:httpcore:${Versions.httpcore}"
|
||||
const val httpmime = "org.apache.httpcomponents:httpmime:${Versions.httpmime}"
|
||||
const val javassist = "org.javassist:javassist:${Versions.javassist}"
|
||||
const val javax = "javax.annotation:javax.annotation-api:${Versions.javax}"
|
||||
const val javaxInject = "javax.inject:javax.inject:${Versions.javaxInject}"
|
||||
const val jbsdiff = "io.sigpipe:jbsdiff:${Versions.jbsdiff}"
|
||||
const val jclCore = "org.xeustechnologies:jcl-core:${Versions.jclCore}"
|
||||
const val jedis = "redis.clients:jedis:${Versions.jedis}"
|
||||
const val jna = "net.java.dev.jna:jna:${Versions.jna}"
|
||||
const val jnaPlatform = "net.java.dev.jna:jna-platform:${Versions.jna}"
|
||||
const val jogampJogl = "org.jogamp.jogl:jogl-all:${Versions.jogamp}"
|
||||
const val jogampGluegen = "org.jogamp.gluegen:gluegen-rt:${Versions.jogamp}"
|
||||
const val jogampGluegenLinuxAmd64 = "org.jogamp.gluegen:gluegen-rt:${Versions.jogamp}:natives-linux-amd64"
|
||||
const val jogampGluegenLinuxI586 = "org.jogamp.gluegen:gluegen-rt:${Versions.jogamp}:natives-linux-i586"
|
||||
const val jogampGluegenWindowsAmd64 = "org.jogamp.gluegen:gluegen-rt:${Versions.jogamp}:natives-windows-amd64"
|
||||
const val jogampGluegenWindowsI586 = "org.jogamp.gluegen:gluegen-rt:${Versions.jogamp}:natives-windows-i586"
|
||||
const val jogampJoglLinuxAmd64 = "org.jogamp.jogl:jogl-all:${Versions.jogamp}:natives-linux-amd64"
|
||||
const val jogampJoglLinuxI586 = "org.jogamp.jogl:jogl-all:${Versions.jogamp}:natives-linux-i586"
|
||||
const val jogampJoglWindowsAmd64 = "org.jogamp.jogl:jogl-all:${Versions.jogamp}:natives-windows-amd64"
|
||||
const val jogampJoglWindowsI586 = "org.jogamp.jogl:jogl-all:${Versions.jogamp}:natives-windows-i586"
|
||||
const val jopt = "net.sf.jopt-simple:jopt-simple:${Versions.jopt}"
|
||||
const val jooq = "org.jooq:jooq:${Versions.jooq}"
|
||||
const val jooqCodegen = "org.jooq:jooq-codegen:${Versions.jooq}"
|
||||
const val jooqMeta = "org.jooq:jooq-meta:${Versions.jooq}"
|
||||
const val junit = "junit:junit:${Versions.junit}"
|
||||
const val jupiter = "org.junit.jupiter:junit-jupiter-api:${Versions.jupiter}"
|
||||
const val logback = "ch.qos.logback:logback-classic:${Versions.logback}"
|
||||
const val lombok = "org.projectlombok:lombok:${Versions.lombok}"
|
||||
const val mapstruct = "org.mapstruct:mapstruct-jdk8:${Versions.mapstruct}"
|
||||
const val mapstructProcessor = "org.mapstruct:mapstruct-processor:${Versions.mapstruct}"
|
||||
const val mariadbJdbc = "org.mariadb.jdbc:mariadb-java-client:${Versions.mariadbJdbc}"
|
||||
const val mavenPluginAnnotations = "org.apache.maven.plugin-tools:maven-plugin-annotations:${Versions.mavenPluginAnnotations}"
|
||||
const val mavenPluginApi = "org.apache.maven:maven-plugin-api:${Versions.mavenPluginApi}"
|
||||
const val minio = "io.minio:minio:${Versions.minio}"
|
||||
const val mockitoCore = "org.mockito:mockito-core:${Versions.mockito}"
|
||||
const val mockitoInline = "org.mockito:mockito-inline:${Versions.mockito}"
|
||||
const val mongodbDriverSync = "org.mongodb:mongodb-driver-sync:${Versions.mongodbDriverSync}"
|
||||
const val mysqlConnectorJava = "mysql:mysql-connector-java:${Versions.mysqlConnectorJava}"
|
||||
const val naturalMouse = "com.github.joonasvali.naturalmouse:naturalmouse:${Versions.naturalMouse}"
|
||||
const val nettyAll = "io.netty:netty-all:${Versions.netty}"
|
||||
const val nettyBuffer = "io.netty:netty-buffer:${Versions.netty}"
|
||||
const val okhttp3 = "com.squareup.okhttp3:okhttp:${Versions.okhttp3}"
|
||||
const val okhttp3Webserver = "com.squareup.okhttp3:mockwebserver:${Versions.okhttp3}"
|
||||
const val orangeExtensions = "net.runelite:orange-extensions:${Versions.orangeExtensions}"
|
||||
const val petitparser = "com.github.petitparser:java-petitparser:${Versions.petitparser}"
|
||||
const val plexus = "org.codehaus.plexus:plexus-utils:${Versions.plexus}"
|
||||
const val rxjava = "io.reactivex.rxjava2:rxjava:${Versions.rxjava}"
|
||||
const val rxrelay = "com.jakewharton.rxrelay2:rxrelay:${Versions.rxrelay}"
|
||||
const val scribejava = "com.github.scribejava:scribejava-apis:${Versions.scribejava}"
|
||||
const val sisu = "org.eclipse.sisu:org.eclipse.sisu.inject:${Versions.sisu}"
|
||||
const val slf4jApi = "org.slf4j:slf4j-api:${Versions.slf4j}"
|
||||
const val slf4jNop = "org.slf4j:slf4j-nop:${Versions.slf4j}"
|
||||
const val slf4jSimple = "org.slf4j:slf4j-simple:${Versions.slf4j}"
|
||||
const val springbootDevtools = "org.springframework.boot:spring-boot-devtools:${Versions.springboot}"
|
||||
const val springbootStarter = "org.springframework.boot:spring-boot-starter:${Versions.springboot}"
|
||||
const val springbootStarterWeb = "org.springframework.boot:spring-boot-starter-web:${Versions.springboot}"
|
||||
const val springbootStarterJdbc = "org.springframework.boot:spring-boot-starter-jdbc:${Versions.springboot}"
|
||||
const val springbootStarterTest = "org.springframework.boot:spring-boot-starter-test:${Versions.springboot}"
|
||||
const val springbootStarterTomcat = "org.springframework.boot:spring-boot-starter-tomcat:${Versions.springboot}"
|
||||
const val springbootJdbc = "org.springframework:spring-jdbc:${Versions.springJdbc}"
|
||||
const val sql2o = "org.sql2o:sql2o:${Versions.sql2o}"
|
||||
const val substance = "net.runelite.pushingpixels:substance:${Versions.substance}"
|
||||
const val trident = "net.runelite.pushingpixels:trident:${Versions.trident}"
|
||||
const val vanilla = "net.runelite.rs:vanilla:${ProjectVersions.rsversion}"
|
||||
}
|
||||
19
buildSrc/src/main/kotlin/FernflowerPlugin.kt
Normal file
19
buildSrc/src/main/kotlin/FernflowerPlugin.kt
Normal file
@@ -0,0 +1,19 @@
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.StopExecutionException
|
||||
import org.gradle.api.tasks.diagnostics.DependencyReportTask
|
||||
import org.gradle.kotlin.dsl.register
|
||||
|
||||
class FernflowerPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
project.tasks.register<FernflowerTask>("decompile") {
|
||||
dependsOn(project.tasks.getByName("jar"))
|
||||
|
||||
doFirst {
|
||||
if (!project.tasks.getByName("jar").didWork) {
|
||||
throw StopExecutionException()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
45
buildSrc/src/main/kotlin/FernflowerTask.kt
Normal file
45
buildSrc/src/main/kotlin/FernflowerTask.kt
Normal file
@@ -0,0 +1,45 @@
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.PathSensitive
|
||||
import org.gradle.api.tasks.PathSensitivity
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler
|
||||
|
||||
@CacheableTask
|
||||
open class FernflowerTask: DefaultTask() {
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
var extraArgs: List<String>? = null
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
var inputJar: String? = null
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
var outputDir: String? = null
|
||||
|
||||
@InputFile
|
||||
@PathSensitive(PathSensitivity.ABSOLUTE)
|
||||
var getInputJar = project.file(inputJar ?: "${project.buildDir}/libs/${project.name}-${project.version}.jar")
|
||||
|
||||
@OutputDirectory
|
||||
var getOutputDir = project.file(outputDir ?: "${project.buildDir}/decompiled-sources")
|
||||
|
||||
|
||||
@TaskAction
|
||||
fun decompile() {
|
||||
getOutputDir.mkdirs()
|
||||
val args = mutableListOf(getInputJar.toString(), getOutputDir.toString())
|
||||
if (extraArgs != null) {
|
||||
args.addAll(extraArgs!!)
|
||||
}
|
||||
|
||||
ConsoleDecompiler.main(args.toTypedArray())
|
||||
}
|
||||
}
|
||||
55
buildSrc/src/main/kotlin/JsonBuilder.kt
Normal file
55
buildSrc/src/main/kotlin/JsonBuilder.kt
Normal file
@@ -0,0 +1,55 @@
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
||||
class JsonBuilder() {
|
||||
private var json = JSONObject()
|
||||
|
||||
constructor(vararg pairs: Pair<String, *>) : this() {
|
||||
add(*pairs)
|
||||
}
|
||||
|
||||
fun add(vararg pairs: Pair<String, *>) {
|
||||
for ((key, value) in pairs) {
|
||||
when (value) {
|
||||
is Boolean -> json.put(key, value)
|
||||
is Number -> add(key, value)
|
||||
is String -> json.put(key, value)
|
||||
is JsonBuilder -> json.put(key, value.json)
|
||||
is Array<*> -> add(key, value)
|
||||
is JSONObject -> json.put(key, value)
|
||||
is JSONArray -> json.put(key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun add(key: String, value: Number): JsonBuilder {
|
||||
when (value) {
|
||||
is Int -> json.put(key, value)
|
||||
is Long -> json.put(key, value)
|
||||
is Float -> json.put(key, value)
|
||||
is Double -> json.put(key, value)
|
||||
else -> {}
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
fun <T> add(key: String, items: Array<T>): JsonBuilder {
|
||||
val jsonArray = JSONArray()
|
||||
items.forEach {
|
||||
when (it) {
|
||||
is String,is Long,is Int, is Boolean -> jsonArray.put(it)
|
||||
is JsonBuilder -> jsonArray.put(it.json)
|
||||
else -> try {jsonArray.put(it)} catch (ignored:Exception) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json.put(key, jsonArray)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
override fun toString() = json.toString()
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
implementation-class=FernflowerPlugin
|
||||
@@ -1,28 +0,0 @@
|
||||
description = 'Cache Client'
|
||||
|
||||
dependencies {
|
||||
api project(':cache')
|
||||
api project(':protocol')
|
||||
|
||||
implementation group: 'com.google.guava', name: 'guava', version: guava
|
||||
implementation group: 'io.netty', name: 'netty-all', version: netty
|
||||
implementation group: 'org.slf4j', name: 'slf4j-api', version: slf4j
|
||||
|
||||
testImplementation group: 'junit', name: 'junit', version: junit
|
||||
testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: slf4j
|
||||
testImplementation project(path: ':cache', configuration: 'testArchives')
|
||||
}
|
||||
|
||||
task update {
|
||||
dependsOn ":cache-client:build"
|
||||
|
||||
doLast {
|
||||
def path = sourceSets.main.runtimeClasspath
|
||||
def loader = new URLClassLoader(path.collect { f -> f.toURI().toURL() } as URL[])
|
||||
def cacheClient = loader.loadClass('net.runelite.cache.client.CacheClient')
|
||||
|
||||
cacheClient.getCache(rsversion);
|
||||
|
||||
loader.close()
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 ThatGamerBlue
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -22,28 +22,28 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
description = 'Injected Client'
|
||||
|
||||
compileJava {
|
||||
dependsOn ':injector-plugin:assemble'
|
||||
description = "Cache Client"
|
||||
|
||||
dependencies {
|
||||
api(project(":cache"))
|
||||
api(project(":protocol"))
|
||||
|
||||
implementation(Libraries.guava)
|
||||
implementation(Libraries.nettyAll)
|
||||
implementation(Libraries.slf4jApi)
|
||||
|
||||
testImplementation(Libraries.junit)
|
||||
testImplementation(Libraries.slf4jSimple)
|
||||
testImplementation(project(path = ":cache", configuration = "testArchives"))
|
||||
}
|
||||
|
||||
compileJava.outputs.upToDateWhen { false }
|
||||
tasks {
|
||||
register<JavaExec>("download") {
|
||||
dependsOn(":cache-client:build")
|
||||
|
||||
compileJava.doLast() {
|
||||
copy {
|
||||
File f = file("build/classes/java/main")
|
||||
f.deleteDir()
|
||||
f.mkdirs()
|
||||
from ("${injectedClassesPath}")
|
||||
into ("build/classes/java/main")
|
||||
classpath = project.sourceSets.main.get().runtimeClasspath
|
||||
main = "net.runelite.cache.client.CacheClient"
|
||||
args(listOf(ProjectVersions.rsversion))
|
||||
}
|
||||
}
|
||||
|
||||
classes.doLast() {
|
||||
File f = file("build/classes/java/main/Placeholder.class")
|
||||
f.delete()
|
||||
}
|
||||
|
||||
// tasks.build.dependsOn(tasks.decompile)
|
||||
// this is just here to show how this could be used
|
||||
@@ -85,7 +85,7 @@ public class CacheClient implements AutoCloseable
|
||||
private CompletableFuture<HandshakeResponseType> handshakeFuture;
|
||||
private final Queue<PendingFileRequest> requests = new ArrayDeque<>();
|
||||
|
||||
public static void getCache(int clientRevision)
|
||||
public static void main(String[] args)
|
||||
{
|
||||
Path path = Paths.get(System.getProperty("user.home"), "jagexcache" + File.separator + "oldschool" + File.separator + "LIVE");
|
||||
final File jagexcache = new File(String.valueOf(path));
|
||||
@@ -95,7 +95,7 @@ public class CacheClient implements AutoCloseable
|
||||
try (Store store = new Store(jagexcache))
|
||||
{
|
||||
store.load();
|
||||
CacheClient c = new CacheClient(store, clientRevision);
|
||||
CacheClient c = new CacheClient(store, Integer.parseInt(args[0]));
|
||||
c.connect();
|
||||
CompletableFuture<HandshakeResponseType> handshake = c.handshake();
|
||||
handshake.get();
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
description = 'Cache Updater'
|
||||
|
||||
dependencies {
|
||||
annotationProcessor group: 'org.projectlombok', name: 'lombok', version: lombok
|
||||
|
||||
compileOnly group: 'org.projectlombok', name: 'lombok', version: lombok
|
||||
|
||||
implementation group: 'io.minio', name: 'minio', version: minio
|
||||
implementation group: 'mysql', name: 'mysql-connector-java', version: mysqlConnectorJava
|
||||
implementation group: 'org.springframework.boot', name: 'spring-boot-devtools', version: springboot
|
||||
implementation group: 'org.springframework.boot', name: 'spring-boot-starter', version: springboot
|
||||
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-jdbc', version: springboot
|
||||
implementation group: 'org.sql2o', name: 'sql2o', version: sql2o
|
||||
implementation project(':cache-client')
|
||||
}
|
||||
40
cache-updater/cache-updater.gradle.kts
Normal file
40
cache-updater/cache-updater.gradle.kts
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
description = "Cache Updater"
|
||||
|
||||
dependencies {
|
||||
annotationProcessor(Libraries.lombok)
|
||||
|
||||
compileOnly(Libraries.lombok)
|
||||
|
||||
implementation(Libraries.minio)
|
||||
implementation(Libraries.mysqlConnectorJava)
|
||||
implementation(Libraries.springbootDevtools)
|
||||
implementation(Libraries.springbootStarter)
|
||||
implementation(Libraries.springbootStarterJdbc)
|
||||
implementation(Libraries.sql2o)
|
||||
implementation(project(":cache-client"))
|
||||
}
|
||||
36
cache/build.gradle
vendored
36
cache/build.gradle
vendored
@@ -1,36 +0,0 @@
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
||||
plugins {
|
||||
id "com.github.hauner.jarTest" version "1.0.1"
|
||||
}
|
||||
|
||||
description = 'Cache'
|
||||
|
||||
dependencies {
|
||||
annotationProcessor group: 'org.projectlombok', name: 'lombok', version: lombok
|
||||
|
||||
api project(':http-api')
|
||||
|
||||
compileOnly group: 'org.projectlombok', name: 'lombok', version: lombok
|
||||
|
||||
implementation group: 'com.google.code.gson', name: 'gson', version: gson
|
||||
implementation group: 'com.google.guava', name: 'guava', version: guava
|
||||
implementation group: 'commons-cli', name: 'commons-cli', version: commonsCli
|
||||
implementation group: 'io.netty', name: 'netty-buffer', version: netty
|
||||
implementation group: 'org.antlr', name: 'antlr4-runtime', version: antlr
|
||||
implementation group: 'org.apache.commons', name: 'commons-compress', version: apacheCommonsCompress
|
||||
implementation group: 'org.slf4j', name: 'slf4j-api', version: slf4j
|
||||
|
||||
testImplementation group: 'junit', name: 'junit', version: junit
|
||||
testImplementation group: 'net.runelite.rs', name: 'cache', version: cacheversion
|
||||
testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: slf4j
|
||||
}
|
||||
|
||||
processTestResources {
|
||||
from file("src/test/resources/cache.properties"), {
|
||||
filter(ReplaceTokens, tokens: [
|
||||
"rs.version": rsversion.toString(),
|
||||
"cache.version": cacheversion.toString()
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -23,46 +23,47 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.runelite.asm.visitors;
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.Type;
|
||||
import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.asm.attributes.annotation.Element;
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
public class ClassAnnotationVisitor extends AnnotationVisitor
|
||||
{
|
||||
private final ClassFile classFile;
|
||||
private final Type type;
|
||||
private final Annotation annotation;
|
||||
|
||||
public ClassAnnotationVisitor(ClassFile classFile, Type type)
|
||||
{
|
||||
super(Opcodes.ASM5);
|
||||
|
||||
this.classFile = classFile;
|
||||
this.type = type;
|
||||
|
||||
annotation = new Annotation(classFile.getAnnotations());
|
||||
annotation.setType(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(String name, Object value)
|
||||
{
|
||||
Element element = new Element(annotation);
|
||||
|
||||
element.setName(name);
|
||||
element.setValue(value);
|
||||
|
||||
annotation.addElement(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd()
|
||||
{
|
||||
classFile.getAnnotations().addAnnotation(annotation);
|
||||
}
|
||||
plugins {
|
||||
id(Plugins.jarTest.first) version Plugins.jarTest.second
|
||||
}
|
||||
|
||||
description = "Cache"
|
||||
|
||||
dependencies {
|
||||
annotationProcessor(Libraries.lombok)
|
||||
|
||||
api(project(":http-api"))
|
||||
|
||||
compileOnly(Libraries.lombok)
|
||||
|
||||
implementation(Libraries.gson)
|
||||
implementation(Libraries.guava)
|
||||
implementation(Libraries.commonsCli)
|
||||
implementation(Libraries.nettyBuffer)
|
||||
implementation(Libraries.antlr)
|
||||
implementation(Libraries.apacheCommonsCompress)
|
||||
implementation(Libraries.slf4jApi)
|
||||
|
||||
testImplementation(Libraries.junit)
|
||||
testImplementation(group = "net.runelite.rs", name = "cache", version = "${ProjectVersions.cacheversion}")
|
||||
testImplementation(Libraries.slf4jSimple)
|
||||
}
|
||||
|
||||
tasks {
|
||||
"processTestResources"(ProcessResources::class) {
|
||||
val tokens = mapOf(
|
||||
"rs.version" to ProjectVersions.rsversion.toString(),
|
||||
"cache.version" to ProjectVersions.cacheversion.toString()
|
||||
)
|
||||
|
||||
inputs.properties(tokens)
|
||||
|
||||
from("src/test/resources") {
|
||||
include("cache.properties")
|
||||
|
||||
filter<ReplaceTokens>("tokens" to tokens)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,38 +31,37 @@ import lombok.Data;
|
||||
@Data
|
||||
public class NpcDefinition
|
||||
{
|
||||
|
||||
public final int id;
|
||||
public short[] recolorToFind;
|
||||
public int rotation = 32;
|
||||
public String name = "null";
|
||||
public short[] recolorToReplace;
|
||||
public int size = 1;
|
||||
public int[] models;
|
||||
public int[] models_2;
|
||||
public int stanceAnimation = -1;
|
||||
public int anInt2165 = -1;
|
||||
public int tileSpacesOccupied = 1;
|
||||
public int walkAnimation = -1;
|
||||
public short[] retextureToReplace;
|
||||
public int rotate90RightAnimation = -1;
|
||||
public boolean aBool2170 = true;
|
||||
public int resizeX = 128;
|
||||
public int contrast = 0;
|
||||
public int[] chatheadModels;
|
||||
public int standingAnimation = -1;
|
||||
public int rotateLeftAnimation = -1;
|
||||
public int rotateRightAnimation = -1;
|
||||
public int walkingAnimation = -1;
|
||||
public int rotate180Animation = -1;
|
||||
public int varbitIndex = -1;
|
||||
public String[] options = new String[5];
|
||||
public boolean renderOnMinimap = true;
|
||||
public int combatLevel = -1;
|
||||
public int rotate90RightAnimation = -1;
|
||||
public int rotate90LeftAnimation = -1;
|
||||
public int resizeY = 128;
|
||||
public boolean hasRenderPriority = false;
|
||||
public int ambient = 0;
|
||||
public int headIcon = -1;
|
||||
public int[] configs;
|
||||
public short[] recolorToFind;
|
||||
public short[] recolorToReplace;
|
||||
public short[] retextureToFind;
|
||||
public short[] retextureToReplace;
|
||||
public String[] actions = new String[5];
|
||||
public boolean isMinimapVisible = true;
|
||||
public int combatLevel = -1;
|
||||
public int widthScale = 128;
|
||||
public int heightScale = 128;
|
||||
public boolean hasRenderPriority;
|
||||
public int ambient;
|
||||
public int contrast;
|
||||
public int headIcon = -1;
|
||||
public int rotationSpeed = 32;
|
||||
public int[] configs;
|
||||
public int varbitId = -1;
|
||||
public int varpIndex = -1;
|
||||
public boolean isClickable = true;
|
||||
public int anInt2189 = -1;
|
||||
public boolean aBool2190 = false;
|
||||
public Map<Integer, Object> params = null;
|
||||
public boolean isInteractable = true;
|
||||
public boolean rotationFlag = true;
|
||||
public boolean isPet;
|
||||
public Map<Integer, Object> params;
|
||||
}
|
||||
|
||||
@@ -24,13 +24,12 @@
|
||||
*/
|
||||
package net.runelite.cache.definitions.loaders;
|
||||
|
||||
import java.util.HashMap;
|
||||
import net.runelite.cache.definitions.NpcDefinition;
|
||||
import net.runelite.cache.io.InputStream;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class NpcLoader
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(NpcLoader.class);
|
||||
@@ -67,7 +66,6 @@ public class NpcLoader
|
||||
{
|
||||
def.models[index] = stream.readUnsignedShort();
|
||||
}
|
||||
|
||||
}
|
||||
else if (opcode == 2)
|
||||
{
|
||||
@@ -75,37 +73,37 @@ public class NpcLoader
|
||||
}
|
||||
else if (opcode == 12)
|
||||
{
|
||||
def.tileSpacesOccupied = stream.readUnsignedByte();
|
||||
def.size = stream.readUnsignedByte();
|
||||
}
|
||||
else if (opcode == 13)
|
||||
{
|
||||
def.stanceAnimation = stream.readUnsignedShort();
|
||||
def.standingAnimation = stream.readUnsignedShort();
|
||||
}
|
||||
else if (opcode == 14)
|
||||
{
|
||||
def.walkAnimation = stream.readUnsignedShort();
|
||||
def.walkingAnimation = stream.readUnsignedShort();
|
||||
}
|
||||
else if (opcode == 15)
|
||||
{
|
||||
def.anInt2165 = stream.readUnsignedShort();
|
||||
def.rotateLeftAnimation = stream.readUnsignedShort();
|
||||
}
|
||||
else if (opcode == 16)
|
||||
{
|
||||
def.anInt2189 = stream.readUnsignedShort();
|
||||
def.rotateRightAnimation = stream.readUnsignedShort();
|
||||
}
|
||||
else if (opcode == 17)
|
||||
{
|
||||
def.walkAnimation = stream.readUnsignedShort();
|
||||
def.walkingAnimation = stream.readUnsignedShort();
|
||||
def.rotate180Animation = stream.readUnsignedShort();
|
||||
def.rotate90RightAnimation = stream.readUnsignedShort();
|
||||
def.rotate90LeftAnimation = stream.readUnsignedShort();
|
||||
}
|
||||
else if (opcode >= 30 && opcode < 35)
|
||||
{
|
||||
def.options[opcode - 30] = stream.readString();
|
||||
if (def.options[opcode - 30].equalsIgnoreCase("Hidden"))
|
||||
def.actions[opcode - 30] = stream.readString();
|
||||
if (def.actions[opcode - 30].equalsIgnoreCase("Hidden"))
|
||||
{
|
||||
def.options[opcode - 30] = null;
|
||||
def.actions[opcode - 30] = null;
|
||||
}
|
||||
}
|
||||
else if (opcode == 40)
|
||||
@@ -137,17 +135,17 @@ public class NpcLoader
|
||||
else if (opcode == 60)
|
||||
{
|
||||
length = stream.readUnsignedByte();
|
||||
def.models_2 = new int[length];
|
||||
def.chatheadModels = new int[length];
|
||||
|
||||
for (index = 0; index < length; ++index)
|
||||
{
|
||||
def.models_2[index] = stream.readUnsignedShort();
|
||||
def.chatheadModels[index] = stream.readUnsignedShort();
|
||||
}
|
||||
|
||||
}
|
||||
else if (opcode == 93)
|
||||
{
|
||||
def.renderOnMinimap = false;
|
||||
def.isMinimapVisible = false;
|
||||
}
|
||||
else if (opcode == 95)
|
||||
{
|
||||
@@ -155,11 +153,11 @@ public class NpcLoader
|
||||
}
|
||||
else if (opcode == 97)
|
||||
{
|
||||
def.resizeX = stream.readUnsignedShort();
|
||||
def.widthScale = stream.readUnsignedShort();
|
||||
}
|
||||
else if (opcode == 98)
|
||||
{
|
||||
def.resizeY = stream.readUnsignedShort();
|
||||
def.heightScale = stream.readUnsignedShort();
|
||||
}
|
||||
else if (opcode == 99)
|
||||
{
|
||||
@@ -179,18 +177,18 @@ public class NpcLoader
|
||||
}
|
||||
else if (opcode == 103)
|
||||
{
|
||||
def.rotation = stream.readUnsignedShort();
|
||||
def.rotationSpeed = stream.readUnsignedShort();
|
||||
}
|
||||
else if (opcode == 106)
|
||||
{
|
||||
def.varbitIndex = stream.readUnsignedShort();
|
||||
if ('\uffff' == def.varbitIndex)
|
||||
def.varbitId = stream.readUnsignedShort();
|
||||
if (def.varbitId == 65535)
|
||||
{
|
||||
def.varbitIndex = -1;
|
||||
def.varbitId = -1;
|
||||
}
|
||||
|
||||
def.varpIndex = stream.readUnsignedShort();
|
||||
if ('\uffff' == def.varpIndex)
|
||||
if (def.varpIndex == 65535)
|
||||
{
|
||||
def.varpIndex = -1;
|
||||
}
|
||||
@@ -212,26 +210,26 @@ public class NpcLoader
|
||||
}
|
||||
else if (opcode == 107)
|
||||
{
|
||||
def.isClickable = false;
|
||||
def.isInteractable = false;
|
||||
}
|
||||
else if (opcode == 109)
|
||||
{
|
||||
def.aBool2170 = false;
|
||||
def.rotationFlag = false;
|
||||
}
|
||||
else if (opcode == 111)
|
||||
{
|
||||
def.aBool2190 = true;
|
||||
def.isPet = true;
|
||||
}
|
||||
else if (opcode == 118)
|
||||
{
|
||||
def.varbitIndex = stream.readUnsignedShort();
|
||||
if ('\uffff' == def.varbitIndex)
|
||||
def.varbitId = stream.readUnsignedShort();
|
||||
if (def.varbitId == 65535)
|
||||
{
|
||||
def.varbitIndex = -1;
|
||||
def.varbitId = -1;
|
||||
}
|
||||
|
||||
def.varpIndex = stream.readUnsignedShort();
|
||||
if ('\uffff' == def.varpIndex)
|
||||
if (def.varpIndex == 65535)
|
||||
{
|
||||
def.varpIndex = -1;
|
||||
}
|
||||
|
||||
@@ -47,45 +47,45 @@ public class NpcSaver
|
||||
out.writeByte(2);
|
||||
out.writeString(npc.name);
|
||||
}
|
||||
if (npc.tileSpacesOccupied != 1)
|
||||
if (npc.size != 1)
|
||||
{
|
||||
out.writeByte(12);
|
||||
out.writeByte(npc.tileSpacesOccupied);
|
||||
out.writeByte(npc.size);
|
||||
}
|
||||
if (npc.stanceAnimation != -1)
|
||||
if (npc.standingAnimation != -1)
|
||||
{
|
||||
out.writeByte(13);
|
||||
out.writeShort(npc.stanceAnimation);
|
||||
out.writeShort(npc.standingAnimation);
|
||||
}
|
||||
if (npc.walkAnimation != -1)
|
||||
if (npc.walkingAnimation != -1)
|
||||
{
|
||||
out.writeByte(14);
|
||||
out.writeShort(npc.walkAnimation);
|
||||
out.writeShort(npc.walkingAnimation);
|
||||
}
|
||||
if (npc.anInt2165 != -1)
|
||||
if (npc.rotateLeftAnimation != -1)
|
||||
{
|
||||
out.writeByte(15);
|
||||
out.writeShort(npc.anInt2165);
|
||||
out.writeShort(npc.rotateLeftAnimation);
|
||||
}
|
||||
if (npc.anInt2189 != -1)
|
||||
if (npc.rotateRightAnimation != -1)
|
||||
{
|
||||
out.writeByte(16);
|
||||
out.writeShort(npc.anInt2189);
|
||||
out.writeShort(npc.rotateRightAnimation);
|
||||
}
|
||||
if (npc.rotate180Animation != -1 || npc.rotate90LeftAnimation != -1 || npc.rotate90RightAnimation != -1)
|
||||
{
|
||||
out.writeByte(17);
|
||||
out.writeShort(npc.walkAnimation);
|
||||
out.writeShort(npc.walkingAnimation);
|
||||
out.writeShort(npc.rotate180Animation);
|
||||
out.writeShort(npc.rotate90RightAnimation);
|
||||
out.writeShort(npc.rotate90LeftAnimation);
|
||||
}
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
if (npc.options[i] != null)
|
||||
if (npc.actions[i] != null)
|
||||
{
|
||||
out.writeByte(30 + i);
|
||||
out.writeString(npc.options[i]);
|
||||
out.writeString(npc.actions[i]);
|
||||
}
|
||||
}
|
||||
if (npc.recolorToFind != null && npc.recolorToReplace != null)
|
||||
@@ -108,16 +108,16 @@ public class NpcSaver
|
||||
out.writeShort(npc.retextureToReplace[i]);
|
||||
}
|
||||
}
|
||||
if (npc.models_2 != null)
|
||||
if (npc.chatheadModels != null)
|
||||
{
|
||||
out.writeByte(60);
|
||||
out.writeByte(npc.models_2.length);
|
||||
for (int modelId : npc.models_2)
|
||||
out.writeByte(npc.chatheadModels.length);
|
||||
for (int modelId : npc.chatheadModels)
|
||||
{
|
||||
out.writeShort(modelId);
|
||||
}
|
||||
}
|
||||
if (!npc.renderOnMinimap)
|
||||
if (!npc.isMinimapVisible)
|
||||
{
|
||||
out.writeByte(93);
|
||||
}
|
||||
@@ -127,9 +127,9 @@ public class NpcSaver
|
||||
out.writeShort(npc.combatLevel);
|
||||
}
|
||||
out.writeByte(97);
|
||||
out.writeShort(npc.resizeX);
|
||||
out.writeShort(npc.widthScale);
|
||||
out.writeByte(98);
|
||||
out.writeShort(npc.resizeY);
|
||||
out.writeShort(npc.heightScale);
|
||||
if (npc.hasRenderPriority)
|
||||
{
|
||||
out.writeByte(99);
|
||||
@@ -144,23 +144,23 @@ public class NpcSaver
|
||||
out.writeShort(npc.headIcon);
|
||||
}
|
||||
out.writeByte(103);
|
||||
out.writeShort(npc.rotation);
|
||||
if (!npc.isClickable)
|
||||
out.writeShort(npc.rotationSpeed);
|
||||
if (!npc.isInteractable)
|
||||
{
|
||||
out.writeByte(107);
|
||||
}
|
||||
if (!npc.aBool2170)
|
||||
if (!npc.rotationFlag)
|
||||
{
|
||||
out.writeByte(109);
|
||||
}
|
||||
if (npc.aBool2190)
|
||||
if (npc.isPet)
|
||||
{
|
||||
out.writeByte(111);
|
||||
}
|
||||
if (npc.configs != null)
|
||||
{
|
||||
out.writeByte(118);
|
||||
out.writeShort(npc.varbitIndex);
|
||||
out.writeShort(npc.varbitId);
|
||||
out.writeShort(npc.varpIndex);
|
||||
|
||||
int[] c = npc.configs;
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
plugins {
|
||||
id "com.github.hauner.jarTest" version "1.0.1"
|
||||
}
|
||||
|
||||
description = 'Deobfuscator'
|
||||
|
||||
def deobfuscatedJar = "${rootPath}/runescape-client/build/libs/rs-client-${project.version}.jar"
|
||||
|
||||
def unzipFile(String file, String dest)
|
||||
{
|
||||
def zipFile = new ZipFile(file)
|
||||
|
||||
zipFile.entries().each { it ->
|
||||
def path = Paths.get(dest + File.separator + it.name)
|
||||
if (it.directory)
|
||||
{
|
||||
Files.createDirectories(path)
|
||||
}
|
||||
else
|
||||
{
|
||||
def parentDir = path.getParent()
|
||||
if (!Files.exists(parentDir))
|
||||
{
|
||||
Files.createDirectories(parentDir)
|
||||
}
|
||||
Files.copy(zipFile.getInputStream(it), path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
deobjars
|
||||
}
|
||||
|
||||
dependencies {
|
||||
deobjars group: 'net.runelite.rs', name: 'vanilla', version: rsversion
|
||||
deobjars project(':rs-client')
|
||||
|
||||
implementation group: 'com.google.code.gson', name: 'gson', version: gson
|
||||
implementation group: 'com.google.guava', name: 'guava', version: guava
|
||||
implementation group: 'net.runelite', name: 'fernflower', version: fernflower
|
||||
implementation group: 'org.ow2.asm', name: 'asm', version: asm
|
||||
implementation group: 'org.ow2.asm', name: 'asm-util', version: asm
|
||||
implementation group: 'org.slf4j', name: 'slf4j-api', version: slf4j
|
||||
implementation project(':runelite-api')
|
||||
implementation project(':runescape-api')
|
||||
|
||||
runtime group: 'org.slf4j', name: 'slf4j-simple', version: slf4j
|
||||
|
||||
testImplementation configurations.deobjars.dependencies
|
||||
testImplementation group: 'junit', name: 'junit', version: junit
|
||||
testImplementation group: 'org.mockito', name: 'mockito-core', version: mockito
|
||||
}
|
||||
|
||||
processResources {
|
||||
from file("src/main/resources/deob.properties"), {
|
||||
filter(ReplaceTokens, tokens: [
|
||||
"rs.version": rsversion.toString(),
|
||||
"vanilla.jar": configurations.deobjars.find {it.name.startsWith("vanilla")}.toString().replace('\\', "/"),
|
||||
"rs.client": configurations.deobjars.find {it.name.startsWith("rs-client")}.toString().replace('\\', "/")
|
||||
])
|
||||
}
|
||||
}
|
||||
processTestResources {
|
||||
from file("src/test/resources/deob-test.properties"), {
|
||||
filter(ReplaceTokens, tokens: [
|
||||
"rs.client": configurations.deobjars.find {it.name.startsWith("rs-client")}.toString().replace('\\', "/"),
|
||||
"rs.version": rsversion.toString(),
|
||||
"vanilla.jar": configurations.deobjars.find {it.name.startsWith("vanilla")}.toString().replace('\\', "/")
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
task gamepackUpdate {
|
||||
dependsOn ":deobfuscator:build"
|
||||
dependsOn ":rs-client:build"
|
||||
|
||||
doLast {
|
||||
def path = sourceSets.main.runtimeClasspath
|
||||
def loader = new URLClassLoader(path.collect { f -> f.toURI().toURL() } as URL[])
|
||||
def downloader = loader.loadClass('net.runelite.gamepack.Downloader')
|
||||
def clientVersion = loader.loadClass('net.runelite.deob.clientver.ClientVersionMain')
|
||||
def deob = loader.loadClass('net.runelite.deob.Deob')
|
||||
def mappings = loader.loadClass('net.runelite.deob.updater.UpdateMappings')
|
||||
|
||||
String gamepack = downloader.gamepack()
|
||||
int version = clientVersion.version(gamepack)
|
||||
|
||||
String gamepackVersion = gamepack.replace("gamepack.jar", "gamepack-" + version + ".jar")
|
||||
String gamepackDeob = gamepack.replace("gamepack.jar", "gamepack-" + version + "-deob.jar")
|
||||
String gamepackMappings = gamepack.replace("gamepack.jar", "gamepack-" + version + "-updated-mappings.jar")
|
||||
String gamepackMappingsDecomp = gamepackMappings.replace(".jar", "-decomp")
|
||||
String gamepackMappingsFern = gamepackMappingsDecomp + File.separator + gamepackMappings.split("/gamepack/")[1]
|
||||
|
||||
if (version == -1 || version == rsversion)
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
deob.main(gamepackVersion, gamepackDeob)
|
||||
mappings.main(deobfuscatedJar, gamepackDeob, gamepackMappings)
|
||||
|
||||
new File(gamepackMappingsDecomp).mkdirs()
|
||||
ConsoleDecompiler.main(gamepackMappings, gamepackMappingsDecomp)
|
||||
|
||||
unzipFile(gamepackMappingsFern, gamepackMappingsDecomp)
|
||||
new File(gamepackMappingsFern).delete()
|
||||
|
||||
loader.close()
|
||||
}
|
||||
}
|
||||
81
deobfuscator/deobfuscator.gradle.kts
Normal file
81
deobfuscator/deobfuscator.gradle.kts
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
||||
plugins {
|
||||
id(Plugins.jarTest.first) version Plugins.jarTest.second
|
||||
}
|
||||
|
||||
val deobjars = configurations.create("deobjars")
|
||||
|
||||
dependencies {
|
||||
deobjars(group = "net.runelite.rs", name = "vanilla", version = ProjectVersions.rsversion.toString())
|
||||
deobjars(project(":runescape-client"))
|
||||
|
||||
implementation(Libraries.annotations)
|
||||
implementation(Libraries.asmAll)
|
||||
implementation(Libraries.asmUtil)
|
||||
implementation(Libraries.fernflower)
|
||||
implementation(Libraries.gson)
|
||||
implementation(Libraries.guava)
|
||||
implementation(Libraries.slf4jApi)
|
||||
implementation(project(":runelite-api"))
|
||||
implementation(project(":runescape-api"))
|
||||
|
||||
runtime(Libraries.slf4jSimple)
|
||||
|
||||
testImplementation(deobjars)
|
||||
testImplementation(Libraries.junit)
|
||||
testImplementation(Libraries.mockitoCore)
|
||||
}
|
||||
|
||||
tasks {
|
||||
val tokens = mapOf(
|
||||
"rs.version" to ProjectVersions.rsversion.toString(),
|
||||
"vanilla.jar" to deobjars.find { it.name.startsWith("vanilla") }.toString().replace("\\", "/"),
|
||||
"rs.client" to deobjars.find { it.name.startsWith("runescape-client") }.toString().replace("\\", "/")
|
||||
)
|
||||
|
||||
"processResources"(ProcessResources::class) {
|
||||
inputs.properties(tokens)
|
||||
|
||||
from("src/main/resources") {
|
||||
include("deob.properties")
|
||||
|
||||
filter<ReplaceTokens>("tokens" to tokens)
|
||||
}
|
||||
}
|
||||
|
||||
"processTestResources"(ProcessResources::class) {
|
||||
inputs.properties(tokens)
|
||||
|
||||
from("src/test/resources") {
|
||||
include("deob-test.properties")
|
||||
|
||||
filter<ReplaceTokens>("tokens" to tokens)
|
||||
}
|
||||
}
|
||||
}
|
||||
17
deobfuscator/src/main/java/net/runelite/asm/Annotated.java
Normal file
17
deobfuscator/src/main/java/net/runelite/asm/Annotated.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package net.runelite.asm;
|
||||
|
||||
import java.util.Iterator;
|
||||
import net.runelite.asm.attributes.Annotations;
|
||||
import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface Annotated extends Iterable<Annotation>
|
||||
{
|
||||
Annotations getAnnotations();
|
||||
|
||||
@NotNull
|
||||
default Iterator<Annotation> iterator()
|
||||
{
|
||||
return getAnnotations().iterator();
|
||||
}
|
||||
}
|
||||
@@ -31,13 +31,12 @@ import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.asm.pool.Class;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import static net.runelite.deob.DeobAnnotations.*;
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
public class ClassFile
|
||||
public class ClassFile implements Annotated, Named
|
||||
{
|
||||
private ClassGroup group;
|
||||
|
||||
@@ -100,10 +99,9 @@ public class ClassFile
|
||||
visitor.visit(version, access, name.getName(), null, super_class.getName(), ints);
|
||||
visitor.visitSource(source, null);
|
||||
|
||||
for (Annotation annotation : annotations.getAnnotations())
|
||||
for (Annotation annotation : annotations)
|
||||
{
|
||||
AnnotationVisitor av = visitor.visitAnnotation(annotation.getType().toString(), true);
|
||||
annotation.accept(av);
|
||||
annotation.accept(visitor.visitAnnotation(annotation.getType().toString(), true));
|
||||
}
|
||||
|
||||
for (Field field : fields)
|
||||
|
||||
@@ -27,13 +27,16 @@ package net.runelite.asm;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import net.runelite.asm.attributes.Code;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import static net.runelite.deob.DeobAnnotations.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ClassGroup
|
||||
public class ClassGroup implements Iterable<ClassFile>
|
||||
{
|
||||
private final List<ClassFile> classes = new ArrayList<>(); // to keep order
|
||||
private final Map<String, ClassFile> classMap = new HashMap<>();
|
||||
@@ -156,4 +159,17 @@ public class ClassGroup
|
||||
|
||||
return findClass(name);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<ClassFile> iterator()
|
||||
{
|
||||
return this.classes.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<? super ClassFile> action)
|
||||
{
|
||||
this.classes.forEach(action);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,15 +27,13 @@ package net.runelite.asm;
|
||||
import net.runelite.asm.attributes.Annotations;
|
||||
import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.deob.DeobAnnotations;
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
|
||||
import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
|
||||
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
|
||||
public class Field
|
||||
public class Field implements Annotated, Named
|
||||
{
|
||||
public static final int ACCESS_MODIFIERS = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED;
|
||||
|
||||
@@ -53,15 +51,14 @@ public class Field
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
|
||||
annotations = new Annotations();
|
||||
this.annotations = new Annotations();
|
||||
}
|
||||
|
||||
public void accept(FieldVisitor visitor)
|
||||
{
|
||||
for (Annotation annotation : annotations.getAnnotations())
|
||||
{
|
||||
AnnotationVisitor av = visitor.visitAnnotation(annotation.getType().toString(), true);
|
||||
annotation.accept(av);
|
||||
annotation.accept(visitor.visitAnnotation(annotation.getType().toString(), true));
|
||||
}
|
||||
|
||||
visitor.visitEnd();
|
||||
|
||||
@@ -25,12 +25,14 @@
|
||||
package net.runelite.asm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import net.runelite.asm.pool.Class;
|
||||
import net.runelite.deob.DeobAnnotations;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class Interfaces
|
||||
public class Interfaces implements Iterable<Class>
|
||||
{
|
||||
private final ClassFile classFile;
|
||||
|
||||
@@ -107,4 +109,10 @@ public class Interfaces
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Iterator<Class> iterator()
|
||||
{
|
||||
return this.interfaces.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import net.runelite.asm.attributes.code.Parameter;
|
||||
import net.runelite.asm.attributes.code.instruction.types.LVTInstruction;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import net.runelite.deob.DeobAnnotations;
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import static org.objectweb.asm.Opcodes.ACC_FINAL;
|
||||
@@ -47,7 +46,7 @@ import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
import static org.objectweb.asm.Opcodes.ACC_STATIC;
|
||||
import static org.objectweb.asm.Opcodes.ACC_SYNCHRONIZED;
|
||||
|
||||
public class Method
|
||||
public class Method implements Annotated, Named
|
||||
{
|
||||
public static final int ACCESS_MODIFIERS = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED;
|
||||
|
||||
@@ -92,8 +91,7 @@ public class Method
|
||||
|
||||
for (Annotation annotation : annotations.getAnnotations())
|
||||
{
|
||||
AnnotationVisitor av = visitor.visitAnnotation(annotation.getType().toString(), true);
|
||||
annotation.accept(av);
|
||||
annotation.accept(visitor.visitAnnotation(annotation.getType().toString(), true));
|
||||
}
|
||||
|
||||
if (code != null)
|
||||
|
||||
6
deobfuscator/src/main/java/net/runelite/asm/Named.java
Normal file
6
deobfuscator/src/main/java/net/runelite/asm/Named.java
Normal file
@@ -0,0 +1,6 @@
|
||||
package net.runelite.asm;
|
||||
|
||||
public interface Named
|
||||
{
|
||||
String getName();
|
||||
}
|
||||
@@ -26,13 +26,16 @@
|
||||
package net.runelite.asm.attributes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import net.runelite.asm.Type;
|
||||
import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.asm.attributes.annotation.Element;
|
||||
import net.runelite.asm.attributes.annotation.SimpleElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class Annotations
|
||||
public class Annotations implements Iterable<Annotation>
|
||||
{
|
||||
private final List<Annotation> annotations = new ArrayList<>();
|
||||
|
||||
@@ -71,15 +74,19 @@ public class Annotations
|
||||
|
||||
public Annotation addAnnotation(Type type, String name, Object value)
|
||||
{
|
||||
Annotation annotation = new Annotation(this);
|
||||
annotation.setType(type);
|
||||
Annotation annotation = new Annotation(type);
|
||||
addAnnotation(annotation);
|
||||
|
||||
Element element = new Element(annotation);
|
||||
element.setName(name);
|
||||
element.setValue(value);
|
||||
Element element = new SimpleElement(name, value);
|
||||
annotation.addElement(element);
|
||||
|
||||
return annotation;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<Annotation> iterator()
|
||||
{
|
||||
return this.annotations.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,6 @@ public class Code
|
||||
|
||||
/**
|
||||
* calculates the size of the lvt required for this method
|
||||
* @return
|
||||
*/
|
||||
public int getMaxLocals()
|
||||
{
|
||||
|
||||
@@ -26,30 +26,26 @@
|
||||
package net.runelite.asm.attributes.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import net.runelite.asm.Type;
|
||||
import net.runelite.asm.attributes.Annotations;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
|
||||
public class Annotation
|
||||
public class Annotation extends Element<List<Element>> implements Iterable<Element>
|
||||
{
|
||||
private final Annotations annotations;
|
||||
private Type type;
|
||||
private final List<Element> elements = new ArrayList<>();
|
||||
private final Type type;
|
||||
|
||||
public Annotation(Annotations annotations)
|
||||
public Annotation(Type type)
|
||||
{
|
||||
this.annotations = annotations;
|
||||
this.value = new ArrayList<>();
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Annotations getAnnotations()
|
||||
{
|
||||
return annotations;
|
||||
}
|
||||
|
||||
public void setType(Type type)
|
||||
public Annotation(String name, Type type)
|
||||
{
|
||||
this.value = new ArrayList<>();
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@@ -60,23 +56,44 @@ public class Annotation
|
||||
|
||||
public List<Element> getElements()
|
||||
{
|
||||
return elements;
|
||||
return value;
|
||||
}
|
||||
|
||||
public Element getElement()
|
||||
{
|
||||
return elements.get(0);
|
||||
return value.get(0);
|
||||
}
|
||||
|
||||
public void addElement(Element element)
|
||||
{
|
||||
elements.add(element);
|
||||
value.add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setValue(List<Element> value)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void accept(AnnotationVisitor visitor)
|
||||
{
|
||||
for (Element element : elements)
|
||||
visitor.visit(element.getName(), element.getValue());
|
||||
if (visitor == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (Element element : this)
|
||||
{
|
||||
accept(visitor, element.name, element.value);
|
||||
}
|
||||
|
||||
visitor.visitEnd();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<Element> iterator()
|
||||
{
|
||||
return this.value.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package net.runelite.asm.attributes.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArrayElement extends Element<List> implements Iterable<Object>
|
||||
{
|
||||
public ArrayElement(String name)
|
||||
{
|
||||
this.name = name;
|
||||
this.value = new ArrayList<>();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addValue(Object value)
|
||||
{
|
||||
this.value.add(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setValue(List value)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Iterator<Object> iterator()
|
||||
{
|
||||
return this.value.iterator();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Stream<Object> stream()
|
||||
{
|
||||
return this.value.stream();
|
||||
}
|
||||
}
|
||||
@@ -25,21 +25,14 @@
|
||||
|
||||
package net.runelite.asm.attributes.annotation;
|
||||
|
||||
public class Element
|
||||
import java.util.List;
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
|
||||
public abstract class Element<T>
|
||||
{
|
||||
private final Annotation annotation;
|
||||
private String name;
|
||||
private Object value;
|
||||
String name = "value";
|
||||
|
||||
public Element(Annotation annotation)
|
||||
{
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
public Annotation getAnnotation()
|
||||
{
|
||||
return annotation;
|
||||
}
|
||||
T value;
|
||||
|
||||
public String getName()
|
||||
{
|
||||
@@ -51,12 +44,12 @@ public class Element
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Object getValue()
|
||||
public T getValue()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(Object value)
|
||||
public void setValue(T value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
@@ -65,4 +58,34 @@ public class Element
|
||||
{
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
public static void accept(AnnotationVisitor visitor, final String name, final Object value)
|
||||
{
|
||||
if (visitor == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (value instanceof Annotation)
|
||||
{
|
||||
Annotation annotation = (Annotation) value;
|
||||
annotation.accept(visitor.visitAnnotation(name, annotation.getType().toString()));
|
||||
}
|
||||
else if (value instanceof List)
|
||||
{
|
||||
AnnotationVisitor arr = visitor.visitArray(name);
|
||||
List<?> arrayValue = (List<?>) value;
|
||||
|
||||
for (Object o : arrayValue)
|
||||
{
|
||||
accept(arr, null, o);
|
||||
}
|
||||
|
||||
arr.visitEnd();
|
||||
}
|
||||
else
|
||||
{
|
||||
visitor.visit(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package net.runelite.asm.attributes.annotation;
|
||||
|
||||
public class SimpleElement extends Element<Object>
|
||||
{
|
||||
public SimpleElement(Object value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public SimpleElement(String name, Object value)
|
||||
{
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -26,11 +26,14 @@ package net.runelite.asm.attributes.code;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import net.runelite.asm.attributes.Code;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class Instructions
|
||||
public class Instructions implements Iterable<Instruction>
|
||||
{
|
||||
private final Code code;
|
||||
private final List<Instruction> instructions = new ArrayList<>();
|
||||
@@ -186,4 +189,25 @@ public class Instructions
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
public int size()
|
||||
{
|
||||
return this.instructions.size();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Iterator<Instruction> iterator()
|
||||
{
|
||||
return this.instructions.iterator();
|
||||
}
|
||||
|
||||
public ListIterator<Instruction> listIterator()
|
||||
{
|
||||
return this.instructions.listIterator();
|
||||
}
|
||||
|
||||
public ListIterator<Instruction> listIterator(int i)
|
||||
{
|
||||
return this.instructions.listIterator(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,8 +52,7 @@ public abstract class ArrayStore extends Instruction implements ArrayStoreInstru
|
||||
if (r.getInstruction() instanceof GetFieldInstruction)
|
||||
{
|
||||
GetFieldInstruction gf = (GetFieldInstruction) r.getInstruction();
|
||||
Field f = gf.getMyField();
|
||||
return f;
|
||||
return gf.getMyField();
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
*/
|
||||
package net.runelite.asm.attributes.code.instructions;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import net.runelite.asm.ClassFile;
|
||||
@@ -86,7 +85,7 @@ public class InvokeStatic extends Instruction implements InvokeInstruction
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<net.runelite.asm.Method> getMethods()
|
||||
{
|
||||
return myMethod != null ? Arrays.asList(myMethod) : Collections.EMPTY_LIST;
|
||||
return myMethod != null ? Collections.singletonList(myMethod) : Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -70,10 +70,9 @@ public class MethodContext
|
||||
return contexts.get(i);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Collection<InstructionContext> getInstructionContexts()
|
||||
{
|
||||
return (Collection) contexts.values();
|
||||
return contexts.values();
|
||||
}
|
||||
|
||||
public void reset()
|
||||
|
||||
@@ -25,44 +25,59 @@
|
||||
|
||||
package net.runelite.asm.visitors;
|
||||
|
||||
import net.runelite.asm.Method;
|
||||
import net.runelite.asm.Type;
|
||||
import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.asm.attributes.annotation.Element;
|
||||
import net.runelite.asm.attributes.annotation.ArrayElement;
|
||||
import net.runelite.asm.attributes.annotation.SimpleElement;
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
public class MethodAnnotationVisitor extends AnnotationVisitor
|
||||
public class AnnotationElementVisitor extends AnnotationVisitor
|
||||
{
|
||||
private final Method method;
|
||||
private final Type type;
|
||||
private final Annotation annotation;
|
||||
|
||||
public MethodAnnotationVisitor(Method method, Type type)
|
||||
AnnotationElementVisitor(Annotation annotation)
|
||||
{
|
||||
super(Opcodes.ASM5);
|
||||
|
||||
this.method = method;
|
||||
this.type = type;
|
||||
|
||||
annotation = new Annotation(method.getAnnotations());
|
||||
annotation.setType(type);
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(String name, Object value)
|
||||
{
|
||||
Element element = new Element(annotation);
|
||||
|
||||
element.setName(name);
|
||||
element.setValue(value);
|
||||
|
||||
SimpleElement element = new SimpleElement(name, value);
|
||||
annotation.addElement(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd()
|
||||
public AnnotationVisitor visitArray(String name)
|
||||
{
|
||||
method.getAnnotations().addAnnotation(annotation);
|
||||
ArrayElement element = new ArrayElement(name);
|
||||
this.annotation.addElement(element);
|
||||
return new AnnotationVisitor(Opcodes.ASM5)
|
||||
{
|
||||
@Override
|
||||
public void visit(String name, Object value)
|
||||
{
|
||||
element.addValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String name, String descriptor)
|
||||
{
|
||||
Annotation annotation = new Annotation(name, new Type(descriptor));
|
||||
element.addValue(annotation);
|
||||
return new AnnotationElementVisitor(annotation);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String name, String descriptor)
|
||||
{
|
||||
Annotation annotation = new Annotation(name, new Type(descriptor));
|
||||
this.annotation.addElement(annotation);
|
||||
return new AnnotationElementVisitor(annotation);
|
||||
}
|
||||
}
|
||||
@@ -22,12 +22,12 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.runelite.asm.visitors;
|
||||
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.Field;
|
||||
import net.runelite.asm.Type;
|
||||
import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
@@ -35,25 +35,25 @@ import org.objectweb.asm.Opcodes;
|
||||
|
||||
public class ClassFieldVisitor extends FieldVisitor
|
||||
{
|
||||
private final ClassFile classFile;
|
||||
private final Field field;
|
||||
|
||||
public ClassFieldVisitor(ClassFile cf, int access, String name, Type desc, Object value)
|
||||
ClassFieldVisitor(ClassFile cf, int access, String name, Type desc, Object value)
|
||||
{
|
||||
super(Opcodes.ASM5);
|
||||
|
||||
this.classFile = cf;
|
||||
this.field = new Field(cf, name, desc);
|
||||
this.field.setAccessFlags(access);
|
||||
this.field.setValue(value);
|
||||
|
||||
field = new Field(cf, name, desc);
|
||||
field.setAccessFlags(access);
|
||||
field.setValue(value);
|
||||
cf.addField(field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
|
||||
{
|
||||
Type type = new Type(desc);
|
||||
return new FieldAnnotationVisitor(field, type);
|
||||
Annotation element = new Annotation(new Type(desc));
|
||||
this.field.getAnnotations().addAnnotation(element);
|
||||
return new AnnotationElementVisitor(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,10 +61,4 @@ public class ClassFieldVisitor extends FieldVisitor
|
||||
{
|
||||
System.out.println(attr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd()
|
||||
{
|
||||
classFile.addField(field);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ package net.runelite.asm.visitors;
|
||||
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.Type;
|
||||
import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
@@ -69,8 +70,10 @@ public class ClassFileVisitor extends ClassVisitor
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
|
||||
{
|
||||
Type type = new Type(desc);
|
||||
return new ClassAnnotationVisitor(classFile, type);
|
||||
Annotation annotation = new Annotation(new Type(desc));
|
||||
classFile.getAnnotations().addAnnotation(annotation);
|
||||
|
||||
return new AnnotationElementVisitor(annotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -32,6 +32,7 @@ import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.Method;
|
||||
import net.runelite.asm.Type;
|
||||
import net.runelite.asm.attributes.Code;
|
||||
import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.asm.attributes.code.Exceptions;
|
||||
import net.runelite.asm.attributes.code.Instruction;
|
||||
import net.runelite.asm.attributes.code.InstructionType;
|
||||
@@ -72,18 +73,14 @@ import static org.objectweb.asm.Opcodes.ICONST_5;
|
||||
import static org.objectweb.asm.Opcodes.ICONST_M1;
|
||||
import static org.objectweb.asm.Opcodes.LCONST_0;
|
||||
import static org.objectweb.asm.Opcodes.LCONST_1;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class CodeVisitor extends MethodVisitor
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(CodeVisitor.class);
|
||||
|
||||
private final ClassFile classFile;
|
||||
private final Method method;
|
||||
private Code code;
|
||||
|
||||
public CodeVisitor(ClassFile classFile, int access, String name, Signature signature, String[] sexceptions)
|
||||
CodeVisitor(ClassFile classFile, int access, String name, Signature signature, String[] sexceptions)
|
||||
{
|
||||
super(Opcodes.ASM5);
|
||||
|
||||
@@ -111,8 +108,9 @@ public class CodeVisitor extends MethodVisitor
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
|
||||
{
|
||||
Type type = new Type(desc);
|
||||
return new MethodAnnotationVisitor(method, type);
|
||||
Annotation element = new Annotation(new Type(desc));
|
||||
this.method.getAnnotations().addAnnotation(element);
|
||||
return new AnnotationElementVisitor(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -333,7 +331,7 @@ public class CodeVisitor extends MethodVisitor
|
||||
if (cst instanceof org.objectweb.asm.Type)
|
||||
{
|
||||
org.objectweb.asm.Type t = (org.objectweb.asm.Type) cst;
|
||||
entry = new net.runelite.asm.pool.Class((String) t.getClassName());
|
||||
entry = new net.runelite.asm.pool.Class(t.getClassName());
|
||||
}
|
||||
|
||||
LDC ldc = new LDC(code.getInstructions(), entry);
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.runelite.asm.visitors;
|
||||
|
||||
import net.runelite.asm.Field;
|
||||
import net.runelite.asm.Type;
|
||||
import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.asm.attributes.annotation.Element;
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
public class FieldAnnotationVisitor extends AnnotationVisitor
|
||||
{
|
||||
private final Field field;
|
||||
private final Type type;
|
||||
private final Annotation annotation;
|
||||
|
||||
public FieldAnnotationVisitor(Field field, Type type)
|
||||
{
|
||||
super(Opcodes.ASM5);
|
||||
|
||||
this.field = field;
|
||||
this.type = type;
|
||||
|
||||
annotation = new Annotation(field.getAnnotations());
|
||||
annotation.setType(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(String name, Object value)
|
||||
{
|
||||
Element element = new Element(annotation);
|
||||
|
||||
element.setName(name);
|
||||
element.setValue(value);
|
||||
|
||||
annotation.addElement(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd()
|
||||
{
|
||||
field.getAnnotations().addAnnotation(annotation);
|
||||
}
|
||||
}
|
||||
@@ -193,6 +193,7 @@ public class Renamer implements Deobfuscator
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void run(ClassGroup group)
|
||||
{
|
||||
group.buildClassGraph();
|
||||
|
||||
@@ -37,6 +37,7 @@ import net.runelite.asm.Method;
|
||||
import net.runelite.asm.attributes.Annotations;
|
||||
import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.asm.attributes.annotation.Element;
|
||||
import net.runelite.asm.attributes.annotation.SimpleElement;
|
||||
import net.runelite.asm.attributes.code.Instruction;
|
||||
import net.runelite.asm.attributes.code.InstructionType;
|
||||
import net.runelite.asm.attributes.code.Instructions;
|
||||
@@ -113,7 +114,6 @@ public class ConstantParameter implements Deobfuscator
|
||||
|
||||
List<StackContext> pops = invokeCtx.getPops();
|
||||
|
||||
outer:
|
||||
// object is popped first, then param 1, 2, 3, etc. double and long take two slots.
|
||||
for (int lvtOffset = offset, parameterIndex = 0;
|
||||
parameterIndex < method.getDescriptor().size();
|
||||
@@ -451,9 +451,7 @@ public class ConstantParameter implements Deobfuscator
|
||||
}
|
||||
|
||||
// Add garbage value
|
||||
Element element = new Element(obfuscatedSignature);
|
||||
element.setName("garbageValue");
|
||||
element.setValue(value.toString());
|
||||
Element element = new SimpleElement("garbageValue", value.toString());
|
||||
obfuscatedSignature.addElement(element);
|
||||
}
|
||||
}
|
||||
@@ -464,12 +462,12 @@ public class ConstantParameter implements Deobfuscator
|
||||
public void run(ClassGroup group)
|
||||
{
|
||||
Execution execution = new Execution(group);
|
||||
execution.addExecutionVisitor(i -> findParameters(i));
|
||||
execution.addExecutionVisitor(this::findParameters);
|
||||
execution.populateInitialMethods();
|
||||
execution.run();
|
||||
|
||||
execution = new Execution(group);
|
||||
execution.addMethodContextVisitor(mc -> findDeadParameters(mc));
|
||||
execution.addMethodContextVisitor(this::findDeadParameters);
|
||||
execution.populateInitialMethods();
|
||||
execution.run();
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import net.runelite.asm.Method;
|
||||
import net.runelite.asm.attributes.Annotations;
|
||||
import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.asm.attributes.annotation.Element;
|
||||
import net.runelite.asm.attributes.annotation.SimpleElement;
|
||||
import net.runelite.deob.DeobAnnotations;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -124,15 +125,12 @@ public class AnnotationMapper
|
||||
{
|
||||
if (isCopyable(a))
|
||||
{
|
||||
Annotation annotation = new Annotation(to);
|
||||
annotation.setType(a.getType());
|
||||
Annotation annotation = new Annotation(a.getType());
|
||||
to.addAnnotation(annotation);
|
||||
|
||||
for (Element e : a.getElements())
|
||||
{
|
||||
Element element = new Element(annotation);
|
||||
element.setName(e.getName());
|
||||
element.setValue(e.getValue());
|
||||
Element element = new SimpleElement(e.getName(), e.getValue());
|
||||
annotation.addElement(element);
|
||||
}
|
||||
|
||||
@@ -155,7 +153,6 @@ public class AnnotationMapper
|
||||
private boolean isCopyable(Annotation a)
|
||||
{
|
||||
return a.getType().equals(DeobAnnotations.EXPORT)
|
||||
|| a.getType().equals(DeobAnnotations.IMPLEMENTS)
|
||||
|| a.getType().equals(DeobAnnotations.HOOK);
|
||||
|| a.getType().equals(DeobAnnotations.IMPLEMENTS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import net.runelite.asm.Method;
|
||||
import net.runelite.asm.attributes.Annotations;
|
||||
import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.asm.attributes.annotation.Element;
|
||||
import net.runelite.asm.attributes.annotation.SimpleElement;
|
||||
import net.runelite.deob.Deob;
|
||||
import net.runelite.deob.DeobAnnotations;
|
||||
import org.slf4j.Logger;
|
||||
@@ -23,6 +24,7 @@ public class AnnotationAdder
|
||||
private final ClassGroup group;
|
||||
private final Logger log = LoggerFactory.getLogger(AnnotationAdder.class);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void run()
|
||||
{
|
||||
int impl = 0;
|
||||
@@ -50,12 +52,9 @@ public class AnnotationAdder
|
||||
{
|
||||
Annotations an = c.getAnnotations();
|
||||
|
||||
Annotation implAn = new Annotation(an);
|
||||
implAn.setType(DeobAnnotations.IMPLEMENTS);
|
||||
Annotation implAn = new Annotation(DeobAnnotations.IMPLEMENTS);
|
||||
|
||||
Element value = new Element(implAn);
|
||||
value.setValue(c.getClassName());
|
||||
value.setName("value");
|
||||
Element value = new SimpleElement(c.getClassName());
|
||||
|
||||
implAn.addElement(value);
|
||||
an.addAnnotation(implAn);
|
||||
@@ -81,12 +80,9 @@ public class AnnotationAdder
|
||||
Annotation a = an.find(DeobAnnotations.EXPORT);
|
||||
if (a == null)
|
||||
{
|
||||
a = new Annotation(an);
|
||||
a.setType(DeobAnnotations.EXPORT);
|
||||
a = new Annotation(DeobAnnotations.EXPORT);
|
||||
|
||||
Element value = new Element(a);
|
||||
value.setValue(fieldName);
|
||||
value.setName("value");
|
||||
Element value = new SimpleElement(fieldName);
|
||||
a.addElement(value);
|
||||
an.addAnnotation(a);
|
||||
|
||||
@@ -114,12 +110,9 @@ public class AnnotationAdder
|
||||
Annotation a = an.find(DeobAnnotations.EXPORT);
|
||||
if (a == null)
|
||||
{
|
||||
a = new Annotation(an);
|
||||
a.setType(DeobAnnotations.EXPORT);
|
||||
a = new Annotation(DeobAnnotations.EXPORT);
|
||||
|
||||
Element value = new Element(a);
|
||||
value.setValue(methodName);
|
||||
value.setName("value");
|
||||
Element value = new SimpleElement(methodName);
|
||||
a.addElement(value);
|
||||
an.addAnnotation(a);
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import net.runelite.asm.Type;
|
||||
import net.runelite.asm.attributes.Annotations;
|
||||
import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.asm.attributes.annotation.Element;
|
||||
import net.runelite.asm.attributes.annotation.SimpleElement;
|
||||
|
||||
public class AnnotationCopier
|
||||
{
|
||||
@@ -98,14 +99,11 @@ public class AnnotationCopier
|
||||
if (!isType(a.getType()))
|
||||
continue;
|
||||
|
||||
Annotation a2 = new Annotation(an2);
|
||||
a2.setType(a.getType());
|
||||
Annotation a2 = new Annotation(a.getType());
|
||||
|
||||
for (Element element : a.getElements())
|
||||
{
|
||||
Element element2 = new Element(a2);
|
||||
element2.setName(element.getName());
|
||||
element2.setValue(element.getValue());
|
||||
Element element2 = new SimpleElement(element.getName(), element.getValue());
|
||||
a2.addElement(element2);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,13 +34,9 @@ import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.deob.DeobAnnotations;
|
||||
import net.runelite.deob.deobfuscators.Renamer;
|
||||
import net.runelite.deob.util.NameMappings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class AnnotationRenamer
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(AnnotationRenamer.class);
|
||||
|
||||
private ClassGroup group;
|
||||
|
||||
public AnnotationRenamer(ClassGroup group)
|
||||
|
||||
@@ -25,9 +25,11 @@
|
||||
package net.runelite.deob.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
@@ -79,6 +81,41 @@ public class JarUtil
|
||||
return group;
|
||||
}
|
||||
|
||||
public static ClassFile loadClass(byte[] bytes)
|
||||
{
|
||||
ClassReader reader = new ClassReader(bytes);
|
||||
ClassFileVisitor cv = new ClassFileVisitor();
|
||||
reader.accept(cv, ClassReader.SKIP_FRAMES);
|
||||
return cv.getClassFile();
|
||||
}
|
||||
|
||||
public static ClassGroup loadClasses(Collection<File> files) throws IOException
|
||||
{
|
||||
final ClassGroup group = new ClassGroup();
|
||||
|
||||
for (File file : files)
|
||||
{
|
||||
if (!file.getName().endsWith(".class"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try (InputStream is = new FileInputStream(file))
|
||||
{
|
||||
ClassReader reader = new ClassReader(is);
|
||||
ClassFileVisitor cv = new ClassFileVisitor();
|
||||
|
||||
reader.accept(cv, ClassReader.SKIP_FRAMES);
|
||||
|
||||
group.addClass(cv.getClassFile());
|
||||
}
|
||||
}
|
||||
|
||||
group.initialize();
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
public static void saveJar(ClassGroup group, File jarfile) throws IOException
|
||||
{
|
||||
try (JarOutputStream jout = new JarOutputStream(new FileOutputStream(jarfile), new Manifest()))
|
||||
|
||||
@@ -276,8 +276,7 @@ public class HookImporter
|
||||
{
|
||||
for (Element e : a.getElements())
|
||||
{
|
||||
String str = (String) e.getValue();
|
||||
return str;
|
||||
return (String) e.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -288,7 +287,7 @@ public class HookImporter
|
||||
private Signature getObfuscatedMethodSignature(Method method)
|
||||
{
|
||||
String sig = getAnnotation(method.getAnnotations(), OBFUSCATED_SIGNATURE);
|
||||
if (sig.isEmpty() == false)
|
||||
if (!sig.isEmpty())
|
||||
{
|
||||
return toObSignature(new Signature(sig)); // if it is annoted, use that
|
||||
}
|
||||
|
||||
@@ -3,4 +3,4 @@ org.gradle.warning.mode=all
|
||||
org.gradle.parallel=true
|
||||
org.gradle.console=rich
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
org.gradle.jvmargs=-XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
2
gradlew
vendored
2
gradlew
vendored
@@ -44,7 +44,7 @@ APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx4g" "-Xms2g" "-Dfile.encoding=UTF-8"'
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
2
gradlew.bat
vendored
2
gradlew.bat
vendored
@@ -30,7 +30,7 @@ set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx4g" "-Xms2g" "-Dfile.encoding=UTF-8"
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
||||
description = 'Web API'
|
||||
|
||||
dependencies {
|
||||
annotationProcessor group: 'org.projectlombok', name: 'lombok', version: lombok
|
||||
|
||||
compileOnly group: 'javax.inject', name: 'javax.inject', version: javaxInject
|
||||
compileOnly group: 'org.projectlombok', name: 'lombok', version: lombok
|
||||
|
||||
implementation group: 'com.google.code.gson', name: 'gson', version: gson
|
||||
implementation group: 'com.google.guava', name: 'guava', version: guava
|
||||
implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: okhttp3
|
||||
implementation group: 'io.reactivex.rxjava2', name: 'rxjava', version: rxjava
|
||||
implementation group: 'org.apache.commons', name: 'commons-csv', version: apacheCommonsCsv
|
||||
implementation group: 'org.slf4j', name: 'slf4j-api', version: slf4j
|
||||
implementation project(':runelite-api')
|
||||
|
||||
testImplementation group: 'com.squareup.okhttp3', name: 'mockwebserver', version: okhttp3
|
||||
testImplementation group: 'junit', name: 'junit', version: junit
|
||||
testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: slf4j
|
||||
}
|
||||
|
||||
processResources {
|
||||
from file("src/main/resources/runelite.properties"), {
|
||||
filter(ReplaceTokens, tokens: [
|
||||
"projectver": project.version,
|
||||
"rsver": rsversion.toString(),
|
||||
"gitcommit": gitCommitShort,
|
||||
"gitdirty": gitDirty.toString()
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -22,42 +22,44 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.injector;
|
||||
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.Method;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Matchers;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
||||
public class InjectConstructTest
|
||||
{
|
||||
@Test
|
||||
public void testInjectConstruct() throws Exception
|
||||
{
|
||||
ClassFile targetClass = new ClassFile();
|
||||
targetClass.setName("test");
|
||||
description = "Web API"
|
||||
|
||||
ClassFile vanillaClass = new ClassFile();
|
||||
vanillaClass.setName("ab");
|
||||
Method constructor = new Method(vanillaClass, "<init>", new Signature("()V"));
|
||||
vanillaClass.addMethod(constructor);
|
||||
dependencies {
|
||||
annotationProcessor(Libraries.lombok)
|
||||
|
||||
Inject inject = mock(Inject.class);
|
||||
when(inject.findVanillaForInterface(Matchers.any(Class.class)))
|
||||
.thenReturn(vanillaClass);
|
||||
InjectConstruct injectConstruct = new InjectConstruct(inject);
|
||||
injectConstruct.injectConstruct(targetClass, APIClass.class.getDeclaredMethod("create"));
|
||||
compileOnly(Libraries.javaxInject)
|
||||
compileOnly(Libraries.lombok)
|
||||
|
||||
assertNotNull(targetClass.findMethod("create"));
|
||||
}
|
||||
|
||||
interface APIClass
|
||||
{
|
||||
APIClass create();
|
||||
}
|
||||
implementation(Libraries.gson)
|
||||
implementation(Libraries.guava)
|
||||
implementation(Libraries.okhttp3)
|
||||
implementation(Libraries.rxjava)
|
||||
implementation(Libraries.apacheCommonsCsv)
|
||||
implementation(Libraries.slf4jApi)
|
||||
implementation(project(":runelite-api"))
|
||||
|
||||
testImplementation(Libraries.okhttp3Webserver)
|
||||
testImplementation(Libraries.junit)
|
||||
testImplementation(Libraries.slf4jSimple)
|
||||
}
|
||||
|
||||
tasks {
|
||||
"processResources"(ProcessResources::class) {
|
||||
val tokens = mapOf(
|
||||
"projectver" to ProjectVersions.rlVersion,
|
||||
"rsver" to ProjectVersions.rsversion.toString(),
|
||||
"gitcommit" to project.extra["gitCommit"]
|
||||
)
|
||||
|
||||
inputs.properties(tokens)
|
||||
|
||||
from("src/main/resources") {
|
||||
include("runelite.properties")
|
||||
|
||||
filter<ReplaceTokens>("tokens" to tokens)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,13 +25,9 @@
|
||||
package net.runelite.http.api;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import net.runelite.http.api.item.ItemEquipmentStats;
|
||||
import net.runelite.http.api.item.ItemPrice;
|
||||
import net.runelite.http.api.item.ItemStats;
|
||||
import net.runelite.http.api.util.TypeAdapters;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
@@ -60,12 +56,9 @@ public class RuneLiteAPI
|
||||
public static final String RUNELITE_AUTH = "RUNELITE-AUTH";
|
||||
|
||||
public static final OkHttpClient CLIENT;
|
||||
public static final Gson GSON = new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(ItemStats.class, TypeAdapters.ITEMSTATS)
|
||||
.registerTypeAdapter(ItemEquipmentStats.class, TypeAdapters.EQUIPMENTSTATS)
|
||||
.registerTypeAdapter(ItemPrice.class, TypeAdapters.ITEMPRICE)
|
||||
.create();
|
||||
public static final Gson GSON = new Gson();
|
||||
public static final MediaType JSON = MediaType.parse("application/json");
|
||||
public static String userAgent;
|
||||
|
||||
private static final String BASE = "https://api.runelite.net";
|
||||
private static final String WSBASE = "https://api.runelite.net/ws";
|
||||
@@ -76,8 +69,6 @@ public class RuneLiteAPI
|
||||
private static final String MAVEN_METADATA = "http://repo.runelite.net/net/runelite/runelite-parent/maven-metadata.xml";
|
||||
|
||||
private static final Properties properties = new Properties();
|
||||
private static String userAgent;
|
||||
|
||||
private static String version;
|
||||
private static String upstreamVersion;
|
||||
private static int rsVersion;
|
||||
|
||||
@@ -61,49 +61,20 @@ public class DiscordEmbed
|
||||
VideoEmbed video;
|
||||
ProviderEmbed provider;
|
||||
AuthorEmbed author;
|
||||
@Builder.Default
|
||||
List<FieldEmbed> fields = new ArrayList<>();
|
||||
final List<FieldEmbed> fields = new ArrayList<>();
|
||||
|
||||
public DiscordEmbed()
|
||||
public DiscordEmbed(AuthorEmbed author, ThumbnailEmbed thumb, String description, FooterEmbed footer, String color, List<FieldEmbed> fields)
|
||||
{
|
||||
|
||||
this.author = author;
|
||||
this.thumbnail = thumb;
|
||||
this.description = description;
|
||||
this.footer = footer;
|
||||
this.color = color;
|
||||
this.fields.addAll(fields);
|
||||
}
|
||||
|
||||
public DiscordEmbed(String title, String description)
|
||||
public DiscordMessage toDiscordMessage(String username, String content, String avatarUrl)
|
||||
{
|
||||
this(title, description, null);
|
||||
}
|
||||
|
||||
public DiscordEmbed(String title, String description, String url)
|
||||
{
|
||||
setTitle(title);
|
||||
setDescription(description);
|
||||
setUrl(url);
|
||||
}
|
||||
|
||||
public static DiscordMessage toDiscordMessage(DiscordEmbed embed, String username, String avatarURL)
|
||||
{
|
||||
return DiscordMessage.builder()
|
||||
.username(username)
|
||||
.avatarUrl(avatarURL)
|
||||
.content("")
|
||||
.embed(embed)
|
||||
.build();
|
||||
}
|
||||
|
||||
public DiscordMessage toDiscordMessage(String username, String avatarUrl)
|
||||
{
|
||||
return DiscordEmbed.toDiscordMessage(this, username, avatarUrl);
|
||||
}
|
||||
|
||||
public static class DiscordEmbedBuilder
|
||||
{
|
||||
List<FieldEmbed> fields = new ArrayList<>();
|
||||
|
||||
public DiscordEmbedBuilder field(FieldEmbed field)
|
||||
{
|
||||
fields.add(field);
|
||||
return this;
|
||||
}
|
||||
return new DiscordMessage(username, content, avatarUrl, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,16 +29,12 @@ package net.runelite.http.api.discord;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
public class DiscordMessage
|
||||
{
|
||||
@@ -48,24 +44,14 @@ public class DiscordMessage
|
||||
String avatarUrl;
|
||||
@SerializedName("tts")
|
||||
boolean textToSpeech;
|
||||
List<DiscordEmbed> embeds = new ArrayList<>();
|
||||
final List<DiscordEmbed> embeds = new ArrayList<>();
|
||||
|
||||
public DiscordMessage()
|
||||
DiscordMessage(String username, String content, String avatar_url, DiscordEmbed embed)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public DiscordMessage(String username, String content, String avatar_url)
|
||||
{
|
||||
this(username, content, avatar_url, false);
|
||||
}
|
||||
|
||||
public DiscordMessage(String username, String content, String avatar_url, boolean tts)
|
||||
{
|
||||
setUsername(username);
|
||||
setContent(content);
|
||||
setAvatarUrl(avatar_url);
|
||||
setTextToSpeech(tts);
|
||||
this.username = username;
|
||||
this.content = content;
|
||||
this.avatarUrl = avatar_url;
|
||||
this.embeds.add(embed);
|
||||
}
|
||||
|
||||
public void setUsername(String username)
|
||||
@@ -79,15 +65,4 @@ public class DiscordMessage
|
||||
this.username = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DiscordMessageBuilder
|
||||
{
|
||||
List<DiscordEmbed> embeds = new ArrayList<>();
|
||||
|
||||
public DiscordMessageBuilder embed(DiscordEmbed embed)
|
||||
{
|
||||
embeds.add(embed);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,10 +30,10 @@ import java.util.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.http.api.RuneLiteAPI;
|
||||
import static net.runelite.http.api.RuneLiteAPI.JSON;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
@@ -42,7 +42,6 @@ import okhttp3.Response;
|
||||
@AllArgsConstructor
|
||||
public class GrandExchangeClient
|
||||
{
|
||||
private static final MediaType JSON = MediaType.parse("application/json");
|
||||
private static final Gson GSON = RuneLiteAPI.GSON;
|
||||
|
||||
private final UUID uuid;
|
||||
|
||||
@@ -36,10 +36,10 @@ import java.util.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.http.api.RuneLiteAPI;
|
||||
import static net.runelite.http.api.RuneLiteAPI.JSON;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
@@ -48,7 +48,6 @@ import okhttp3.Response;
|
||||
@AllArgsConstructor
|
||||
public class LootTrackerClient
|
||||
{
|
||||
private static final MediaType JSON = MediaType.parse("application/json");
|
||||
private static final Gson GSON = RuneLiteAPI.GSON;
|
||||
|
||||
private final UUID uuid;
|
||||
|
||||
@@ -31,10 +31,10 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.List;
|
||||
import net.runelite.http.api.RuneLiteAPI;
|
||||
import static net.runelite.http.api.RuneLiteAPI.JSON;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
@@ -43,8 +43,6 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
public class XteaClient
|
||||
{
|
||||
private static final MediaType JSON = MediaType.parse("application/json");
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(XteaClient.class);
|
||||
|
||||
public void submit(XteaRequest xteaRequest)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
runelite.version=@projectver@
|
||||
rs.version=@rsver@
|
||||
runelite.commit=@gitcommit@
|
||||
runelite.dirty=@gitdirty@
|
||||
@@ -1,26 +0,0 @@
|
||||
apply plugin: 'war'
|
||||
|
||||
description = 'Web Service OpenOSRS'
|
||||
|
||||
dependencies {
|
||||
annotationProcessor group: 'org.projectlombok', name: 'lombok', version: lombok
|
||||
|
||||
api project(':cache')
|
||||
api project(':http-api')
|
||||
api project(':http-service')
|
||||
|
||||
implementation group: 'com.google.code.gson', name: 'gson', version: gson
|
||||
implementation group: 'com.google.guava', name: 'guava', version: guava
|
||||
implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: okhttp3
|
||||
implementation group: 'org.springframework', name: 'spring-jdbc', version: springJdbc
|
||||
implementation group: 'org.springframework.boot', name: 'spring-boot-devtools', version: springboot
|
||||
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springboot
|
||||
implementation group: 'org.sql2o', name: 'sql2o', version: sql2o
|
||||
implementation(group: 'redis.clients', name: 'jedis', version: jedis) {
|
||||
exclude(module: 'commons-pool2')
|
||||
}
|
||||
|
||||
providedCompile group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: mariadbJdbc
|
||||
providedCompile group: 'org.projectlombok', name: 'lombok', version: lombok
|
||||
providedCompile group: 'org.springframework.boot', name: 'spring-boot-starter-tomcat', version: springboot
|
||||
}
|
||||
53
http-service-openosrs/http-service-openosrs.gradle.kts
Normal file
53
http-service-openosrs/http-service-openosrs.gradle.kts
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
war
|
||||
}
|
||||
|
||||
description = "Web Service OpenOSRS"
|
||||
|
||||
dependencies {
|
||||
annotationProcessor(Libraries.lombok)
|
||||
|
||||
api(project(":cache"))
|
||||
api(project(":http-api"))
|
||||
api(project(":http-service"))
|
||||
|
||||
implementation(Libraries.gson)
|
||||
implementation(Libraries.guava)
|
||||
implementation(Libraries.okhttp3)
|
||||
implementation(Libraries.springbootJdbc)
|
||||
implementation(Libraries.springbootDevtools)
|
||||
implementation(Libraries.springbootStarterWeb)
|
||||
implementation(Libraries.sql2o)
|
||||
implementation(Libraries.jedis) {
|
||||
exclude(module = "commons-pool2")
|
||||
}
|
||||
|
||||
providedCompile(Libraries.mariadbJdbc)
|
||||
providedCompile(Libraries.lombok)
|
||||
providedCompile(Libraries.springbootStarterTomcat)
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
apply plugin: 'war'
|
||||
|
||||
description = 'Web Service'
|
||||
|
||||
dependencies {
|
||||
annotationProcessor group: 'org.mapstruct', name: 'mapstruct-processor', version: mapstruct
|
||||
annotationProcessor group: 'org.projectlombok', name: 'lombok', version: lombok
|
||||
|
||||
api project(':cache')
|
||||
api project(':http-api')
|
||||
api project(':runelite-api')
|
||||
|
||||
implementation group: 'com.github.scribejava', name: 'scribejava-apis', version: scribejava
|
||||
implementation group: 'com.google.code.gson', name: 'gson', version: gson
|
||||
implementation group: 'com.google.guava', name: 'guava', version: guava
|
||||
implementation group: 'io.minio', name: 'minio', version: minio
|
||||
implementation group: 'org.mapstruct', name: 'mapstruct-jdk8', version: mapstruct
|
||||
implementation group: 'org.mongodb', name: 'mongodb-driver-sync', version: mongodbDriverSync
|
||||
implementation group: 'org.slf4j', name: 'slf4j-api', version: slf4j
|
||||
implementation group: 'org.springframework', name: 'spring-jdbc', version: springJdbc
|
||||
implementation group: 'org.springframework.boot', name: 'spring-boot-devtools', version: springboot
|
||||
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springboot
|
||||
implementation group: 'org.sql2o', name: 'sql2o', version: sql2o
|
||||
implementation(group: 'redis.clients', name: 'jedis', version: jedis) {
|
||||
exclude(module: 'commons-pool2')
|
||||
}
|
||||
|
||||
providedCompile group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: mariadbJdbc
|
||||
providedCompile group: 'org.projectlombok', name: 'lombok', version: lombok
|
||||
providedCompile group: 'org.springframework.boot', name: 'spring-boot-starter-tomcat', version: springboot
|
||||
|
||||
testImplementation group: 'com.h2database', name: 'h2', version: '1.4.200'
|
||||
testImplementation group: 'com.squareup.okhttp3', name: 'mockwebserver', version: okhttp3
|
||||
testImplementation(group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: springboot) {
|
||||
exclude(module: 'commons-logging')
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -22,53 +22,43 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.injector;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import net.runelite.asm.ClassGroup;
|
||||
import net.runelite.deob.DeobTestProperties;
|
||||
import net.runelite.deob.TemporyFolderLocation;
|
||||
import net.runelite.deob.util.JarUtil;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
public class InjectTest
|
||||
{
|
||||
@Rule
|
||||
public DeobTestProperties properties = new DeobTestProperties();
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder folder = TemporyFolderLocation.getTemporaryFolder();
|
||||
|
||||
private ClassGroup deob, vanilla;
|
||||
|
||||
@Before
|
||||
public void before() throws IOException
|
||||
{
|
||||
deob = JarUtil.loadJar(new File(properties.getRsClient()));
|
||||
vanilla = JarUtil.loadJar(new File(properties.getVanillaClient()));
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() throws IOException
|
||||
{
|
||||
JarUtil.saveJar(vanilla, folder.newFile());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testRun() throws InjectionException
|
||||
{
|
||||
Inject instance = new Inject(deob, vanilla);
|
||||
instance.run();
|
||||
|
||||
InjectorValidator iv = new InjectorValidator(vanilla);
|
||||
iv.validate();
|
||||
}
|
||||
|
||||
plugins {
|
||||
war
|
||||
}
|
||||
|
||||
description = "Web Service"
|
||||
|
||||
dependencies {
|
||||
annotationProcessor(Libraries.mapstructProcessor)
|
||||
annotationProcessor(Libraries.lombok)
|
||||
|
||||
api(project(":cache"))
|
||||
api(project(":http-api"))
|
||||
api(project(":runelite-api"))
|
||||
|
||||
implementation(Libraries.scribejava)
|
||||
implementation(Libraries.gson)
|
||||
implementation(Libraries.guava)
|
||||
implementation(Libraries.minio)
|
||||
implementation(Libraries.mapstruct)
|
||||
implementation(Libraries.mongodbDriverSync)
|
||||
implementation(Libraries.slf4jApi)
|
||||
implementation(Libraries.springbootJdbc)
|
||||
implementation(Libraries.springbootDevtools)
|
||||
implementation(Libraries.springbootStarterWeb)
|
||||
implementation(Libraries.sql2o)
|
||||
implementation(Libraries.jedis) {
|
||||
exclude(module = "commons-pool2")
|
||||
}
|
||||
|
||||
providedCompile(Libraries.mariadbJdbc)
|
||||
providedCompile(Libraries.lombok)
|
||||
providedCompile(Libraries.springbootStarterTomcat)
|
||||
|
||||
testImplementation(Libraries.h2)
|
||||
testImplementation(Libraries.okhttp3Webserver)
|
||||
testImplementation(Libraries.springbootStarterTest) {
|
||||
exclude(module = "commons-logging")
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,7 @@ import net.runelite.http.api.config.ConfigEntry;
|
||||
import net.runelite.http.api.config.Configuration;
|
||||
import org.bson.Document;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@@ -66,11 +67,12 @@ public class ConfigService
|
||||
|
||||
@Autowired
|
||||
public ConfigService(
|
||||
MongoClient mongoClient
|
||||
MongoClient mongoClient,
|
||||
@Value("${mongo.database}") String databaseName
|
||||
)
|
||||
{
|
||||
|
||||
MongoDatabase database = mongoClient.getDatabase("config");
|
||||
MongoDatabase database = mongoClient.getDatabase(databaseName);
|
||||
MongoCollection<Document> collection = database.getCollection("config");
|
||||
this.mongoCollection = collection;
|
||||
|
||||
|
||||
@@ -98,6 +98,11 @@ public class OSBGrandExchangeService
|
||||
Integer itemId = entry.getKey();
|
||||
OsbuddySummaryItem item = entry.getValue();
|
||||
|
||||
if (item.getBuy_average() <= 0 || item.getSell_average() <= 0 || item.getOverall_average() <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
query
|
||||
.addParameter("itemId", itemId)
|
||||
.addParameter("buyAverage", item.getBuy_average())
|
||||
|
||||
@@ -32,10 +32,11 @@ redis:
|
||||
|
||||
mongo:
|
||||
jndiName: java:comp/env/mongodb/runelite
|
||||
database: runelite
|
||||
|
||||
# Twitter client for feed
|
||||
runelite:
|
||||
twitter:
|
||||
consumerkey:
|
||||
secretkey:
|
||||
listid: 968949795153948673
|
||||
listid: 1185897074786742273
|
||||
71
injected-client/injected-client.gradle.kts
Normal file
71
injected-client/injected-client.gradle.kts
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2019 ThatGamerBlue
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
apply<FernflowerPlugin>()
|
||||
|
||||
description = "Injected Client"
|
||||
|
||||
plugins {
|
||||
id("com.openosrs.injector")
|
||||
}
|
||||
|
||||
configurations {
|
||||
create("vanilla")
|
||||
create("injected-client")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
"vanilla"(Libraries.vanilla)
|
||||
}
|
||||
|
||||
injector {
|
||||
mixins.set(tasks.getByPath(":runelite-mixins:jar").outputs.files.singleFile)
|
||||
rsapi.set(tasks.getByPath(":runescape-api:jar").outputs.files.singleFile)
|
||||
rsclient.set(tasks.getByPath(":runescape-client:jar").outputs.files.singleFile)
|
||||
vanilla.set(project.file(configurations["vanilla"].asPath))
|
||||
}
|
||||
|
||||
artifacts {
|
||||
add("runtime", tasks.inject.get().output) {
|
||||
builtBy(tasks.inject)
|
||||
}
|
||||
}
|
||||
|
||||
// keep the sourcesets etc but remove useless tasks
|
||||
tasks {
|
||||
classes {
|
||||
enabled = false
|
||||
}
|
||||
compileJava {
|
||||
enabled = false
|
||||
}
|
||||
jar {
|
||||
enabled = false
|
||||
}
|
||||
processResources {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
group = 'us.runelitepl.rs'
|
||||
description = 'Injector'
|
||||
|
||||
def buildPath = buildDir.toString().replace('\\', '/') // this doesnt work in an ext block for some reason
|
||||
def deobfuscatedJar = "${rootPath}/runescape-client/build/libs/rs-client-${project.version}.jar"
|
||||
def vanillaJar = "${buildPath}/vanilla-${rsversion}.jar"
|
||||
|
||||
configurations {
|
||||
vanilla
|
||||
}
|
||||
|
||||
dependencies {
|
||||
annotationProcessor group: 'org.eclipse.sisu', name: 'org.eclipse.sisu.inject', version: sisu
|
||||
|
||||
compileOnly group: 'org.apache.maven.plugin-tools', name: 'maven-plugin-annotations', version: mavenPluginAnnotations
|
||||
|
||||
implementation group: 'com.google.guava', name: 'guava', version: guava
|
||||
implementation group: 'org.apache.maven', name: 'maven-plugin-api', version: mavenPluginApi
|
||||
implementation group: 'org.ow2.asm', name: 'asm', version: asm
|
||||
implementation group: 'org.ow2.asm', name: 'asm-util', version: asm
|
||||
implementation project(':deobfuscator')
|
||||
implementation project(':mixins')
|
||||
implementation project(':runelite-api')
|
||||
implementation project(':runescape-api')
|
||||
|
||||
testImplementation group: 'junit', name: 'junit', version: junit
|
||||
testImplementation group: 'org.mockito', name: 'mockito-core', version: mockito
|
||||
testImplementation project(':deobfuscator')
|
||||
testImplementation project(path: ':deobfuscator', configuration: 'testArchives')
|
||||
|
||||
vanilla "net.runelite.rs:vanilla:${rsversion}"
|
||||
}
|
||||
|
||||
compileJava {
|
||||
dependsOn ":rs-client:build"
|
||||
|
||||
inputs.dir("${project.rootDir}/runescape-client/")
|
||||
inputs.dir("${project.rootDir}/runescape-api/")
|
||||
inputs.dir("${project.rootDir}/runelite-mixins/")
|
||||
}
|
||||
|
||||
compileJava.doLast() {
|
||||
copy {
|
||||
from configurations.vanilla
|
||||
into "$buildDir"
|
||||
}
|
||||
def path = sourceSets.main.runtimeClasspath
|
||||
def loader
|
||||
try {
|
||||
loader = new URLClassLoader(path.collect { f -> f.toURI().toURL() } as URL[])
|
||||
def inject = loader.loadClass('net.runelite.injector.Injector')
|
||||
String[] jarPaths = [
|
||||
deobfuscatedJar.toString(),
|
||||
vanillaJar.toString(),
|
||||
injectedClassesPath.toString()
|
||||
]
|
||||
inject.main(jarPaths)
|
||||
} finally {
|
||||
if (loader) {
|
||||
loader.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,582 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.injector;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.ClassGroup;
|
||||
import net.runelite.asm.Field;
|
||||
import net.runelite.asm.Interfaces;
|
||||
import net.runelite.asm.Method;
|
||||
import net.runelite.asm.Type;
|
||||
import net.runelite.asm.attributes.Annotations;
|
||||
import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.asm.attributes.code.Instruction;
|
||||
import net.runelite.asm.attributes.code.Instructions;
|
||||
import net.runelite.asm.attributes.code.instructions.ALoad;
|
||||
import net.runelite.asm.attributes.code.instructions.DLoad;
|
||||
import net.runelite.asm.attributes.code.instructions.FLoad;
|
||||
import net.runelite.asm.attributes.code.instructions.ILoad;
|
||||
import net.runelite.asm.attributes.code.instructions.LLoad;
|
||||
import net.runelite.asm.pool.Class;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import net.runelite.deob.DeobAnnotations;
|
||||
import net.runelite.deob.deobfuscators.arithmetic.DMath;
|
||||
import static net.runelite.injector.InjectUtil.getFieldType;
|
||||
import net.runelite.injector.raw.ClearColorBuffer;
|
||||
import net.runelite.injector.raw.DrawAfterWidgets;
|
||||
import net.runelite.injector.raw.Occluder;
|
||||
import net.runelite.injector.raw.RasterizerHook;
|
||||
import net.runelite.injector.raw.RenderDraw;
|
||||
import net.runelite.injector.raw.ScriptVM;
|
||||
import net.runelite.mapping.Import;
|
||||
import net.runelite.rs.api.RSClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import net.runelite.injector.raw.HidePlayerAttacks;
|
||||
|
||||
// import net.runelite.injector.raw.DrawMenu;
|
||||
|
||||
public class Inject
|
||||
{
|
||||
public static final java.lang.Class<?> CLIENT_CLASS = RSClient.class;
|
||||
public static final String API_PACKAGE_BASE = "net.runelite.rs.api.RS";
|
||||
public static final String RL_API_PACKAGE_BASE = "net.runelite.api.";
|
||||
private static final Logger logger = LoggerFactory.getLogger(Inject.class);
|
||||
private final InjectHookMethod hookMethod = new InjectHookMethod(this);
|
||||
|
||||
private final InjectGetter getters = new InjectGetter(this);
|
||||
private final InjectSetter setters = new InjectSetter(this);
|
||||
private final InjectInvoker invokes = new InjectInvoker(this);
|
||||
private final InjectConstruct construct = new InjectConstruct(this);
|
||||
|
||||
private final MixinInjector mixinInjector = new MixinInjector(this);
|
||||
|
||||
// deobfuscated contains exports etc to apply to vanilla
|
||||
private final ClassGroup deobfuscated, vanilla;
|
||||
|
||||
public Inject(ClassGroup deobfuscated, ClassGroup vanilla)
|
||||
{
|
||||
this.deobfuscated = deobfuscated;
|
||||
this.vanilla = vanilla;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a java.lang.Class to a Type
|
||||
*
|
||||
* @param c
|
||||
* @return
|
||||
*/
|
||||
public static Type classToType(java.lang.Class<?> c)
|
||||
{
|
||||
int dimms = 0;
|
||||
while (c.isArray())
|
||||
{
|
||||
c = c.getComponentType();
|
||||
++dimms;
|
||||
}
|
||||
|
||||
if (c.isPrimitive())
|
||||
{
|
||||
String s;
|
||||
|
||||
switch (c.getName())
|
||||
{
|
||||
case "int":
|
||||
s = "I";
|
||||
break;
|
||||
case "long":
|
||||
s = "J";
|
||||
break;
|
||||
case "boolean":
|
||||
s = "Z";
|
||||
break;
|
||||
case "char":
|
||||
s = "C";
|
||||
break;
|
||||
case "short":
|
||||
s = "S";
|
||||
break;
|
||||
case "float":
|
||||
s = "F";
|
||||
break;
|
||||
case "double":
|
||||
s = "D";
|
||||
break;
|
||||
case "byte":
|
||||
s = "B";
|
||||
break;
|
||||
case "void":
|
||||
s = "V";
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("unknown primitive type " + c.getName());
|
||||
}
|
||||
|
||||
return Type.getType(s, dimms);
|
||||
}
|
||||
|
||||
return Type.getType("L" + c.getName().replace('.', '/') + ";", dimms);
|
||||
}
|
||||
|
||||
public Signature getMethodSignature(Method m)
|
||||
{
|
||||
Signature signature = m.getDescriptor();
|
||||
|
||||
Annotation obfSignature = m.getAnnotations().find(DeobAnnotations.OBFUSCATED_SIGNATURE);
|
||||
if (obfSignature != null)
|
||||
{
|
||||
//Annotation exists. Signature was updated by us during deobfuscation
|
||||
signature = DeobAnnotations.getObfuscatedSignature(m);
|
||||
}
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a Signature from a java method
|
||||
*
|
||||
* @param method
|
||||
* @return
|
||||
*/
|
||||
public Signature javaMethodToSignature(java.lang.reflect.Method method)
|
||||
{
|
||||
Signature.Builder builder = new Signature.Builder()
|
||||
.setReturnType(classToType(method.getReturnType()));
|
||||
for (java.lang.Class<?> clazz : method.getParameterTypes())
|
||||
{
|
||||
builder.addArgument(classToType(clazz));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public void run() throws InjectionException
|
||||
{
|
||||
Map<ClassFile, java.lang.Class> implemented = new HashMap<>();
|
||||
|
||||
// inject interfaces first, so the validateTypeIsConvertibleTo
|
||||
// check below works
|
||||
for (ClassFile cf : deobfuscated.getClasses())
|
||||
{
|
||||
Annotations an = cf.getAnnotations();
|
||||
|
||||
if (an == null || an.size() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
String obfuscatedName = DeobAnnotations.getObfuscatedName(an);
|
||||
if (obfuscatedName == null)
|
||||
{
|
||||
obfuscatedName = cf.getName();
|
||||
}
|
||||
|
||||
ClassFile other = vanilla.findClass(obfuscatedName);
|
||||
assert other != null : "unable to find vanilla class from obfuscated name: " + obfuscatedName;
|
||||
|
||||
java.lang.Class implementingClass = injectInterface(cf, other);
|
||||
// it can not implement an interface but still have exported static fields, which are
|
||||
// moved to client
|
||||
|
||||
implemented.put(cf, implementingClass);
|
||||
}
|
||||
|
||||
// Has to be done before mixins
|
||||
// well, can be done after really
|
||||
// but why do that when you can do it before
|
||||
new RasterizerHook(this).inject();
|
||||
|
||||
// requires interfaces to be injected
|
||||
mixinInjector.inject();
|
||||
construct.inject(implemented);
|
||||
|
||||
for (ClassFile cf : deobfuscated.getClasses())
|
||||
{
|
||||
java.lang.Class implementingClass = implemented.get(cf);
|
||||
Annotations an = cf.getAnnotations();
|
||||
|
||||
if (an == null || an.size() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
String obfuscatedName = DeobAnnotations.getObfuscatedName(an);
|
||||
if (obfuscatedName == null)
|
||||
{
|
||||
obfuscatedName = cf.getName();
|
||||
}
|
||||
|
||||
ClassFile other = vanilla.findClass(obfuscatedName);
|
||||
assert other != null : "unable to find vanilla class from obfuscated name: " + obfuscatedName;
|
||||
|
||||
for (Field f : cf.getFields())
|
||||
{
|
||||
an = f.getAnnotations();
|
||||
|
||||
if (an == null || an.find(DeobAnnotations.EXPORT) == null)
|
||||
{
|
||||
continue; // not an exported field
|
||||
}
|
||||
|
||||
Annotation exportAnnotation = an.find(DeobAnnotations.EXPORT);
|
||||
String exportedName = exportAnnotation.getElement().getString();
|
||||
|
||||
obfuscatedName = DeobAnnotations.getObfuscatedName(an);
|
||||
|
||||
Annotation getterAnnotation = an.find(DeobAnnotations.OBFUSCATED_GETTER);
|
||||
Number getter = null;
|
||||
if (getterAnnotation != null)
|
||||
{
|
||||
getter = (Number) getterAnnotation.getElement().getValue();
|
||||
}
|
||||
// the ob jar is the same as the vanilla so this field must exist in this class.
|
||||
|
||||
Type obType = getFieldType(f);
|
||||
Field otherf = other.findField(obfuscatedName, obType);
|
||||
assert otherf != null;
|
||||
|
||||
assert f.isStatic() == otherf.isStatic();
|
||||
|
||||
ClassFile targetClass = f.isStatic() ? vanilla.findClass("client") : other; // target class for getter
|
||||
java.lang.Class targetApiClass = f.isStatic() ? CLIENT_CLASS : implementingClass; // target api class for getter
|
||||
if (targetApiClass == null)
|
||||
{
|
||||
assert !f.isStatic();
|
||||
|
||||
// non static field exported on non exported interface
|
||||
// logger.debug("Non static exported field {} on non exported interface", exportedName);
|
||||
continue;
|
||||
}
|
||||
|
||||
java.lang.reflect.Method apiMethod = findImportMethodOnApi(targetApiClass, exportedName, true);
|
||||
if (apiMethod != null)
|
||||
{
|
||||
Number setter = null;
|
||||
if (getter != null)
|
||||
{
|
||||
setter = DMath.modInverse(getter); // inverse getter to get the setter
|
||||
}
|
||||
|
||||
setters.injectSetter(targetClass, targetApiClass, otherf, exportedName, setter);
|
||||
}
|
||||
|
||||
apiMethod = findImportMethodOnApi(targetApiClass, exportedName, false);
|
||||
if (apiMethod == null)
|
||||
{
|
||||
//logger.debug("Unable to find import method on api class {} with imported name {}, not injecting getter", targetApiClass, exportedName);
|
||||
continue;
|
||||
}
|
||||
|
||||
// check that otherf is converable to apiMethod's
|
||||
// return type
|
||||
Type fieldType = otherf.getType();
|
||||
Type returnType = classToType(apiMethod.getReturnType());
|
||||
if (!validateTypeIsConvertibleTo(fieldType, returnType))
|
||||
{
|
||||
throw new InjectionException("Type " + fieldType + " is not convertable to " + returnType + " for getter " + apiMethod);
|
||||
}
|
||||
|
||||
getters.injectGetter(targetClass, apiMethod, otherf, getter);
|
||||
}
|
||||
|
||||
for (Method m : cf.getMethods())
|
||||
{
|
||||
hookMethod.process(m);
|
||||
invokes.process(m, other, implementingClass);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Injected {} getters, {} setters, {} invokers",
|
||||
getters.getInjectedGetters(),
|
||||
setters.getInjectedSetters(), invokes.getInjectedInvokers());
|
||||
|
||||
new DrawAfterWidgets(this).inject();
|
||||
new ScriptVM(this).inject();
|
||||
new ClearColorBuffer(this).inject();
|
||||
new RenderDraw(this).inject();
|
||||
// new DrawMenu(this).inject();
|
||||
new Occluder(this).inject();
|
||||
new HidePlayerAttacks(this).inject();
|
||||
}
|
||||
|
||||
private java.lang.Class injectInterface(ClassFile cf, ClassFile other)
|
||||
{
|
||||
Annotations an = cf.getAnnotations();
|
||||
if (an == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Annotation a = an.find(DeobAnnotations.IMPLEMENTS);
|
||||
if (a == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
String ifaceName = API_PACKAGE_BASE + a.getElement().getString();
|
||||
java.lang.Class<?> apiClass;
|
||||
|
||||
try
|
||||
{
|
||||
apiClass = java.lang.Class.forName(ifaceName);
|
||||
}
|
||||
catch (ClassNotFoundException ex)
|
||||
{
|
||||
logger.trace("Class {} implements nonexistent interface {}, skipping interface injection",
|
||||
cf.getName(),
|
||||
ifaceName);
|
||||
return null;
|
||||
}
|
||||
|
||||
String ifaceNameInternal = ifaceName.replace('.', '/'); // to internal name
|
||||
Class clazz = new Class(ifaceNameInternal);
|
||||
|
||||
Interfaces interfaces = other.getInterfaces();
|
||||
interfaces.addInterface(clazz);
|
||||
|
||||
return apiClass;
|
||||
}
|
||||
|
||||
public java.lang.reflect.Method findImportMethodOnApi(java.lang.Class<?> clazz, String name, Boolean setter)
|
||||
{
|
||||
for (java.lang.reflect.Method method : clazz.getDeclaredMethods())
|
||||
{
|
||||
if (method.isSynthetic())
|
||||
{
|
||||
/*
|
||||
* If you override an interface method in another interface
|
||||
* with a return type that is a child of the overriden methods
|
||||
* return type, both methods end up in the interface, and both
|
||||
* are *annotated*. But the base one is synthetic.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
|
||||
Import i = method.getAnnotation(Import.class);
|
||||
|
||||
if (i == null || !name.equals(i.value()) || (setter != null && (method.getParameterCount() > 0) != setter))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* create a load instruction for a variable of type from a given index
|
||||
*
|
||||
* @param instructions
|
||||
* @param type
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public Instruction createLoadForTypeIndex(Instructions instructions, Type type, int index)
|
||||
{
|
||||
if (type.getDimensions() > 0 || !type.isPrimitive())
|
||||
{
|
||||
return new ALoad(instructions, index);
|
||||
}
|
||||
|
||||
switch (type.toString())
|
||||
{
|
||||
case "B":
|
||||
case "C":
|
||||
case "I":
|
||||
case "S":
|
||||
case "Z":
|
||||
return new ILoad(instructions, index);
|
||||
case "D":
|
||||
return new DLoad(instructions, index);
|
||||
case "F":
|
||||
return new FLoad(instructions, index);
|
||||
case "J":
|
||||
return new LLoad(instructions, index);
|
||||
default:
|
||||
throw new RuntimeException("Unknown type");
|
||||
}
|
||||
}
|
||||
|
||||
ClassFile toDeobClass(ClassFile obClass)
|
||||
{
|
||||
for (ClassFile cf : deobfuscated.getClasses())
|
||||
{
|
||||
String obfuscatedName = DeobAnnotations.getObfuscatedName(cf.getAnnotations());
|
||||
|
||||
if (obClass.getName().equalsIgnoreCase(obfuscatedName))
|
||||
{
|
||||
return cf;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Type deobfuscatedTypeToApiType(Type type) throws InjectionException
|
||||
{
|
||||
if (type.isPrimitive())
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
ClassFile cf = deobfuscated.findClass(type.getInternalName());
|
||||
if (cf == null)
|
||||
{
|
||||
return type; // not my type
|
||||
}
|
||||
|
||||
java.lang.Class<?> rsApiType;
|
||||
try
|
||||
{
|
||||
rsApiType = java.lang.Class.forName(API_PACKAGE_BASE + cf.getName().replace("/", "."));
|
||||
}
|
||||
catch (ClassNotFoundException ex)
|
||||
{
|
||||
throw new InjectionException("Deobfuscated type " + type.getInternalName() + " has no API type", ex);
|
||||
}
|
||||
|
||||
java.lang.Class<?> rlApiType = null;
|
||||
|
||||
for (java.lang.Class<?> inter : rsApiType.getInterfaces())
|
||||
{
|
||||
if (inter.getName().startsWith(RL_API_PACKAGE_BASE))
|
||||
{
|
||||
rlApiType = inter;
|
||||
}
|
||||
}
|
||||
|
||||
// if (rlApiType == null)
|
||||
// {
|
||||
// throw new InjectionException("RS API type " + rsApiType + " does not extend RL API interface");
|
||||
// }
|
||||
|
||||
final java.lang.Class<?> finalType = rlApiType == null ? rsApiType : rlApiType;
|
||||
|
||||
return Type.getType("L" + finalType.getName().replace('.', '/') + ";", type.getDimensions());
|
||||
}
|
||||
|
||||
Type apiTypeToDeobfuscatedType(Type type)
|
||||
{
|
||||
if (type.isPrimitive())
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
String internalName = type.getInternalName().replace('/', '.');
|
||||
if (!internalName.startsWith(API_PACKAGE_BASE))
|
||||
{
|
||||
return type; // not an rs api type
|
||||
}
|
||||
|
||||
return Type.getType("L" + type.getInternalName().substring(API_PACKAGE_BASE.length()) + ";", type.getDimensions());
|
||||
}
|
||||
|
||||
ClassFile findVanillaForInterface(java.lang.Class<?> clazz)
|
||||
{
|
||||
String className = clazz.getName().replace('.', '/');
|
||||
for (ClassFile cf : getVanilla().getClasses())
|
||||
{
|
||||
for (net.runelite.asm.pool.Class cl : cf.getInterfaces().getInterfaces())
|
||||
{
|
||||
if (cl.getName().equals(className))
|
||||
{
|
||||
return cf;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean validateTypeIsConvertibleTo(Type from, Type to) throws InjectionException
|
||||
{
|
||||
if (from.getDimensions() != to.getDimensions())
|
||||
{
|
||||
throw new InjectionException("Array dimension mismatch");
|
||||
}
|
||||
|
||||
if (from.isPrimitive())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
ClassFile vanillaClass = vanilla.findClass(from.getInternalName());
|
||||
if (vanillaClass == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean okay = false;
|
||||
for (Class inter : vanillaClass.getInterfaces().getInterfaces())
|
||||
{
|
||||
java.lang.Class c;
|
||||
|
||||
try
|
||||
{
|
||||
c = java.lang.Class.forName(inter.getName().replace('/', '.'));
|
||||
}
|
||||
catch (ClassNotFoundException ex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
okay |= check(c, to);
|
||||
}
|
||||
|
||||
return okay;
|
||||
}
|
||||
|
||||
private boolean check(java.lang.Class c, Type type)
|
||||
{
|
||||
String s = type.getInternalName()
|
||||
.replace('/', '.');
|
||||
|
||||
if (c.getName().equals(s))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (java.lang.Class c2 : c.getInterfaces())
|
||||
{
|
||||
if (check(c2, type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public final ClassGroup getDeobfuscated()
|
||||
{
|
||||
return deobfuscated;
|
||||
}
|
||||
|
||||
public final ClassGroup getVanilla()
|
||||
{
|
||||
return vanilla;
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.injector;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.ClassGroup;
|
||||
import net.runelite.asm.Method;
|
||||
import net.runelite.asm.Type;
|
||||
import net.runelite.asm.attributes.Code;
|
||||
import net.runelite.asm.attributes.code.Instruction;
|
||||
import net.runelite.asm.attributes.code.Instructions;
|
||||
import net.runelite.asm.attributes.code.instructions.CheckCast;
|
||||
import net.runelite.asm.attributes.code.instructions.Dup;
|
||||
import net.runelite.asm.attributes.code.instructions.InvokeSpecial;
|
||||
import net.runelite.asm.attributes.code.instructions.New;
|
||||
import net.runelite.asm.attributes.code.instructions.Return;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import net.runelite.deob.DeobAnnotations;
|
||||
import net.runelite.mapping.Construct;
|
||||
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class InjectConstruct
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(InjectConstruct.class);
|
||||
|
||||
private final Inject inject;
|
||||
|
||||
InjectConstruct(Inject inject)
|
||||
{
|
||||
this.inject = inject;
|
||||
}
|
||||
|
||||
public void inject(Map<ClassFile, java.lang.Class> implemented) throws InjectionException
|
||||
{
|
||||
for (Entry<ClassFile, java.lang.Class> entry : implemented.entrySet())
|
||||
{
|
||||
Class<?> clazz = entry.getValue();
|
||||
ClassFile cf = entry.getKey();
|
||||
|
||||
if (clazz == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (java.lang.reflect.Method method : clazz.getDeclaredMethods())
|
||||
{
|
||||
if (method.isSynthetic())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Construct construct = method.getAnnotation(Construct.class);
|
||||
if (construct == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
String obfuscatedName = DeobAnnotations.getObfuscatedName(cf.getAnnotations());
|
||||
if (obfuscatedName == null)
|
||||
{
|
||||
obfuscatedName = cf.getName();
|
||||
}
|
||||
|
||||
ClassGroup vanilla = inject.getVanilla();
|
||||
ClassFile other = vanilla.findClass(obfuscatedName);
|
||||
assert other != null : "unable to find vanilla class from obfuscated name: " + obfuscatedName;
|
||||
|
||||
injectConstruct(other, method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void injectConstruct(ClassFile targetClass, java.lang.reflect.Method apiMethod) throws InjectionException
|
||||
{
|
||||
logger.info("Injecting construct for {}", apiMethod);
|
||||
|
||||
assert targetClass.findMethod(apiMethod.getName()) == null;
|
||||
|
||||
Class<?> typeToConstruct = apiMethod.getReturnType();
|
||||
ClassFile vanillaClass = inject.findVanillaForInterface(typeToConstruct);
|
||||
if (vanillaClass == null)
|
||||
{
|
||||
throw new InjectionException("Unable to find vanilla class which implements interface " + typeToConstruct);
|
||||
}
|
||||
|
||||
Signature sig = inject.javaMethodToSignature(apiMethod);
|
||||
|
||||
Signature constructorSig = new Signature.Builder()
|
||||
.addArguments(Stream.of(apiMethod.getParameterTypes())
|
||||
.map(arg ->
|
||||
{
|
||||
ClassFile vanilla = inject.findVanillaForInterface(arg);
|
||||
if (vanilla != null)
|
||||
{
|
||||
return new Type("L" + vanilla.getName() + ";");
|
||||
}
|
||||
return Inject.classToType(arg);
|
||||
})
|
||||
.collect(Collectors.toList()))
|
||||
.setReturnType(Type.VOID)
|
||||
.build();
|
||||
Method vanillaConstructor = vanillaClass.findMethod("<init>", constructorSig);
|
||||
if (vanillaConstructor == null)
|
||||
{
|
||||
throw new InjectionException("Unable to find constructor for " + vanillaClass.getName() + ".<init>" + constructorSig);
|
||||
}
|
||||
|
||||
Method setterMethod = new Method(targetClass, apiMethod.getName(), sig);
|
||||
setterMethod.setAccessFlags(ACC_PUBLIC);
|
||||
targetClass.addMethod(setterMethod);
|
||||
|
||||
Code code = new Code(setterMethod);
|
||||
setterMethod.setCode(code);
|
||||
|
||||
Instructions instructions = code.getInstructions();
|
||||
List<Instruction> ins = instructions.getInstructions();
|
||||
|
||||
ins.add(new New(instructions, vanillaClass.getPoolClass()));
|
||||
ins.add(new Dup(instructions));
|
||||
int idx = 1;
|
||||
int parameter = 0;
|
||||
for (Type type : vanillaConstructor.getDescriptor().getArguments())
|
||||
{
|
||||
Instruction load = inject.createLoadForTypeIndex(instructions, type, idx);
|
||||
idx += type.getSize();
|
||||
ins.add(load);
|
||||
|
||||
Type paramType = sig.getTypeOfArg(parameter);
|
||||
if (!type.equals(paramType))
|
||||
{
|
||||
CheckCast checkCast = new CheckCast(instructions);
|
||||
checkCast.setType(type);
|
||||
ins.add(checkCast);
|
||||
}
|
||||
|
||||
++parameter;
|
||||
}
|
||||
ins.add(new InvokeSpecial(instructions, vanillaConstructor.getPoolMethod()));
|
||||
ins.add(new Return(instructions));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.injector;
|
||||
|
||||
import java.util.List;
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.Field;
|
||||
import net.runelite.asm.Method;
|
||||
import net.runelite.asm.attributes.Code;
|
||||
import net.runelite.asm.attributes.code.Instruction;
|
||||
import net.runelite.asm.attributes.code.InstructionType;
|
||||
import net.runelite.asm.attributes.code.Instructions;
|
||||
import net.runelite.asm.attributes.code.instructions.ALoad;
|
||||
import net.runelite.asm.attributes.code.instructions.GetField;
|
||||
import net.runelite.asm.attributes.code.instructions.GetStatic;
|
||||
import net.runelite.asm.attributes.code.instructions.IMul;
|
||||
import net.runelite.asm.attributes.code.instructions.LDC;
|
||||
import net.runelite.asm.attributes.code.instructions.LMul;
|
||||
import net.runelite.asm.attributes.code.instructions.Return;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class InjectGetter
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(InjectGetter.class);
|
||||
|
||||
private final Inject inject;
|
||||
|
||||
private int injectedGetters;
|
||||
|
||||
InjectGetter(Inject inject)
|
||||
{
|
||||
this.inject = inject;
|
||||
}
|
||||
|
||||
void injectGetter(ClassFile clazz, java.lang.reflect.Method method, Field field, Number getter)
|
||||
{
|
||||
// clazz = class file we're injecting the method into.
|
||||
// method = api method (java reflect) that we're overriding
|
||||
// field = field we're getting. might not be in this class if static.
|
||||
// getter = encryption getter
|
||||
|
||||
assert clazz.findMethod(method.getName()) == null;
|
||||
assert field.isStatic() || field.getClassFile() == clazz;
|
||||
|
||||
Signature sig = new Signature.Builder()
|
||||
.setReturnType(Inject.classToType(method.getReturnType()))
|
||||
.build();
|
||||
Method getterMethod = new Method(clazz, method.getName(), sig);
|
||||
getterMethod.setAccessFlags(ACC_PUBLIC);
|
||||
|
||||
// create code
|
||||
Code code = new Code(getterMethod);
|
||||
getterMethod.setCode(code);
|
||||
|
||||
Instructions instructions = code.getInstructions();
|
||||
List<Instruction> ins = instructions.getInstructions();
|
||||
|
||||
if (field.isStatic())
|
||||
{
|
||||
code.setMaxStack(1);
|
||||
|
||||
ins.add(new GetStatic(instructions, field.getPoolField()));
|
||||
}
|
||||
else
|
||||
{
|
||||
code.setMaxStack(2);
|
||||
|
||||
ins.add(new ALoad(instructions, 0));
|
||||
ins.add(new GetField(instructions, field.getPoolField()));
|
||||
}
|
||||
|
||||
if (getter != null)
|
||||
{
|
||||
code.setMaxStack(2);
|
||||
|
||||
assert getter instanceof Integer || getter instanceof Long;
|
||||
|
||||
if (getter instanceof Integer)
|
||||
{
|
||||
ins.add(new LDC(instructions, (int) getter));
|
||||
ins.add(new IMul(instructions));
|
||||
}
|
||||
else
|
||||
{
|
||||
ins.add(new LDC(instructions, (long) getter));
|
||||
ins.add(new LMul(instructions));
|
||||
}
|
||||
}
|
||||
|
||||
InstructionType returnType;
|
||||
if (field.getType().isPrimitive() && field.getType().getDimensions() == 0)
|
||||
{
|
||||
switch (field.getType().toString())
|
||||
{
|
||||
case "B":
|
||||
case "C":
|
||||
case "I":
|
||||
case "S":
|
||||
case "Z":
|
||||
returnType = InstructionType.IRETURN;
|
||||
break;
|
||||
case "D":
|
||||
returnType = InstructionType.DRETURN;
|
||||
break;
|
||||
case "F":
|
||||
returnType = InstructionType.FRETURN;
|
||||
break;
|
||||
case "J":
|
||||
returnType = InstructionType.LRETURN;
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unknown type");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
returnType = InstructionType.ARETURN;
|
||||
}
|
||||
|
||||
ins.add(new Return(instructions, returnType));
|
||||
|
||||
clazz.addMethod(getterMethod);
|
||||
++injectedGetters;
|
||||
}
|
||||
|
||||
int getInjectedGetters()
|
||||
{
|
||||
return injectedGetters;
|
||||
}
|
||||
}
|
||||
@@ -1,396 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.injector;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import net.runelite.asm.Field;
|
||||
import net.runelite.asm.Method;
|
||||
import net.runelite.asm.Type;
|
||||
import net.runelite.asm.attributes.Code;
|
||||
import net.runelite.asm.attributes.code.Instruction;
|
||||
import net.runelite.asm.attributes.code.InstructionType;
|
||||
import net.runelite.asm.attributes.code.Instructions;
|
||||
import net.runelite.asm.attributes.code.instruction.types.DupInstruction;
|
||||
import net.runelite.asm.attributes.code.instruction.types.SetFieldInstruction;
|
||||
import net.runelite.asm.attributes.code.instructions.ArrayStore;
|
||||
import net.runelite.asm.attributes.code.instructions.CheckCast;
|
||||
import net.runelite.asm.attributes.code.instructions.Dup;
|
||||
import net.runelite.asm.attributes.code.instructions.IMul;
|
||||
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
|
||||
import net.runelite.asm.attributes.code.instructions.InvokeVirtual;
|
||||
import net.runelite.asm.attributes.code.instructions.LDC;
|
||||
import net.runelite.asm.attributes.code.instructions.LMul;
|
||||
import net.runelite.asm.attributes.code.instructions.PutField;
|
||||
import net.runelite.asm.attributes.code.instructions.Swap;
|
||||
import net.runelite.asm.execution.Execution;
|
||||
import net.runelite.asm.execution.InstructionContext;
|
||||
import net.runelite.asm.execution.StackContext;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class InjectHook
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(InjectHook.class);
|
||||
private static final String HOOK_METHOD_SIGNATURE = "(I)V";
|
||||
private static final String CLINIT = "<clinit>";
|
||||
private final Inject inject;
|
||||
private final Map<Field, HookInfo> hooked = new HashMap<>();
|
||||
private int injectedHooks;
|
||||
|
||||
InjectHook(Inject inject)
|
||||
{
|
||||
this.inject = inject;
|
||||
}
|
||||
|
||||
void hook(Field field, HookInfo hookInfo)
|
||||
{
|
||||
hooked.put(field, hookInfo);
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
Execution e = new Execution(inject.getVanilla());
|
||||
e.populateInitialMethods();
|
||||
|
||||
Set<Instruction> done = new HashSet<>();
|
||||
Set<Instruction> doneIh = new HashSet<>();
|
||||
|
||||
e.addExecutionVisitor((InstructionContext ic) ->
|
||||
{
|
||||
Instruction i = ic.getInstruction();
|
||||
Instructions ins = i.getInstructions();
|
||||
Code code = ins.getCode();
|
||||
Method method = code.getMethod();
|
||||
|
||||
if (method.getName().equals(CLINIT))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(i instanceof SetFieldInstruction))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!done.add(i))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetFieldInstruction sfi = (SetFieldInstruction) i;
|
||||
Field fieldBeingSet = sfi.getMyField();
|
||||
|
||||
if (fieldBeingSet == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HookInfo hookInfo = hooked.get(fieldBeingSet);
|
||||
if (hookInfo == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
String hookName = hookInfo.fieldName;
|
||||
assert hookName != null;
|
||||
|
||||
logger.trace("Found injection location for hook {} at instruction {}", hookName, sfi);
|
||||
++injectedHooks;
|
||||
|
||||
StackContext value = ic.getPops().get(0);
|
||||
|
||||
StackContext objectStackContext = null;
|
||||
if (sfi instanceof PutField)
|
||||
{
|
||||
objectStackContext = ic.getPops().get(1);
|
||||
}
|
||||
|
||||
int idx = ins.getInstructions().indexOf(sfi);
|
||||
assert idx != -1;
|
||||
|
||||
try
|
||||
{
|
||||
if (hookInfo.before)
|
||||
{
|
||||
injectCallbackBefore(ins, idx, hookInfo, null, objectStackContext, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// idx + 1 to insert after the set
|
||||
injectCallback(ins, idx + 1, hookInfo, null, objectStackContext);
|
||||
}
|
||||
}
|
||||
catch (InjectionException ex)
|
||||
{
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
});
|
||||
|
||||
// these look like:
|
||||
// getfield
|
||||
// iload_0
|
||||
// iconst_0
|
||||
// iastore
|
||||
e.addExecutionVisitor((InstructionContext ic) ->
|
||||
{
|
||||
Instruction i = ic.getInstruction();
|
||||
Instructions ins = i.getInstructions();
|
||||
Code code = ins.getCode();
|
||||
Method method = code.getMethod();
|
||||
|
||||
if (method.getName().equals(CLINIT))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(i instanceof ArrayStore))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!doneIh.add(i))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayStore as = (ArrayStore) i;
|
||||
|
||||
Field fieldBeingSet = as.getMyField(ic);
|
||||
if (fieldBeingSet == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HookInfo hookInfo = hooked.get(fieldBeingSet);
|
||||
if (hookInfo == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
String hookName = hookInfo.fieldName;
|
||||
|
||||
StackContext value = ic.getPops().get(0);
|
||||
StackContext index = ic.getPops().get(1);
|
||||
|
||||
StackContext arrayReference = ic.getPops().get(2);
|
||||
InstructionContext arrayReferencePushed = arrayReference.getPushed();
|
||||
|
||||
StackContext objectStackContext = null;
|
||||
if (arrayReferencePushed.getInstruction().getType() == InstructionType.GETFIELD)
|
||||
{
|
||||
objectStackContext = arrayReferencePushed.getPops().get(0);
|
||||
}
|
||||
|
||||
// inject hook after 'i'
|
||||
logger.info("Found array injection location for hook {} at instruction {}", hookName, i);
|
||||
++injectedHooks;
|
||||
|
||||
int idx = ins.getInstructions().indexOf(i);
|
||||
assert idx != -1;
|
||||
|
||||
try
|
||||
{
|
||||
if (hookInfo.before)
|
||||
{
|
||||
injectCallbackBefore(ins, idx, hookInfo, index, objectStackContext, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
injectCallback(ins, idx + 1, hookInfo, index, objectStackContext);
|
||||
}
|
||||
}
|
||||
catch (InjectionException ex)
|
||||
{
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
});
|
||||
|
||||
e.run();
|
||||
}
|
||||
|
||||
private void injectCallbackBefore(Instructions ins, int idx, HookInfo hookInfo, StackContext index, StackContext object, StackContext value) throws InjectionException
|
||||
{
|
||||
Signature signature = hookInfo.method.getDescriptor();
|
||||
Type methodArgumentType = signature.getTypeOfArg(0);
|
||||
|
||||
if (!hookInfo.method.isStatic())
|
||||
{
|
||||
if (object == null)
|
||||
{
|
||||
throw new InjectionException("null object");
|
||||
}
|
||||
|
||||
ins.getInstructions().add(idx++, new Dup(ins)); // dup value
|
||||
idx = recursivelyPush(ins, idx, object);
|
||||
ins.getInstructions().add(idx++, new Swap(ins));
|
||||
if (hookInfo.getter != null)
|
||||
{
|
||||
assert hookInfo.getter instanceof Integer || hookInfo.getter instanceof Long;
|
||||
|
||||
if (hookInfo.getter instanceof Integer)
|
||||
{
|
||||
ins.getInstructions().add(idx++, new LDC(ins, (int) hookInfo.getter));
|
||||
ins.getInstructions().add(idx++, new IMul(ins));
|
||||
}
|
||||
else
|
||||
{
|
||||
ins.getInstructions().add(idx++, new LDC(ins, (long) hookInfo.getter));
|
||||
ins.getInstructions().add(idx++, new LMul(ins));
|
||||
}
|
||||
}
|
||||
if (!value.type.equals(methodArgumentType))
|
||||
{
|
||||
CheckCast checkCast = new CheckCast(ins);
|
||||
checkCast.setType(methodArgumentType);
|
||||
ins.getInstructions().add(idx++, checkCast);
|
||||
}
|
||||
if (index != null)
|
||||
{
|
||||
idx = recursivelyPush(ins, idx, index);
|
||||
}
|
||||
|
||||
InvokeVirtual invoke = new InvokeVirtual(ins,
|
||||
new net.runelite.asm.pool.Method(
|
||||
new net.runelite.asm.pool.Class(hookInfo.clazz),
|
||||
hookInfo.method.getName(),
|
||||
signature
|
||||
)
|
||||
);
|
||||
ins.getInstructions().add(idx++, invoke);
|
||||
}
|
||||
else
|
||||
{
|
||||
ins.getInstructions().add(idx++, new Dup(ins)); // dup value
|
||||
if (!value.type.equals(methodArgumentType))
|
||||
{
|
||||
CheckCast checkCast = new CheckCast(ins);
|
||||
checkCast.setType(methodArgumentType);
|
||||
ins.getInstructions().add(idx++, checkCast);
|
||||
}
|
||||
if (index != null)
|
||||
{
|
||||
idx = recursivelyPush(ins, idx, index);
|
||||
}
|
||||
|
||||
InvokeStatic invoke = new InvokeStatic(ins,
|
||||
new net.runelite.asm.pool.Method(
|
||||
new net.runelite.asm.pool.Class(hookInfo.clazz),
|
||||
hookInfo.method.getName(),
|
||||
signature
|
||||
)
|
||||
);
|
||||
ins.getInstructions().add(idx++, invoke);
|
||||
}
|
||||
}
|
||||
|
||||
private int recursivelyPush(Instructions ins, int idx, StackContext sctx)
|
||||
{
|
||||
InstructionContext ctx = sctx.getPushed();
|
||||
if (ctx.getInstruction() instanceof DupInstruction)
|
||||
{
|
||||
DupInstruction dupInstruction = (DupInstruction) ctx.getInstruction();
|
||||
sctx = dupInstruction.getOriginal(sctx);
|
||||
ctx = sctx.getPushed();
|
||||
}
|
||||
|
||||
for (StackContext s : Lists.reverse(ctx.getPops()))
|
||||
{
|
||||
idx = recursivelyPush(ins, idx, s);
|
||||
}
|
||||
|
||||
ins.getInstructions().add(idx++, ctx.getInstruction().clone());
|
||||
return idx;
|
||||
}
|
||||
|
||||
private void injectCallback(Instructions ins, int idx, HookInfo hookInfo, StackContext index, StackContext objectPusher) throws InjectionException
|
||||
{
|
||||
if (!hookInfo.method.isStatic())
|
||||
{
|
||||
if (objectPusher == null)
|
||||
{
|
||||
throw new InjectionException("Null object pusher");
|
||||
}
|
||||
|
||||
idx = recursivelyPush(ins, idx, objectPusher);
|
||||
if (index != null)
|
||||
{
|
||||
idx = recursivelyPush(ins, idx, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
ins.getInstructions().add(idx++, new LDC(ins, -1));
|
||||
}
|
||||
|
||||
InvokeVirtual invoke = new InvokeVirtual(ins,
|
||||
new net.runelite.asm.pool.Method(
|
||||
new net.runelite.asm.pool.Class(hookInfo.clazz),
|
||||
hookInfo.method.getName(),
|
||||
new Signature(HOOK_METHOD_SIGNATURE)
|
||||
)
|
||||
);
|
||||
ins.getInstructions().add(idx++, invoke);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index != null)
|
||||
{
|
||||
idx = recursivelyPush(ins, idx, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
ins.getInstructions().add(idx++, new LDC(ins, -1));
|
||||
}
|
||||
|
||||
InvokeStatic invoke = new InvokeStatic(ins,
|
||||
new net.runelite.asm.pool.Method(
|
||||
new net.runelite.asm.pool.Class(hookInfo.clazz),
|
||||
hookInfo.method.getName(),
|
||||
new Signature(HOOK_METHOD_SIGNATURE)
|
||||
)
|
||||
);
|
||||
ins.getInstructions().add(idx++, invoke);
|
||||
}
|
||||
}
|
||||
|
||||
int getInjectedHooks()
|
||||
{
|
||||
return injectedHooks;
|
||||
}
|
||||
|
||||
static class HookInfo
|
||||
{
|
||||
String fieldName;
|
||||
String clazz;
|
||||
Method method;
|
||||
boolean before;
|
||||
Number getter;
|
||||
}
|
||||
}
|
||||
@@ -1,255 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.injector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.ClassGroup;
|
||||
import net.runelite.asm.Method;
|
||||
import net.runelite.asm.Type;
|
||||
import net.runelite.asm.attributes.Annotations;
|
||||
import net.runelite.asm.attributes.Code;
|
||||
import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.asm.attributes.code.Instruction;
|
||||
import net.runelite.asm.attributes.code.InstructionType;
|
||||
import net.runelite.asm.attributes.code.Instructions;
|
||||
import net.runelite.asm.attributes.code.instruction.types.InvokeInstruction;
|
||||
import net.runelite.asm.attributes.code.instruction.types.ReturnInstruction;
|
||||
import net.runelite.asm.attributes.code.instructions.ALoad;
|
||||
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
|
||||
import net.runelite.asm.attributes.code.instructions.InvokeVirtual;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import net.runelite.deob.DeobAnnotations;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class InjectHookMethod
|
||||
{
|
||||
public static final String HOOKS = "net/runelite/client/callback/Hooks";
|
||||
private static final Logger logger = LoggerFactory.getLogger(InjectHookMethod.class);
|
||||
private final Inject inject;
|
||||
|
||||
InjectHookMethod(Inject inject)
|
||||
{
|
||||
this.inject = inject;
|
||||
}
|
||||
|
||||
void process(Method method) throws InjectionException
|
||||
{
|
||||
Annotations an = method.getAnnotations();
|
||||
if (an == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Annotation a = an.find(DeobAnnotations.HOOK);
|
||||
if (a == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
String hookName = a.getElement().getString();
|
||||
boolean end = a.getElements().size() == 2 && a.getElements().get(1).getValue().equals(true);
|
||||
|
||||
inject(null, method, hookName, end, true);
|
||||
}
|
||||
|
||||
public void inject(Method hookMethod, Method method, String name, boolean end, boolean useHooks) throws InjectionException
|
||||
{
|
||||
Annotations an = method.getAnnotations();
|
||||
|
||||
// Method is hooked
|
||||
// Find equivalent method in vanilla, and insert callback at the beginning
|
||||
ClassFile cf = method.getClassFile();
|
||||
String obfuscatedMethodName = DeobAnnotations.getObfuscatedName(an),
|
||||
obfuscatedClassName = DeobAnnotations.getObfuscatedName(cf.getAnnotations());
|
||||
|
||||
// might be a constructor
|
||||
if (obfuscatedMethodName == null)
|
||||
{
|
||||
obfuscatedMethodName = method.getName();
|
||||
}
|
||||
|
||||
assert obfuscatedClassName != null : "hook on method in class with no obfuscated name";
|
||||
assert obfuscatedMethodName != null : "hook on method with no obfuscated name";
|
||||
|
||||
Signature obfuscatedSignature = inject.getMethodSignature(method);
|
||||
|
||||
ClassGroup vanilla = inject.getVanilla();
|
||||
ClassFile vanillaClass = vanilla.findClass(obfuscatedClassName);
|
||||
Method vanillaMethod = vanillaClass.findMethod(obfuscatedMethodName, obfuscatedSignature);
|
||||
assert method.isStatic() == vanillaMethod.isStatic();
|
||||
|
||||
// Insert instructions at beginning of method
|
||||
injectHookMethod(hookMethod, name, end, method, vanillaMethod, useHooks);
|
||||
}
|
||||
|
||||
private void injectHookMethod(Method hookMethod, String hookName, boolean end, Method deobMethod, Method vanillaMethod, boolean useHooks) throws InjectionException
|
||||
{
|
||||
Code code = vanillaMethod.getCode();
|
||||
if (code == null)
|
||||
{
|
||||
logger.warn(vanillaMethod + " code is null");
|
||||
}
|
||||
Instructions instructions = code.getInstructions();
|
||||
|
||||
Signature.Builder builder = new Signature.Builder()
|
||||
.setReturnType(Type.VOID); // Hooks always return void
|
||||
|
||||
for (Type type : deobMethod.getDescriptor().getArguments())
|
||||
{
|
||||
builder.addArgument(inject.deobfuscatedTypeToApiType(type));
|
||||
}
|
||||
|
||||
assert deobMethod.isStatic() == vanillaMethod.isStatic();
|
||||
|
||||
boolean modifiedSignature = false;
|
||||
if (!deobMethod.isStatic() && useHooks)
|
||||
{
|
||||
// Add variable to signature
|
||||
builder.addArgument(0, inject.deobfuscatedTypeToApiType(new Type(deobMethod.getClassFile().getName())));
|
||||
modifiedSignature = true;
|
||||
}
|
||||
|
||||
Signature signature = builder.build();
|
||||
|
||||
List<Integer> insertIndexes = findHookLocations(hookName, end, vanillaMethod);
|
||||
insertIndexes.sort((a, b) -> Integer.compare(b, a));
|
||||
|
||||
for (int insertPos : insertIndexes)
|
||||
{
|
||||
if (!deobMethod.isStatic())
|
||||
{
|
||||
instructions.addInstruction(insertPos++, new ALoad(instructions, 0));
|
||||
}
|
||||
|
||||
int signatureStart = modifiedSignature ? 1 : 0;
|
||||
int index = deobMethod.isStatic() ? 0 : 1; // current variable index
|
||||
|
||||
for (int i = signatureStart; i < signature.size(); ++i)
|
||||
{
|
||||
Type type = signature.getTypeOfArg(i);
|
||||
|
||||
Instruction load = inject.createLoadForTypeIndex(instructions, type, index);
|
||||
instructions.addInstruction(insertPos++, load);
|
||||
|
||||
index += type.getSize();
|
||||
}
|
||||
|
||||
InvokeInstruction invoke;
|
||||
|
||||
// use old Hooks callback
|
||||
if (useHooks)
|
||||
{
|
||||
// Invoke callback
|
||||
invoke = new InvokeStatic(instructions,
|
||||
new net.runelite.asm.pool.Method(
|
||||
new net.runelite.asm.pool.Class(HOOKS),
|
||||
hookName,
|
||||
signature
|
||||
)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invoke methodhook
|
||||
assert hookMethod != null;
|
||||
|
||||
if (vanillaMethod.isStatic())
|
||||
{
|
||||
invoke = new InvokeStatic(instructions,
|
||||
new net.runelite.asm.pool.Method(
|
||||
new net.runelite.asm.pool.Class("client"), // Static methods are in client
|
||||
hookMethod.getName(),
|
||||
signature
|
||||
)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise invoke member function
|
||||
//instructions.addInstruction(insertPos++, new ALoad(instructions, 0));
|
||||
invoke = new InvokeVirtual(instructions,
|
||||
new net.runelite.asm.pool.Method(
|
||||
new net.runelite.asm.pool.Class(vanillaMethod.getClassFile().getName()),
|
||||
hookMethod.getName(),
|
||||
hookMethod.getDescriptor()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
instructions.addInstruction(insertPos++, (Instruction) invoke);
|
||||
}
|
||||
|
||||
logger.info("Injected method hook {} in {} with {} args: {}",
|
||||
hookName, vanillaMethod, signature.size(),
|
||||
signature.getArguments());
|
||||
}
|
||||
|
||||
private List<Integer> findHookLocations(String hookName, boolean end, Method vanillaMethod) throws InjectionException
|
||||
{
|
||||
Instructions instructions = vanillaMethod.getCode().getInstructions();
|
||||
|
||||
if (end)
|
||||
{
|
||||
// find return
|
||||
List<Instruction> returns = instructions.getInstructions().stream()
|
||||
.filter(i -> i instanceof ReturnInstruction)
|
||||
.collect(Collectors.toList());
|
||||
List<Integer> indexes = new ArrayList<>();
|
||||
|
||||
for (Instruction ret : returns)
|
||||
{
|
||||
int idx = instructions.getInstructions().indexOf(ret);
|
||||
assert idx != -1;
|
||||
indexes.add(idx);
|
||||
}
|
||||
|
||||
return indexes;
|
||||
}
|
||||
|
||||
if (!vanillaMethod.getName().equals("<init>"))
|
||||
{
|
||||
return Arrays.asList(0);
|
||||
}
|
||||
|
||||
// Find index after invokespecial
|
||||
for (int i = 0; i < instructions.getInstructions().size(); ++i)
|
||||
{
|
||||
Instruction in = instructions.getInstructions().get(i);
|
||||
|
||||
if (in.getType() == InstructionType.INVOKESPECIAL)
|
||||
{
|
||||
return Arrays.asList(i + 1); // one after
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("constructor with no invokespecial");
|
||||
}
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.injector;
|
||||
|
||||
import java.util.List;
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.ClassGroup;
|
||||
import net.runelite.asm.Method;
|
||||
import net.runelite.asm.Type;
|
||||
import net.runelite.asm.attributes.Annotations;
|
||||
import net.runelite.asm.attributes.Code;
|
||||
import net.runelite.asm.attributes.code.Instruction;
|
||||
import net.runelite.asm.attributes.code.InstructionType;
|
||||
import net.runelite.asm.attributes.code.Instructions;
|
||||
import net.runelite.asm.attributes.code.instructions.ALoad;
|
||||
import net.runelite.asm.attributes.code.instructions.BiPush;
|
||||
import net.runelite.asm.attributes.code.instructions.CheckCast;
|
||||
import net.runelite.asm.attributes.code.instructions.DLoad;
|
||||
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
|
||||
import net.runelite.asm.attributes.code.instructions.InvokeVirtual;
|
||||
import net.runelite.asm.attributes.code.instructions.LDC;
|
||||
import net.runelite.asm.attributes.code.instructions.LLoad;
|
||||
import net.runelite.asm.attributes.code.instructions.Return;
|
||||
import net.runelite.asm.attributes.code.instructions.SiPush;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import net.runelite.deob.DeobAnnotations;
|
||||
import static net.runelite.deob.DeobAnnotations.EXPORT;
|
||||
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class InjectInvoker
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(InjectInvoker.class);
|
||||
|
||||
private final Inject inject;
|
||||
|
||||
private int injectedInvokers;
|
||||
|
||||
InjectInvoker(Inject inject)
|
||||
{
|
||||
this.inject = inject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject an invoker for a method
|
||||
*
|
||||
* @param m Method in the deobfuscated client to inject an invoker for
|
||||
* @param other Class in the vanilla client of the same class m is a
|
||||
* member of
|
||||
* @param implementingClass Java class for the API interface the class
|
||||
* will implement
|
||||
*/
|
||||
void process(Method m, ClassFile other, java.lang.Class<?> implementingClass)
|
||||
{
|
||||
Annotations an = m.getAnnotations();
|
||||
|
||||
if (an == null || an.find(EXPORT) == null)
|
||||
{
|
||||
return; // not an exported method
|
||||
}
|
||||
|
||||
String exportedName = DeobAnnotations.getExportedName(an);
|
||||
String obfuscatedName = DeobAnnotations.getObfuscatedName(an);
|
||||
|
||||
if (obfuscatedName == null)
|
||||
{
|
||||
obfuscatedName = m.getName();
|
||||
}
|
||||
|
||||
String garbage = DeobAnnotations.getDecoder(m);
|
||||
Method otherm = other.findMethod(obfuscatedName, inject.getMethodSignature(m));
|
||||
|
||||
assert otherm != null;
|
||||
assert m.isStatic() == otherm.isStatic();
|
||||
|
||||
ClassGroup vanilla = inject.getVanilla();
|
||||
|
||||
ClassFile targetClass = m.isStatic() ? vanilla.findClass("client") : other;
|
||||
|
||||
// Place into implementing class, unless the method is static
|
||||
java.lang.Class<?> targetClassJava = m.isStatic() ? Inject.CLIENT_CLASS : implementingClass;
|
||||
|
||||
if (targetClassJava == null)
|
||||
{
|
||||
assert !m.isStatic();
|
||||
|
||||
// non static exported method on non exported interface, weird.
|
||||
// logger.debug("Non static exported method {} on non exported interface", exportedName);
|
||||
return;
|
||||
}
|
||||
|
||||
java.lang.reflect.Method apiMethod = inject.findImportMethodOnApi(targetClassJava, exportedName, null); // api method to invoke 'otherm'
|
||||
if (apiMethod == null)
|
||||
{
|
||||
// logger.debug("Unable to find api method on {} with imported name {}, not injecting invoker", targetClassJava, exportedName);
|
||||
return;
|
||||
}
|
||||
|
||||
injectInvoker(targetClass, apiMethod, m, otherm, garbage);
|
||||
++injectedInvokers;
|
||||
}
|
||||
|
||||
private void injectInvoker(ClassFile clazz, java.lang.reflect.Method method, Method deobfuscatedMethod, Method invokeMethod, String garbage)
|
||||
{
|
||||
// clazz = clazz to add invoker to
|
||||
// method = api method to override
|
||||
// deobfuscatedMethod = deobfuscated method, used to get the deobfuscated signature
|
||||
// invokeMethod = method to invoke, obfuscated
|
||||
|
||||
if (clazz.findMethod(method.getName(), deobfuscatedMethod.getDescriptor()) != null)
|
||||
{
|
||||
logger.warn("Not injecting method {} because it already exists!", method);
|
||||
return; // this can happen from exporting a field and method with the same name
|
||||
}
|
||||
|
||||
assert invokeMethod.isStatic() == deobfuscatedMethod.isStatic();
|
||||
assert invokeMethod.isStatic() || invokeMethod.getClassFile() == clazz;
|
||||
|
||||
Type lastGarbageArgumentType = null;
|
||||
|
||||
if (deobfuscatedMethod.getDescriptor().getArguments().size() != invokeMethod.getDescriptor().getArguments().size())
|
||||
{
|
||||
// allow for obfuscated method to have a single bogus signature at the end
|
||||
assert deobfuscatedMethod.getDescriptor().size() + 1 == invokeMethod.getDescriptor().size();
|
||||
|
||||
List<Type> arguments = invokeMethod.getDescriptor().getArguments();
|
||||
lastGarbageArgumentType = arguments.get(arguments.size() - 1);
|
||||
}
|
||||
|
||||
// Injected method signature is always the same as the API
|
||||
Signature apiSignature = inject.javaMethodToSignature(method);
|
||||
Method invokerMethodSignature = new Method(clazz, method.getName(), apiSignature);
|
||||
invokerMethodSignature.setAccessFlags(ACC_PUBLIC);
|
||||
|
||||
// create code attribute
|
||||
Code code = new Code(invokerMethodSignature);
|
||||
invokerMethodSignature.setCode(code);
|
||||
|
||||
Instructions instructions = code.getInstructions();
|
||||
List<Instruction> ins = instructions.getInstructions();
|
||||
|
||||
code.setMaxStack(1 + invokeMethod.getDescriptor().size()); // this + arguments
|
||||
|
||||
// load function arguments onto the stack.
|
||||
int index = 0;
|
||||
if (!invokeMethod.isStatic())
|
||||
{
|
||||
ins.add(new ALoad(instructions, index++)); // this
|
||||
}
|
||||
else
|
||||
{
|
||||
++index; // this method is always non static
|
||||
}
|
||||
for (int i = 0; i < deobfuscatedMethod.getDescriptor().size(); ++i)
|
||||
{
|
||||
Type type = deobfuscatedMethod.getDescriptor().getTypeOfArg(i);
|
||||
|
||||
Instruction loadInstruction = inject.createLoadForTypeIndex(instructions, type, index);
|
||||
ins.add(loadInstruction);
|
||||
|
||||
Signature invokeDesc = invokeMethod.getDescriptor();
|
||||
Type obType = invokeDesc.getTypeOfArg(i);
|
||||
if (!type.equals(obType))
|
||||
{
|
||||
CheckCast checkCast = new CheckCast(instructions);
|
||||
checkCast.setType(obType);
|
||||
ins.add(checkCast);
|
||||
}
|
||||
|
||||
if (loadInstruction instanceof DLoad || loadInstruction instanceof LLoad)
|
||||
{
|
||||
index += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastGarbageArgumentType != null)
|
||||
{
|
||||
// function requires garbage value
|
||||
|
||||
// if garbage is null here it might just be an unused parameter, not part of the obfuscation
|
||||
if (garbage == null)
|
||||
{
|
||||
garbage = "0";
|
||||
}
|
||||
|
||||
switch (lastGarbageArgumentType.toString())
|
||||
{
|
||||
case "Z":
|
||||
case "B":
|
||||
case "C":
|
||||
ins.add(new BiPush(instructions, Byte.parseByte(garbage)));
|
||||
break;
|
||||
case "S":
|
||||
ins.add(new SiPush(instructions, Short.parseShort(garbage)));
|
||||
break;
|
||||
case "I":
|
||||
ins.add(new LDC(instructions, Integer.parseInt(garbage)));
|
||||
break;
|
||||
case "D":
|
||||
ins.add(new LDC(instructions, Double.parseDouble(garbage)));
|
||||
break;
|
||||
case "F":
|
||||
ins.add(new LDC(instructions, Float.parseFloat(garbage)));
|
||||
break;
|
||||
case "J":
|
||||
ins.add(new LDC(instructions, Long.parseLong(garbage)));
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unknown type");
|
||||
}
|
||||
}
|
||||
|
||||
if (invokeMethod.isStatic())
|
||||
{
|
||||
ins.add(new InvokeStatic(instructions, invokeMethod.getPoolMethod()));
|
||||
}
|
||||
else
|
||||
{
|
||||
ins.add(new InvokeVirtual(instructions, invokeMethod.getPoolMethod()));
|
||||
}
|
||||
|
||||
Type returnValue = invokeMethod.getDescriptor().getReturnValue();
|
||||
InstructionType returnType;
|
||||
|
||||
if (returnValue.isPrimitive() && returnValue.getDimensions() == 0)
|
||||
{
|
||||
switch (returnValue.toString())
|
||||
{
|
||||
case "Z":
|
||||
case "I":
|
||||
returnType = InstructionType.IRETURN;
|
||||
break;
|
||||
case "J":
|
||||
returnType = InstructionType.LRETURN;
|
||||
break;
|
||||
case "F":
|
||||
returnType = InstructionType.FRETURN;
|
||||
break;
|
||||
case "D":
|
||||
returnType = InstructionType.DRETURN;
|
||||
break;
|
||||
case "V":
|
||||
returnType = InstructionType.RETURN;
|
||||
break;
|
||||
default:
|
||||
assert false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
returnType = InstructionType.ARETURN;
|
||||
}
|
||||
|
||||
ins.add(new Return(instructions, returnType));
|
||||
|
||||
clazz.addMethod(invokerMethodSignature);
|
||||
}
|
||||
|
||||
int getInjectedInvokers()
|
||||
{
|
||||
return injectedInvokers;
|
||||
}
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.injector;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.ClassGroup;
|
||||
import net.runelite.deob.clientver.ClientVersion;
|
||||
import net.runelite.deob.util.JarUtil;
|
||||
import org.apache.maven.plugin.AbstractMojo;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugin.MojoFailureException;
|
||||
import org.apache.maven.plugin.logging.Log;
|
||||
import org.apache.maven.plugins.annotations.LifecyclePhase;
|
||||
import org.apache.maven.plugins.annotations.Mojo;
|
||||
import org.apache.maven.plugins.annotations.Parameter;
|
||||
|
||||
@Mojo(
|
||||
name = "runelite-injector",
|
||||
defaultPhase = LifecyclePhase.GENERATE_RESOURCES
|
||||
)
|
||||
public class InjectMojo extends AbstractMojo
|
||||
{
|
||||
private final Log log = getLog();
|
||||
@Parameter(defaultValue = "${project.build.outputDirectory}")
|
||||
private File outputDirectory;
|
||||
@Parameter(defaultValue = "./runescape-client/target/rs-client-${project.version}.jar", readonly = true, required = true)
|
||||
private String rsClientPath;
|
||||
@Parameter(defaultValue = "${net.runelite.rs:vanilla:jar}", readonly = true, required = true)
|
||||
private String vanillaPath;
|
||||
|
||||
@Override
|
||||
public void execute() throws MojoExecutionException, MojoFailureException
|
||||
{
|
||||
ClientVersion ver = new ClientVersion(new File(vanillaPath));
|
||||
int version;
|
||||
try
|
||||
{
|
||||
version = ver.getVersion();
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw new MojoExecutionException("Unable to read vanilla client version", ex);
|
||||
}
|
||||
|
||||
log.info("Vanilla client version " + version);
|
||||
|
||||
ClassGroup rs;
|
||||
ClassGroup vanilla;
|
||||
|
||||
try
|
||||
{
|
||||
rs = JarUtil.loadJar(new File(rsClientPath));
|
||||
vanilla = JarUtil.loadJar(new File(vanillaPath));
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw new MojoExecutionException("Unable to load dependency jars", ex);
|
||||
}
|
||||
|
||||
Injector injector = new Injector(rs, vanilla);
|
||||
try
|
||||
{
|
||||
injector.inject();
|
||||
}
|
||||
catch (InjectionException ex)
|
||||
{
|
||||
throw new MojoExecutionException("Error injecting client", ex);
|
||||
}
|
||||
|
||||
InjectorValidator iv = new InjectorValidator(vanilla);
|
||||
iv.validate();
|
||||
|
||||
if (iv.getError() > 0)
|
||||
{
|
||||
throw new MojoExecutionException("Error building injected jar");
|
||||
}
|
||||
|
||||
if (iv.getMissing() > 0)
|
||||
{
|
||||
throw new MojoExecutionException("Unable to inject all methods");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
writeClasses(vanilla, outputDirectory);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw new MojoExecutionException("Unable to write classes", ex);
|
||||
}
|
||||
|
||||
log.info("Injector wrote " + vanilla.getClasses().size() + " classes, " + iv.getOkay() + " injected methods");
|
||||
}
|
||||
|
||||
private void writeClasses(ClassGroup group, File outputDirectory) throws IOException
|
||||
{
|
||||
for (ClassFile cf : group.getClasses())
|
||||
{
|
||||
File classFile = getClassFile(outputDirectory, cf);
|
||||
byte[] classData = JarUtil.writeClass(group, cf);
|
||||
|
||||
try (FileOutputStream fout = new FileOutputStream(classFile, false))
|
||||
{
|
||||
fout.write(classData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private File getClassFile(File base, ClassFile cf)
|
||||
{
|
||||
File f = base;
|
||||
|
||||
String[] parts = cf.getName().split("/");
|
||||
for (int i = 0; i < parts.length - 1; ++i)
|
||||
{
|
||||
String part = parts[i];
|
||||
|
||||
f = new File(f, part);
|
||||
}
|
||||
|
||||
f.mkdirs();
|
||||
f = new File(f, parts[parts.length - 1] + ".class");
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.injector;
|
||||
|
||||
import java.util.List;
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.Field;
|
||||
import net.runelite.asm.Method;
|
||||
import net.runelite.asm.Type;
|
||||
import net.runelite.asm.attributes.Code;
|
||||
import net.runelite.asm.attributes.code.Instruction;
|
||||
import net.runelite.asm.attributes.code.Instructions;
|
||||
import net.runelite.asm.attributes.code.instructions.ALoad;
|
||||
import net.runelite.asm.attributes.code.instructions.CheckCast;
|
||||
import net.runelite.asm.attributes.code.instructions.IMul;
|
||||
import net.runelite.asm.attributes.code.instructions.LDC;
|
||||
import net.runelite.asm.attributes.code.instructions.LMul;
|
||||
import net.runelite.asm.attributes.code.instructions.PutField;
|
||||
import net.runelite.asm.attributes.code.instructions.PutStatic;
|
||||
import net.runelite.asm.attributes.code.instructions.VReturn;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class InjectSetter
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(InjectSetter.class);
|
||||
|
||||
private final Inject inject;
|
||||
|
||||
private int injectedSetters;
|
||||
|
||||
InjectSetter(Inject inject)
|
||||
{
|
||||
this.inject = inject;
|
||||
}
|
||||
|
||||
/**
|
||||
* inject a setter into the vanilla classgroup
|
||||
*
|
||||
* @param targetClass Class where to inject the setter (field's class,
|
||||
* or client)
|
||||
* @param targetApiClass API targetClass implements, which may have the
|
||||
* setter declared
|
||||
* @param field Field of vanilla that will be set
|
||||
* @param exportedName exported name of field
|
||||
*/
|
||||
void injectSetter(ClassFile targetClass, Class<?> targetApiClass, Field field, String exportedName, Number setter)
|
||||
{
|
||||
java.lang.reflect.Method method = inject.findImportMethodOnApi(targetApiClass, exportedName, true);
|
||||
if (method == null)
|
||||
{
|
||||
logger.warn("Setter injection for field {} but an API method was not found on {}", exportedName, targetApiClass);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method.getParameterCount() != 1)
|
||||
{
|
||||
logger.warn("Setter {} with not parameter count != 1?", exportedName);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("Injecting setter for {} on {}", exportedName, targetApiClass);
|
||||
|
||||
assert targetClass.findMethod(method.getName()) == null;
|
||||
assert field.isStatic() || field.getClassFile() == targetClass;
|
||||
|
||||
Signature sig = new Signature.Builder()
|
||||
.setReturnType(Type.VOID)
|
||||
.addArgument(Inject.classToType(method.getParameterTypes()[0]))
|
||||
.build();
|
||||
|
||||
Method setterMethod = new Method(targetClass, method.getName(), sig);
|
||||
setterMethod.setAccessFlags(ACC_PUBLIC);
|
||||
targetClass.addMethod(setterMethod);
|
||||
++injectedSetters;
|
||||
|
||||
Code code = new Code(setterMethod);
|
||||
setterMethod.setCode(code);
|
||||
|
||||
Instructions instructions = code.getInstructions();
|
||||
List<Instruction> ins = instructions.getInstructions();
|
||||
|
||||
// load this
|
||||
if (!field.isStatic())
|
||||
{
|
||||
ins.add(new ALoad(instructions, 0));
|
||||
}
|
||||
|
||||
// load argument
|
||||
Type argumentType = sig.getTypeOfArg(0);
|
||||
ins.add(inject.createLoadForTypeIndex(instructions, argumentType, 1));
|
||||
|
||||
// cast argument to field type
|
||||
Type fieldType = field.getType();
|
||||
if (!argumentType.equals(fieldType))
|
||||
{
|
||||
CheckCast checkCast = new CheckCast(instructions);
|
||||
checkCast.setType(fieldType);
|
||||
ins.add(checkCast);
|
||||
}
|
||||
|
||||
if (setter != null)
|
||||
{
|
||||
assert setter instanceof Integer || setter instanceof Long;
|
||||
|
||||
if (setter instanceof Integer)
|
||||
{
|
||||
ins.add(new LDC(instructions, (int) setter));
|
||||
ins.add(new IMul(instructions));
|
||||
}
|
||||
else
|
||||
{
|
||||
ins.add(new LDC(instructions, (long) setter));
|
||||
ins.add(new LMul(instructions));
|
||||
}
|
||||
}
|
||||
|
||||
if (field.isStatic())
|
||||
{
|
||||
ins.add(new PutStatic(instructions, field));
|
||||
}
|
||||
else
|
||||
{
|
||||
ins.add(new PutField(instructions, field));
|
||||
}
|
||||
|
||||
ins.add(new VReturn(instructions));
|
||||
}
|
||||
|
||||
int getInjectedSetters()
|
||||
{
|
||||
return injectedSetters;
|
||||
}
|
||||
}
|
||||
@@ -1,281 +0,0 @@
|
||||
package net.runelite.injector;
|
||||
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.ClassGroup;
|
||||
import net.runelite.asm.Field;
|
||||
import net.runelite.asm.Method;
|
||||
import net.runelite.asm.Type;
|
||||
import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import net.runelite.deob.DeobAnnotations;
|
||||
|
||||
public class InjectUtil
|
||||
{
|
||||
public static ClassFile toObClass(final ClassGroup vanilla, final ClassFile deobCf) throws InjectionException
|
||||
{
|
||||
final String obfuscatedName = DeobAnnotations.getObfuscatedName(deobCf.getAnnotations());
|
||||
final ClassFile obCf = vanilla.findClass(obfuscatedName);
|
||||
|
||||
if (obCf == null)
|
||||
{
|
||||
throw new InjectionException(String.format("ClassFile \"%s\" could not be found.", obfuscatedName));
|
||||
}
|
||||
|
||||
return obCf;
|
||||
}
|
||||
|
||||
public static Field toObField(final ClassGroup vanilla, final Field field) throws InjectionException
|
||||
{
|
||||
String obfuscatedClassName = DeobAnnotations.getObfuscatedName(field.getClassFile().getAnnotations());
|
||||
String obfuscatedFieldName = DeobAnnotations.getObfuscatedName(field.getAnnotations()); // obfuscated name of field
|
||||
Type type = getFieldType(field);
|
||||
|
||||
ClassFile obfuscatedClass = vanilla.findClass(obfuscatedClassName);
|
||||
if (obfuscatedClass == null)
|
||||
{
|
||||
throw new InjectionException(String.format("ClassFile \"%s\" could not be found.", obfuscatedClassName));
|
||||
}
|
||||
|
||||
Field obfuscatedField = obfuscatedClass.findFieldDeep(obfuscatedFieldName, type);
|
||||
if (obfuscatedField == null)
|
||||
{
|
||||
throw new InjectionException(String.format("Field \"%s\" could not be found.", obfuscatedFieldName));
|
||||
}
|
||||
|
||||
return obfuscatedField;
|
||||
}
|
||||
|
||||
public static ClassFile toDeobClass(final ClassFile obCf, final ClassGroup deob) throws InjectionException
|
||||
{
|
||||
final ClassFile wowThatWasQuick = deob.findObfuscatedName(obCf.getName());
|
||||
if (wowThatWasQuick == null)
|
||||
{
|
||||
throw new InjectionException("It wasn't obfscated enough, or a bit too much. Whatever it was it, wasn't in deob");
|
||||
}
|
||||
return wowThatWasQuick;
|
||||
}
|
||||
|
||||
public static Type getFieldType(final Field f)
|
||||
{
|
||||
Type type = f.getType();
|
||||
|
||||
Annotation obfSignature = f.getAnnotations().find(DeobAnnotations.OBFUSCATED_SIGNATURE);
|
||||
if (obfSignature != null)
|
||||
{
|
||||
//Annotation exists. Type was updated by us during deobfuscation
|
||||
type = DeobAnnotations.getObfuscatedType(f);
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a static method in ClassGroup group. Check the class with name hint first.
|
||||
* (useful for static methods which are in the class they belong to)
|
||||
*/
|
||||
public static Method findStaticMethod(final ClassGroup group, final String name, final String hint) throws InjectionException
|
||||
{
|
||||
final ClassFile cf = group.findClass(hint);
|
||||
|
||||
if (cf == null)
|
||||
{
|
||||
throw new InjectionException(String.format("ClassFile \"%s\" could not be found.", hint));
|
||||
}
|
||||
|
||||
Method m = cf.findStaticMethod(name);
|
||||
|
||||
if (m == null)
|
||||
{
|
||||
m = group.findStaticMethod(name);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a static method in ClassGroup group. Throws exception if not found.
|
||||
*/
|
||||
public static Method findStaticMethod(final ClassGroup group, final String name) throws InjectionException
|
||||
{
|
||||
Method m = group.findStaticMethod(name);
|
||||
|
||||
if (m == null)
|
||||
{
|
||||
throw new InjectionException(String.format("Static method \"%s\" could not be found.", name));
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a static method in ClassGroup group. Throws exception if not found.
|
||||
*/
|
||||
public static Method findStaticMethod(final ClassGroup group, final String name, Signature sig) throws InjectionException
|
||||
{
|
||||
Method m = group.findStaticMethod(name, sig);
|
||||
|
||||
if (m == null)
|
||||
{
|
||||
throw new InjectionException(String.format("Static method \"%s\" could not be found.", name));
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
public static Method findMethod(Inject inject, String name) throws InjectionException
|
||||
{
|
||||
return findMethod(inject, name, null);
|
||||
}
|
||||
|
||||
public static Method findMethod(Inject inject, String name, String hint) throws InjectionException
|
||||
{
|
||||
if (hint != null)
|
||||
{
|
||||
ClassFile c = inject.getDeobfuscated().findClass(hint);
|
||||
|
||||
if (c == null)
|
||||
{
|
||||
throw new InjectionException("Class " + hint + " doesn't exist. (check capitalization)");
|
||||
}
|
||||
|
||||
Method deob = c.findMethod(name);
|
||||
|
||||
if (deob != null)
|
||||
{
|
||||
String obfuscatedName = DeobAnnotations.getObfuscatedName(deob.getAnnotations());
|
||||
Signature obfuscatedSignature = DeobAnnotations.getObfuscatedSignature(deob);
|
||||
|
||||
ClassFile ob = toObClass(inject.getVanilla(), c);
|
||||
|
||||
return ob.findMethod(obfuscatedName, (obfuscatedSignature != null) ? obfuscatedSignature : deob.getDescriptor());
|
||||
}
|
||||
}
|
||||
|
||||
for (ClassFile c : inject.getDeobfuscated().getClasses())
|
||||
{
|
||||
for (Method m : c.getMethods())
|
||||
{
|
||||
if (!m.getName().equals(name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
String obfuscatedName = DeobAnnotations.getObfuscatedName(m.getAnnotations());
|
||||
Signature obfuscatedSignature = DeobAnnotations.getObfuscatedSignature(m);
|
||||
|
||||
ClassFile c2 = toObClass(inject.getVanilla(), c);
|
||||
|
||||
return c2.findMethod(obfuscatedName, (obfuscatedSignature != null) ? obfuscatedSignature : m.getDescriptor());
|
||||
}
|
||||
}
|
||||
|
||||
throw new InjectionException("Couldn't find method " + name);
|
||||
}
|
||||
|
||||
public static Method findStaticMethod(Inject inject, String name) throws InjectionException
|
||||
{
|
||||
for (ClassFile c : inject.getDeobfuscated().getClasses())
|
||||
{
|
||||
for (Method m : c.getMethods())
|
||||
{
|
||||
if (!m.isStatic() || !m.getName().equals(name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
String obfuscatedName = DeobAnnotations.getObfuscatedName(m.getAnnotations());
|
||||
Signature obfuscatedSignature = DeobAnnotations.getObfuscatedSignature(m);
|
||||
|
||||
ClassFile c2 = toObClass(inject.getVanilla(), c);
|
||||
|
||||
return c2.findMethod(obfuscatedName, (obfuscatedSignature != null) ? obfuscatedSignature : m.getDescriptor());
|
||||
}
|
||||
}
|
||||
|
||||
throw new InjectionException("Couldn't find static method " + name);
|
||||
}
|
||||
|
||||
|
||||
public static Field findObField(Inject inject, String name) throws InjectionException
|
||||
{
|
||||
for (ClassFile c : inject.getVanilla().getClasses())
|
||||
{
|
||||
for (Field f : c.getFields())
|
||||
{
|
||||
if (!f.getName().equals(name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InjectionException(String.format("Field \"%s\" could not be found.", name));
|
||||
}
|
||||
|
||||
public static Field findDeobField(Inject inject, String name) throws InjectionException
|
||||
{
|
||||
return findDeobField(inject, name, null);
|
||||
}
|
||||
|
||||
public static Field findDeobField(Inject inject, String name, String hint) throws InjectionException
|
||||
{
|
||||
if (hint != null)
|
||||
{
|
||||
ClassFile c = inject.getDeobfuscated().findClass(hint);
|
||||
if (c == null)
|
||||
{
|
||||
throw new InjectionException("Class " + hint + " doesn't exist. (check capitalization)");
|
||||
}
|
||||
|
||||
for (Field f : c.getFields())
|
||||
{
|
||||
if (!f.getName().equals(name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
String obfuscatedName = DeobAnnotations.getObfuscatedName(f.getAnnotations());
|
||||
|
||||
ClassFile c2 = toObClass(inject.getVanilla(), c);
|
||||
return c2.findField(obfuscatedName);
|
||||
}
|
||||
}
|
||||
|
||||
for (ClassFile c : inject.getDeobfuscated().getClasses())
|
||||
{
|
||||
for (Field f : c.getFields())
|
||||
{
|
||||
if (!f.getName().equals(name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
String obfuscatedName = DeobAnnotations.getObfuscatedName(f.getAnnotations());
|
||||
|
||||
ClassFile c2 = toObClass(inject.getVanilla(), c);
|
||||
return c2.findField(obfuscatedName);
|
||||
}
|
||||
}
|
||||
|
||||
throw new InjectionException(String.format("Mapped field \"%s\" could not be found.", name));
|
||||
}
|
||||
|
||||
public static Field findDeobFieldButUseless(Inject inject, String name) throws InjectionException
|
||||
{
|
||||
for (ClassFile c : inject.getDeobfuscated().getClasses())
|
||||
{
|
||||
for (Field f : c.getFields())
|
||||
{
|
||||
if (!f.getName().equals(name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InjectionException(String.format("Mapped field \"%s\" could not be found.", name));
|
||||
}
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.injector;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.ClassGroup;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Verifies the injected jar is valid
|
||||
*
|
||||
* @author Adam
|
||||
*/
|
||||
class InjectorValidator
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(InjectorValidator.class);
|
||||
|
||||
private static final String API_PACKAGE_BASE = "net/runelite/rs/api/";
|
||||
|
||||
private final ClassGroup group;
|
||||
|
||||
private int error, missing, okay;
|
||||
|
||||
InjectorValidator(ClassGroup group)
|
||||
{
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
void validate()
|
||||
{
|
||||
for (ClassFile cf : group.getClasses())
|
||||
{
|
||||
validate(cf);
|
||||
}
|
||||
|
||||
logger.info("{} overridden methods, {} missing", okay, missing);
|
||||
}
|
||||
|
||||
private void validate(ClassFile cf)
|
||||
{
|
||||
// find methods of the interface not implemented in the class
|
||||
for (net.runelite.asm.pool.Class clazz : cf.getInterfaces().getInterfaces())
|
||||
{
|
||||
if (!clazz.getName().startsWith(API_PACKAGE_BASE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Class<?> c;
|
||||
try
|
||||
{
|
||||
c = Class.forName(clazz.getName().replace('/', '.'));
|
||||
}
|
||||
catch (ClassNotFoundException ex)
|
||||
{
|
||||
logger.warn(null, ex);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cf.isAbstract())
|
||||
{
|
||||
// Abstract classes don't have to implement anything
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Method method : c.getMethods())
|
||||
{
|
||||
if (method.isSynthetic() || method.isDefault())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// could check method signature here too but it is
|
||||
// annoying to deal with both runelite api and java
|
||||
// reflection api
|
||||
if (cf.findMethodDeep(method.getName()) == null)
|
||||
{
|
||||
logger.warn("Class {} implements interface {} but not does implement method {}",
|
||||
cf.getName(), c.getSimpleName(), method);
|
||||
++missing;
|
||||
}
|
||||
else
|
||||
{
|
||||
++okay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Set<NameAndSignature> signatures = new HashSet<>();
|
||||
|
||||
for (net.runelite.asm.Method method : cf.getMethods())
|
||||
{
|
||||
NameAndSignature nas = new NameAndSignature(method.getName(), method.getDescriptor());
|
||||
|
||||
if (signatures.contains(nas))
|
||||
{
|
||||
logger.error("Class {} has duplicate method with same name and signature {} {}",
|
||||
cf.getName(), method.getName(), method.getDescriptor());
|
||||
++error;
|
||||
}
|
||||
|
||||
signatures.add(nas);
|
||||
}
|
||||
}
|
||||
|
||||
int getError()
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
int getMissing()
|
||||
{
|
||||
return missing;
|
||||
}
|
||||
|
||||
int getOkay()
|
||||
{
|
||||
return okay;
|
||||
}
|
||||
|
||||
static final class NameAndSignature
|
||||
{
|
||||
String name;
|
||||
Signature signature;
|
||||
|
||||
NameAndSignature(String name, Signature signature)
|
||||
{
|
||||
this.name = name;
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int hash = 3;
|
||||
hash = 67 * hash + Objects.hashCode(this.name);
|
||||
hash = 67 * hash + Objects.hashCode(this.signature);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
final NameAndSignature other = (NameAndSignature) obj;
|
||||
if (!Objects.equals(this.name, other.name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(this.signature, other.signature))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,998 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.injector;
|
||||
|
||||
import com.google.common.reflect.ClassPath;
|
||||
import com.google.common.reflect.ClassPath.ClassInfo;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import net.runelite.api.mixins.Mixin;
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.Field;
|
||||
import net.runelite.asm.Method;
|
||||
import net.runelite.asm.Type;
|
||||
import net.runelite.asm.attributes.Code;
|
||||
import net.runelite.asm.attributes.annotation.Annotation;
|
||||
import net.runelite.asm.attributes.code.Instruction;
|
||||
import net.runelite.asm.attributes.code.Instructions;
|
||||
import net.runelite.asm.attributes.code.instruction.types.FieldInstruction;
|
||||
import net.runelite.asm.attributes.code.instruction.types.InvokeInstruction;
|
||||
import net.runelite.asm.attributes.code.instruction.types.LVTInstruction;
|
||||
import net.runelite.asm.attributes.code.instruction.types.PushConstantInstruction;
|
||||
import net.runelite.asm.attributes.code.instruction.types.ReturnInstruction;
|
||||
import net.runelite.asm.attributes.code.instructions.ALoad;
|
||||
import net.runelite.asm.attributes.code.instructions.ANewArray;
|
||||
import net.runelite.asm.attributes.code.instructions.CheckCast;
|
||||
import net.runelite.asm.attributes.code.instructions.GetField;
|
||||
import net.runelite.asm.attributes.code.instructions.ILoad;
|
||||
import net.runelite.asm.attributes.code.instructions.InvokeDynamic;
|
||||
import net.runelite.asm.attributes.code.instructions.InvokeSpecial;
|
||||
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
|
||||
import net.runelite.asm.attributes.code.instructions.Pop;
|
||||
import net.runelite.asm.attributes.code.instructions.PutField;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import net.runelite.asm.visitors.ClassFileVisitor;
|
||||
import net.runelite.deob.DeobAnnotations;
|
||||
import static net.runelite.injector.InjectUtil.findStaticMethod;
|
||||
import static net.runelite.injector.InjectUtil.toDeobClass;
|
||||
import static net.runelite.injector.InjectUtil.toObClass;
|
||||
import static net.runelite.injector.InjectUtil.toObField;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class MixinInjector
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(MixinInjector.class);
|
||||
|
||||
private static final Type INJECT = new Type("Lnet/runelite/api/mixins/Inject;");
|
||||
private static final Type SHADOW = new Type("Lnet/runelite/api/mixins/Shadow;");
|
||||
private static final Type COPY = new Type("Lnet/runelite/api/mixins/Copy;");
|
||||
private static final Type REPLACE = new Type("Lnet/runelite/api/mixins/Replace;");
|
||||
private static final Type FIELDHOOK = new Type("Lnet/runelite/api/mixins/FieldHook;");
|
||||
private static final Type METHODHOOK = new Type("Lnet/runelite/api/mixins/MethodHook;");
|
||||
private static final Type JAVAX_INJECT = new Type("Ljavax/inject/Inject;");
|
||||
private static final Type NAMED = new Type("Ljavax/inject/Named;");
|
||||
|
||||
private static final String MIXIN_BASE = "net.runelite.mixins";
|
||||
private static final String ASSERTION_FIELD = "$assertionsDisabled";
|
||||
|
||||
private final Inject inject;
|
||||
|
||||
// field name -> Field of injected fields
|
||||
private final Map<String, Field> injectedFields = new HashMap<>();
|
||||
// Use net.runelite.asm.pool.Field instead of Field because the pool version has hashcode implemented
|
||||
private final Map<net.runelite.asm.pool.Field, Field> shadowFields = new HashMap<>();
|
||||
|
||||
MixinInjector(Inject inject)
|
||||
{
|
||||
this.inject = inject;
|
||||
}
|
||||
|
||||
public void inject() throws InjectionException
|
||||
{
|
||||
ClassPath classPath;
|
||||
|
||||
try
|
||||
{
|
||||
classPath = ClassPath.from(this.getClass().getClassLoader());
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw new InjectionException(ex);
|
||||
}
|
||||
|
||||
// key: mixin class
|
||||
// value: mixin targets
|
||||
Map<Class<?>, List<ClassFile>> mixinClasses = new HashMap<>();
|
||||
|
||||
// Find mixins and populate mixinClasses
|
||||
for (ClassInfo classInfo : classPath.getTopLevelClasses(MIXIN_BASE))
|
||||
{
|
||||
Class<?> mixinClass = classInfo.load();
|
||||
List<ClassFile> mixinTargets = new ArrayList<>();
|
||||
|
||||
for (Mixin mixin : mixinClass.getAnnotationsByType(Mixin.class))
|
||||
{
|
||||
Class<?> implementInto = mixin.value();
|
||||
|
||||
ClassFile targetCf = inject.findVanillaForInterface(implementInto);
|
||||
|
||||
if (targetCf == null)
|
||||
{
|
||||
throw new InjectionException("No class implements " + implementInto + " for mixin " + mixinClass);
|
||||
}
|
||||
|
||||
mixinTargets.add(targetCf);
|
||||
}
|
||||
|
||||
mixinClasses.put(mixinClass, mixinTargets);
|
||||
}
|
||||
|
||||
inject(mixinClasses);
|
||||
}
|
||||
|
||||
public void inject(Map<Class<?>, List<ClassFile>> mixinClasses) throws InjectionException
|
||||
{
|
||||
injectFields(mixinClasses);
|
||||
findShadowFields(mixinClasses);
|
||||
|
||||
for (Class<?> mixinClass : mixinClasses.keySet())
|
||||
{
|
||||
try
|
||||
{
|
||||
for (ClassFile cf : mixinClasses.get(mixinClass))
|
||||
{
|
||||
// Make a new mixin ClassFile copy every time,
|
||||
// so they don't share Code references
|
||||
ClassFile mixinCf = loadClass(mixinClass);
|
||||
|
||||
injectMethods(mixinCf, cf, shadowFields);
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw new InjectionException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
injectFieldHooks(mixinClasses);
|
||||
injectMethodHooks(mixinClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds fields that are marked @Inject and inject them into the target
|
||||
*/
|
||||
private void injectFields(Map<Class<?>, List<ClassFile>> mixinClasses) throws InjectionException
|
||||
{
|
||||
// Inject fields, and put them in injectedFields if they can be used by other mixins
|
||||
for (Class<?> mixinClass : mixinClasses.keySet())
|
||||
{
|
||||
ClassFile mixinCf;
|
||||
|
||||
try
|
||||
{
|
||||
mixinCf = loadClass(mixinClass);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw new InjectionException(ex);
|
||||
}
|
||||
|
||||
List<ClassFile> targetCfs = mixinClasses.get(mixinClass);
|
||||
|
||||
for (ClassFile cf : targetCfs)
|
||||
{
|
||||
for (Field field : mixinCf.getFields())
|
||||
{
|
||||
// Always inject $assertionsEnabled if its missing.
|
||||
if (ASSERTION_FIELD.equals(field.getName()))
|
||||
{
|
||||
if (cf.findField(ASSERTION_FIELD, Type.BOOLEAN) != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Annotation inject = field.getAnnotations().find(INJECT);
|
||||
|
||||
if (inject == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Field copy = new Field(cf, field.getName(), field.getType());
|
||||
copy.setAccessFlags(field.getAccessFlags());
|
||||
copy.setPublic();
|
||||
copy.setValue(field.getValue());
|
||||
|
||||
Annotation jInject = field.getAnnotations().find(JAVAX_INJECT);
|
||||
if (jInject != null)
|
||||
{
|
||||
copy.getAnnotations().addAnnotation(jInject);
|
||||
logger.info("Added javax inject to {}.{}", cf.getClassName(), copy.getName());
|
||||
|
||||
Annotation named = field.getAnnotations().find(NAMED);
|
||||
if (named != null)
|
||||
{
|
||||
copy.getAnnotations().addAnnotation(named);
|
||||
logger.info("Added javax named to {}.{}", cf.getClassName(), copy.getName());
|
||||
}
|
||||
}
|
||||
|
||||
cf.addField(copy);
|
||||
|
||||
if (injectedFields.containsKey(field.getName()) && !field.getName().equals(ASSERTION_FIELD))
|
||||
{
|
||||
java.util.logging.Logger.getAnonymousLogger().severe("Duplicate field : " + field.getName());
|
||||
throw new InjectionException("Injected field names must be globally unique");
|
||||
}
|
||||
|
||||
injectedFields.put(field.getName(), copy);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find fields which are marked @Shadow, and what they shadow
|
||||
*/
|
||||
private void findShadowFields(Map<Class<?>, List<ClassFile>> mixinClasses) throws InjectionException
|
||||
{
|
||||
// Find shadow fields
|
||||
// Injected static fields take precedence when looking up shadowed fields
|
||||
for (Class<?> mixinClass : mixinClasses.keySet())
|
||||
{
|
||||
ClassFile mixinCf;
|
||||
|
||||
try
|
||||
{
|
||||
mixinCf = loadClass(mixinClass);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw new InjectionException(ex);
|
||||
}
|
||||
|
||||
for (Field field : mixinCf.getFields())
|
||||
{
|
||||
Annotation shadow = field.getAnnotations().find(SHADOW);
|
||||
if (shadow != null)
|
||||
{
|
||||
if (!field.isStatic())
|
||||
{
|
||||
throw new InjectionException("Can only shadow static fields");
|
||||
}
|
||||
|
||||
String shadowName = shadow.getElement().getString(); // shadow this field
|
||||
|
||||
Field injectedField = injectedFields.get(shadowName);
|
||||
if (injectedField != null)
|
||||
{
|
||||
// Shadow a field injected by a mixin
|
||||
shadowFields.put(field.getPoolField(), injectedField);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shadow a field already in the gamepack
|
||||
Field shadowField = InjectUtil.findDeobFieldButUseless(inject, shadowName);
|
||||
|
||||
if (shadowField == null)
|
||||
{
|
||||
throw new InjectionException("Shadow of nonexistent field " + shadowName);
|
||||
}
|
||||
|
||||
Field obShadow = toObField(inject.getVanilla(), shadowField);
|
||||
assert obShadow != null;
|
||||
shadowFields.put(field.getPoolField(), obShadow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ClassFile loadClass(Class<?> clazz) throws IOException
|
||||
{
|
||||
try (InputStream is = clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + ".class"))
|
||||
{
|
||||
ClassReader reader = new ClassReader(is);
|
||||
ClassFileVisitor cv = new ClassFileVisitor();
|
||||
|
||||
reader.accept(cv, 0);
|
||||
|
||||
return cv.getClassFile();
|
||||
}
|
||||
}
|
||||
|
||||
private void injectMethods(ClassFile mixinCf, ClassFile cf, Map<net.runelite.asm.pool.Field, Field> shadowFields)
|
||||
throws InjectionException
|
||||
{
|
||||
// Keeps mappings between methods annotated with @Copy -> the copied method within the vanilla pack
|
||||
Map<net.runelite.asm.pool.Method, CopiedMethod> copiedMethods = new HashMap<>();
|
||||
|
||||
// Handle the copy mixins first, so all other mixins know of the copies
|
||||
for (Method method : mixinCf.getMethods())
|
||||
{
|
||||
Annotation copyAnnotation = method.getAnnotations().find(COPY);
|
||||
|
||||
if (copyAnnotation == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
String deobMethodName = (String) copyAnnotation.getElement().getValue();
|
||||
Method deobMethod;
|
||||
if (method.isStatic())
|
||||
{
|
||||
deobMethod = findStaticMethod(inject.getDeobfuscated(), deobMethodName, method.getDescriptor().rsApiToRsClient());
|
||||
}
|
||||
else
|
||||
{
|
||||
ClassFile deobCf = toDeobClass(cf, inject.getDeobfuscated());
|
||||
deobMethod = deobCf.findMethod(deobMethodName, method.getDescriptor().rsApiToRsClient());
|
||||
}
|
||||
|
||||
|
||||
if (deobMethod == null)
|
||||
{
|
||||
throw new InjectionException("Failed to find the deob method " + deobMethodName + " for mixin " + mixinCf);
|
||||
}
|
||||
|
||||
if (method.isStatic() != deobMethod.isStatic())
|
||||
{
|
||||
throw new InjectionException("Mixin method " + method + " should be " + (deobMethod.isStatic() ? "static" : "non-static"));
|
||||
}
|
||||
|
||||
// Find the vanilla class where the method to copy is in
|
||||
String obClassName = DeobAnnotations.getObfuscatedName(deobMethod.getClassFile().getAnnotations());
|
||||
ClassFile obCf = inject.getVanilla().findClass(obClassName);
|
||||
assert obCf != null : "unable to find vanilla class from obfuscated name " + obClassName;
|
||||
|
||||
String obMethodName = DeobAnnotations.getObfuscatedName(deobMethod.getAnnotations());
|
||||
Signature obMethodSignature = DeobAnnotations.getObfuscatedSignature(deobMethod);
|
||||
|
||||
if (obMethodName == null)
|
||||
{
|
||||
obMethodName = deobMethod.getName();
|
||||
}
|
||||
if (obMethodSignature == null)
|
||||
{
|
||||
obMethodSignature = deobMethod.getDescriptor();
|
||||
}
|
||||
|
||||
Method obMethod = obCf.findMethod(obMethodName, obMethodSignature);
|
||||
if (obMethod == null)
|
||||
{
|
||||
throw new InjectionException("Failed to find the ob method " + obMethodName + " for mixin " + mixinCf);
|
||||
}
|
||||
|
||||
if (method.getDescriptor().size() > obMethod.getDescriptor().size())
|
||||
{
|
||||
throw new InjectionException("Mixin methods cannot have more parameters than their corresponding ob method");
|
||||
}
|
||||
|
||||
Method copy = new Method(cf, "copy$" + deobMethodName, obMethodSignature);
|
||||
moveCode(copy, obMethod.getCode());
|
||||
copy.setAccessFlags(obMethod.getAccessFlags());
|
||||
copy.setPublic();
|
||||
copy.getExceptions().getExceptions().addAll(obMethod.getExceptions().getExceptions());
|
||||
copy.getAnnotations().getAnnotations().addAll(obMethod.getAnnotations().getAnnotations());
|
||||
cf.addMethod(copy);
|
||||
|
||||
/*
|
||||
If the desc for the mixin method and the desc for the ob method
|
||||
are the same in length, assume that the mixin method is taking
|
||||
care of the garbage parameter itself.
|
||||
*/
|
||||
boolean hasGarbageValue = method.getDescriptor().size() != obMethod.getDescriptor().size()
|
||||
&& deobMethod.getDescriptor().size() < obMethodSignature.size();
|
||||
copiedMethods.put(method.getPoolMethod(), new CopiedMethod(copy, hasGarbageValue));
|
||||
|
||||
logger.debug("Injected copy of {} to {}", obMethod, copy);
|
||||
}
|
||||
|
||||
// Handle the rest of the mixin types
|
||||
for (Method method : mixinCf.getMethods())
|
||||
{
|
||||
boolean isClinit = "<clinit>".equals(method.getName());
|
||||
boolean isInit = "<init>".equals(method.getName());
|
||||
boolean hasInject = method.getAnnotations().find(INJECT) != null;
|
||||
|
||||
// You can't annotate clinit, so its always injected
|
||||
if ((hasInject && isInit) || isClinit)
|
||||
{
|
||||
if (!"()V".equals(method.getDescriptor().toString()))
|
||||
{
|
||||
throw new InjectionException("Injected constructors cannot have arguments");
|
||||
}
|
||||
|
||||
Method[] originalMethods = cf.getMethods().stream()
|
||||
.filter(n -> n.getName().equals(method.getName()))
|
||||
.toArray(Method[]::new);
|
||||
// If there isn't a <clinit> already just inject ours, otherwise rename it
|
||||
// This is always true for <init>
|
||||
String name = method.getName();
|
||||
if (originalMethods.length > 0)
|
||||
{
|
||||
name = "rl$$" + (isInit ? "init" : "clinit");
|
||||
}
|
||||
String numberlessName = name;
|
||||
for (int i = 1; cf.findMethod(name, method.getDescriptor()) != null; i++)
|
||||
{
|
||||
name = numberlessName + i;
|
||||
}
|
||||
|
||||
Method copy = new Method(cf, name, method.getDescriptor());
|
||||
moveCode(copy, method.getCode());
|
||||
copy.setAccessFlags(method.getAccessFlags());
|
||||
copy.setPrivate();
|
||||
assert method.getExceptions().getExceptions().isEmpty();
|
||||
|
||||
// Remove the call to the superclass's ctor
|
||||
if (isInit)
|
||||
{
|
||||
Instructions instructions = copy.getCode().getInstructions();
|
||||
ListIterator<Instruction> listIter = instructions.getInstructions().listIterator();
|
||||
for (; listIter.hasNext(); )
|
||||
{
|
||||
Instruction instr = listIter.next();
|
||||
if (instr instanceof InvokeSpecial)
|
||||
{
|
||||
InvokeSpecial invoke = (InvokeSpecial) instr;
|
||||
assert invoke.getMethod().getName().equals("<init>");
|
||||
listIter.remove();
|
||||
int pops = invoke.getMethod().getType().getArguments().size() + 1;
|
||||
for (int i = 0; i < pops; i++)
|
||||
{
|
||||
listIter.add(new Pop(instructions));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setOwnersToTargetClass(mixinCf, cf, copy, shadowFields, copiedMethods);
|
||||
cf.addMethod(copy);
|
||||
|
||||
// Call our method at the return point of the matching method(s)
|
||||
for (Method om : originalMethods)
|
||||
{
|
||||
Instructions instructions = om.getCode().getInstructions();
|
||||
ListIterator<Instruction> listIter = instructions.getInstructions().listIterator();
|
||||
for (; listIter.hasNext(); )
|
||||
{
|
||||
Instruction instr = listIter.next();
|
||||
if (instr instanceof ReturnInstruction)
|
||||
{
|
||||
listIter.previous();
|
||||
if (isInit)
|
||||
{
|
||||
listIter.add(new ALoad(instructions, 0));
|
||||
listIter.add(new InvokeSpecial(instructions, copy.getPoolMethod()));
|
||||
}
|
||||
else if (isClinit)
|
||||
{
|
||||
listIter.add(new InvokeStatic(instructions, copy.getPoolMethod()));
|
||||
}
|
||||
listIter.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Injected mixin method {} to {}", copy, cf);
|
||||
}
|
||||
else if (hasInject)
|
||||
{
|
||||
// Make sure the method doesn't invoke copied methods
|
||||
for (Instruction i : method.getCode().getInstructions().getInstructions())
|
||||
{
|
||||
if (i instanceof InvokeInstruction)
|
||||
{
|
||||
InvokeInstruction ii = (InvokeInstruction) i;
|
||||
|
||||
if (copiedMethods.containsKey(ii.getMethod()))
|
||||
{
|
||||
throw new InjectionException("Injected methods cannot invoke copied methods");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Method copy = new Method(cf, method.getName(), method.getDescriptor());
|
||||
moveCode(copy, method.getCode());
|
||||
copy.setAccessFlags(method.getAccessFlags());
|
||||
copy.setPublic();
|
||||
assert method.getExceptions().getExceptions().isEmpty();
|
||||
|
||||
setOwnersToTargetClass(mixinCf, cf, copy, shadowFields, copiedMethods);
|
||||
|
||||
cf.addMethod(copy);
|
||||
|
||||
logger.debug("Injected mixin method {} to {}", copy, cf);
|
||||
}
|
||||
else if (method.getAnnotations().find(REPLACE) != null)
|
||||
{
|
||||
Annotation replaceAnnotation = method.getAnnotations().find(REPLACE);
|
||||
String deobMethodName = (String) replaceAnnotation.getElement().getValue();
|
||||
|
||||
ClassFile deobCf = inject.toDeobClass(cf);
|
||||
Method deobMethod = findDeobMethod(deobCf, deobMethodName, method.getDescriptor());
|
||||
|
||||
if (deobMethod == null)
|
||||
{
|
||||
throw new InjectionException("Failed to find the deob method " + deobMethodName + " for mixin " + mixinCf);
|
||||
}
|
||||
|
||||
if (method.isStatic() != deobMethod.isStatic())
|
||||
{
|
||||
throw new InjectionException("Mixin method " + method + " should be "
|
||||
+ (deobMethod.isStatic() ? "static" : "non-static"));
|
||||
}
|
||||
|
||||
String obMethodName = DeobAnnotations.getObfuscatedName(deobMethod.getAnnotations());
|
||||
Signature obMethodSignature = DeobAnnotations.getObfuscatedSignature(deobMethod);
|
||||
|
||||
// Deob signature is the same as ob signature
|
||||
if (obMethodName == null)
|
||||
{
|
||||
obMethodName = deobMethod.getName();
|
||||
}
|
||||
if (obMethodSignature == null)
|
||||
{
|
||||
obMethodSignature = deobMethod.getDescriptor();
|
||||
}
|
||||
|
||||
// Find the vanilla class where the method to copy is in
|
||||
String obClassName = DeobAnnotations.getObfuscatedName(deobMethod.getClassFile().getAnnotations());
|
||||
ClassFile obCf = inject.getVanilla().findClass(obClassName);
|
||||
|
||||
Method obMethod = obCf.findMethod(obMethodName, obMethodSignature);
|
||||
assert obMethod != null : "obfuscated method " + obMethodName + obMethodSignature + " does not exist";
|
||||
|
||||
if (method.getDescriptor().size() > obMethod.getDescriptor().size())
|
||||
{
|
||||
throw new InjectionException("Mixin methods cannot have more parameters than their corresponding ob method");
|
||||
}
|
||||
|
||||
Type returnType = method.getDescriptor().getReturnValue();
|
||||
Type deobReturnType = inject.apiTypeToDeobfuscatedType(returnType);
|
||||
if (!returnType.equals(deobReturnType))
|
||||
{
|
||||
ClassFile deobReturnTypeClassFile = inject.getDeobfuscated()
|
||||
.findClass(deobReturnType.getInternalName());
|
||||
if (deobReturnTypeClassFile != null)
|
||||
{
|
||||
ClassFile obReturnTypeClass = toObClass(inject.getVanilla(), deobReturnTypeClassFile);
|
||||
|
||||
Instructions instructions = method.getCode().getInstructions();
|
||||
ListIterator<Instruction> listIter = instructions.getInstructions().listIterator();
|
||||
for (; listIter.hasNext(); )
|
||||
{
|
||||
Instruction instr = listIter.next();
|
||||
if (instr instanceof ReturnInstruction)
|
||||
{
|
||||
listIter.previous();
|
||||
CheckCast checkCast = new CheckCast(instructions);
|
||||
checkCast.setType(new Type(obReturnTypeClass.getName()));
|
||||
listIter.add(checkCast);
|
||||
listIter.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
moveCode(obMethod, method.getCode());
|
||||
|
||||
boolean hasGarbageValue = method.getDescriptor().size() != obMethod.getDescriptor().size()
|
||||
&& deobMethod.getDescriptor().size() < obMethodSignature.size();
|
||||
|
||||
if (hasGarbageValue)
|
||||
{
|
||||
int garbageIndex = obMethod.isStatic()
|
||||
? obMethod.getDescriptor().size() - 1
|
||||
: obMethod.getDescriptor().size();
|
||||
|
||||
/*
|
||||
If the mixin method doesn't have the garbage parameter,
|
||||
the compiler will have produced code that uses the garbage
|
||||
parameter's local variable index for other things,
|
||||
so we'll have to add 1 to all loads/stores to indices
|
||||
that are >= garbageIndex.
|
||||
*/
|
||||
shiftLocalIndices(obMethod.getCode().getInstructions(), garbageIndex);
|
||||
}
|
||||
|
||||
setOwnersToTargetClass(mixinCf, cf, obMethod, shadowFields, copiedMethods);
|
||||
|
||||
logger.debug("Replaced method {} with mixin method {}", obMethod, method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void moveCode(Method method, Code code)
|
||||
{
|
||||
Code newCode = new Code(method);
|
||||
newCode.setMaxStack(code.getMaxStack());
|
||||
newCode.getInstructions().getInstructions().addAll(code.getInstructions().getInstructions());
|
||||
// Update instructions for each instruction
|
||||
for (Instruction i : newCode.getInstructions().getInstructions())
|
||||
{
|
||||
i.setInstructions(newCode.getInstructions());
|
||||
}
|
||||
newCode.getExceptions().getExceptions().addAll(code.getExceptions().getExceptions());
|
||||
for (net.runelite.asm.attributes.code.Exception e : newCode.getExceptions().getExceptions())
|
||||
{
|
||||
e.setExceptions(newCode.getExceptions());
|
||||
}
|
||||
method.setCode(newCode);
|
||||
}
|
||||
|
||||
private void setOwnersToTargetClass(ClassFile mixinCf, ClassFile cf, Method method,
|
||||
Map<net.runelite.asm.pool.Field, Field> shadowFields,
|
||||
Map<net.runelite.asm.pool.Method, CopiedMethod> copiedMethods)
|
||||
throws InjectionException
|
||||
{
|
||||
ListIterator<Instruction> iterator = method.getCode().getInstructions().getInstructions().listIterator();
|
||||
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
Instruction i = iterator.next();
|
||||
|
||||
if (i instanceof ANewArray)
|
||||
{
|
||||
Type type = ((ANewArray) i).getType_();
|
||||
ClassFile deobCf = inject.getDeobfuscated().findClass(type.toString().replace("Lnet/runelite/rs/api/RS", "").replace(";", ""));
|
||||
|
||||
if (deobCf != null)
|
||||
{
|
||||
ClassFile obReturnTypeClass = toObClass(inject.getVanilla(), deobCf);
|
||||
Type newType = new Type("L" + obReturnTypeClass.getName() + ";");
|
||||
|
||||
((ANewArray) i).setType(newType);
|
||||
logger.info("Replaced {} type {} with type {}", i, type, newType);
|
||||
}
|
||||
}
|
||||
|
||||
if (i instanceof InvokeInstruction)
|
||||
{
|
||||
InvokeInstruction ii = (InvokeInstruction) i;
|
||||
|
||||
CopiedMethod copiedMethod = copiedMethods.get(ii.getMethod());
|
||||
if (copiedMethod != null)
|
||||
{
|
||||
ii.setMethod(copiedMethod.obMethod.getPoolMethod());
|
||||
|
||||
// Pass through garbage value if the method has one
|
||||
if (copiedMethod.hasGarbageValue)
|
||||
{
|
||||
int garbageIndex = copiedMethod.obMethod.isStatic()
|
||||
? copiedMethod.obMethod.getDescriptor().size() - 1
|
||||
: copiedMethod.obMethod.getDescriptor().size();
|
||||
|
||||
iterator.previous();
|
||||
iterator.add(new ILoad(method.getCode().getInstructions(), garbageIndex));
|
||||
iterator.next();
|
||||
}
|
||||
}
|
||||
else if (ii.getMethod().getClazz().getName().equals(mixinCf.getName()))
|
||||
{
|
||||
ii.setMethod(new net.runelite.asm.pool.Method(
|
||||
new net.runelite.asm.pool.Class(cf.getName()),
|
||||
ii.getMethod().getName(),
|
||||
ii.getMethod().getType()
|
||||
));
|
||||
}
|
||||
}
|
||||
else if (i instanceof FieldInstruction)
|
||||
{
|
||||
FieldInstruction fi = (FieldInstruction) i;
|
||||
|
||||
Field shadowed = shadowFields.get(fi.getField());
|
||||
if (shadowed != null)
|
||||
{
|
||||
fi.setField(shadowed.getPoolField());
|
||||
}
|
||||
else if (fi.getField().getClazz().getName().equals(mixinCf.getName()))
|
||||
{
|
||||
fi.setField(new net.runelite.asm.pool.Field(
|
||||
new net.runelite.asm.pool.Class(cf.getName()),
|
||||
fi.getField().getName(),
|
||||
fi.getField().getType()
|
||||
));
|
||||
}
|
||||
}
|
||||
else if (i instanceof PushConstantInstruction)
|
||||
{
|
||||
PushConstantInstruction pi = (PushConstantInstruction) i;
|
||||
if (mixinCf.getPoolClass().equals(pi.getConstant()))
|
||||
{
|
||||
pi.setConstant(cf.getPoolClass());
|
||||
}
|
||||
}
|
||||
|
||||
verify(mixinCf, i);
|
||||
}
|
||||
}
|
||||
|
||||
private void shiftLocalIndices(Instructions instructions, int startIdx)
|
||||
{
|
||||
for (Instruction i : instructions.getInstructions())
|
||||
{
|
||||
if (i instanceof LVTInstruction)
|
||||
{
|
||||
LVTInstruction lvti = (LVTInstruction) i;
|
||||
|
||||
if (lvti.getVariableIndex() >= startIdx)
|
||||
{
|
||||
lvti.setVariableIndex(lvti.getVariableIndex() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Method findDeobMethod(ClassFile deobCf, String deobMethodName, Signature descriptor)
|
||||
throws InjectionException
|
||||
{
|
||||
List<Method> matchingMethods = new ArrayList<>();
|
||||
|
||||
for (Method m : deobCf.getMethods())
|
||||
{
|
||||
if (!deobMethodName.equals(m.getName()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Type returnType = inject.apiTypeToDeobfuscatedType(descriptor.getReturnValue());
|
||||
Type returnType2 = m.getDescriptor().getReturnValue();
|
||||
|
||||
if (!returnType.equals(returnType2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Type> args = descriptor.getArguments();
|
||||
List<Type> args2 = m.getDescriptor().getArguments();
|
||||
|
||||
if (args.size() > args2.size())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean matchingArgs = true;
|
||||
|
||||
for (int i = 0; i < args.size(); i++)
|
||||
{
|
||||
Type type = inject.apiTypeToDeobfuscatedType(args.get(i));
|
||||
Type type2 = args2.get(i);
|
||||
|
||||
if (!type.equals(type2))
|
||||
{
|
||||
matchingArgs = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matchingArgs)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
matchingMethods.add(m);
|
||||
}
|
||||
|
||||
if (matchingMethods.size() > 1)
|
||||
{
|
||||
// this happens when it has found several deob methods for some mixin method,
|
||||
// to get rid of the error, refine your search by making your mixin method have more parameters
|
||||
throw new InjectionException("There are several matching methods when there should only be one");
|
||||
}
|
||||
else if (matchingMethods.size() == 1)
|
||||
{
|
||||
return matchingMethods.get(0);
|
||||
}
|
||||
|
||||
Method method = deobCf.findMethod(deobMethodName);
|
||||
|
||||
if (method == null)
|
||||
{
|
||||
// Look for static methods if an instance method couldn't be found
|
||||
for (ClassFile deobCf2 : inject.getDeobfuscated().getClasses())
|
||||
{
|
||||
if (deobCf2 != deobCf)
|
||||
{
|
||||
method = deobCf2.findMethod(deobMethodName);
|
||||
|
||||
if (method != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
private void verify(ClassFile mixinCf, Instruction i) throws InjectionException
|
||||
{
|
||||
if (i instanceof FieldInstruction)
|
||||
{
|
||||
FieldInstruction fi = (FieldInstruction) i;
|
||||
|
||||
if (fi.getField().getClazz().getName().equals(mixinCf.getName()))
|
||||
{
|
||||
if (i instanceof PutField || i instanceof GetField)
|
||||
{
|
||||
throw new InjectionException("Access to non static member field of mixin");
|
||||
}
|
||||
|
||||
Field field = fi.getMyField();
|
||||
if (field != null && !field.isPublic())
|
||||
{
|
||||
throw new InjectionException("Static access to non public field " + field);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (i instanceof InvokeStatic)
|
||||
{
|
||||
InvokeStatic is = (InvokeStatic) i;
|
||||
|
||||
if (is.getMethod().getClazz() != mixinCf.getPoolClass()
|
||||
&& is.getMethod().getClazz().getName().startsWith(MIXIN_BASE.replace(".", "/")))
|
||||
{
|
||||
throw new InjectionException("Invoking static methods of other mixins is not supported");
|
||||
}
|
||||
}
|
||||
else if (i instanceof InvokeDynamic)
|
||||
{
|
||||
// RS classes don't verify under java 7+ due to the
|
||||
// super() invokespecial being inside of a try{}
|
||||
throw new InjectionException("Injected bytecode must be Java 6 compatible");
|
||||
}
|
||||
}
|
||||
|
||||
private void injectFieldHooks(Map<Class<?>, List<ClassFile>> mixinClasses) throws InjectionException
|
||||
{
|
||||
InjectHook injectHook = new InjectHook(inject);
|
||||
|
||||
for (Class<?> mixinClass : mixinClasses.keySet())
|
||||
{
|
||||
ClassFile mixinCf;
|
||||
|
||||
try
|
||||
{
|
||||
mixinCf = loadClass(mixinClass);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw new InjectionException(ex);
|
||||
}
|
||||
|
||||
List<ClassFile> targetCfs = mixinClasses.get(mixinClass);
|
||||
|
||||
for (ClassFile cf : targetCfs)
|
||||
{
|
||||
for (Method method : mixinCf.getMethods())
|
||||
{
|
||||
Annotation fieldHook = method.getAnnotations().find(FIELDHOOK);
|
||||
if (fieldHook != null)
|
||||
{
|
||||
String hookName = fieldHook.getElement().getString();
|
||||
boolean before = fieldHook.getElements().size() == 2 && fieldHook.getElements().get(1).getValue().equals(true);
|
||||
ClassFile deobCf = inject.toDeobClass(cf);
|
||||
Field targetField = deobCf.findField(hookName);
|
||||
if (targetField == null)
|
||||
{
|
||||
// first try non static fields, then static
|
||||
targetField = InjectUtil.findDeobFieldButUseless(inject, hookName);
|
||||
}
|
||||
|
||||
if (targetField == null)
|
||||
{
|
||||
throw new InjectionException("Field hook for nonexistent field " + hookName + " on " + method);
|
||||
}
|
||||
|
||||
Annotation an = targetField.getAnnotations().find(DeobAnnotations.OBFUSCATED_GETTER);
|
||||
Number getter = null;
|
||||
if (an != null)
|
||||
{
|
||||
getter = (Number) an.getElement().getValue();
|
||||
}
|
||||
|
||||
Field obField = toObField(inject.getVanilla(), targetField);
|
||||
|
||||
if (method.isStatic() != targetField.isStatic())
|
||||
{
|
||||
throw new InjectionException("Field hook method static flag must match target field");
|
||||
}
|
||||
|
||||
// cf is the target class to invoke
|
||||
InjectHook.HookInfo hookInfo = new InjectHook.HookInfo();
|
||||
hookInfo.clazz = cf.getName();
|
||||
hookInfo.fieldName = hookName;
|
||||
hookInfo.method = method;
|
||||
hookInfo.before = before;
|
||||
hookInfo.getter = getter;
|
||||
injectHook.hook(obField, hookInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
injectHook.run();
|
||||
|
||||
logger.info("Injected {} field hooks", injectHook.getInjectedHooks());
|
||||
}
|
||||
|
||||
private void injectMethodHooks(Map<Class<?>, List<ClassFile>> mixinClasses) throws InjectionException
|
||||
{
|
||||
InjectHookMethod injectHookMethod = new InjectHookMethod(inject);
|
||||
|
||||
for (Class<?> mixinClass : mixinClasses.keySet())
|
||||
{
|
||||
ClassFile mixinCf;
|
||||
|
||||
try
|
||||
{
|
||||
mixinCf = loadClass(mixinClass);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw new InjectionException(ex);
|
||||
}
|
||||
|
||||
List<ClassFile> targetCfs = mixinClasses.get(mixinClass);
|
||||
|
||||
for (ClassFile cf : targetCfs)
|
||||
{
|
||||
for (Method method : mixinCf.getMethods())
|
||||
{
|
||||
Annotation methodHook = method.getAnnotations().find(METHODHOOK);
|
||||
|
||||
if (methodHook == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
String hookName = methodHook.getElement().getString();
|
||||
boolean end = methodHook.getElements().size() == 2 && methodHook.getElements().get(1).getValue().equals(true);
|
||||
ClassFile deobCf = inject.toDeobClass(cf);
|
||||
Method targetMethod = findDeobMethod(deobCf, hookName, method.getDescriptor());
|
||||
|
||||
if (targetMethod == null)
|
||||
{
|
||||
throw new InjectionException("Method hook for nonexistent method " + hookName + " on " + method);
|
||||
}
|
||||
|
||||
if (method.isStatic() != targetMethod.isStatic())
|
||||
{
|
||||
throw new InjectionException("Method hook static flag must match target - " + hookName);
|
||||
}
|
||||
|
||||
injectHookMethod.inject(method, targetMethod, hookName, end, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class CopiedMethod
|
||||
{
|
||||
private Method obMethod;
|
||||
private boolean hasGarbageValue;
|
||||
|
||||
private CopiedMethod(Method obMethod, boolean hasGarbageValue)
|
||||
{
|
||||
this.obMethod = obMethod;
|
||||
this.hasGarbageValue = hasGarbageValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package net.runelite.injector.raw;
|
||||
|
||||
import java.util.ListIterator;
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.Method;
|
||||
import net.runelite.asm.attributes.Code;
|
||||
import net.runelite.asm.attributes.code.Instruction;
|
||||
import net.runelite.asm.attributes.code.Instructions;
|
||||
import net.runelite.asm.attributes.code.instructions.ILoad;
|
||||
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
|
||||
import net.runelite.asm.attributes.code.instructions.LDC;
|
||||
import net.runelite.asm.pool.Class;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import net.runelite.injector.Inject;
|
||||
import net.runelite.injector.InjectUtil;
|
||||
import net.runelite.injector.InjectionException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ClearColorBuffer
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(ClearColorBuffer.class);
|
||||
private static final net.runelite.asm.pool.Method clearBuffer = new net.runelite.asm.pool.Method(
|
||||
new Class("net.runelite.client.callback.Hooks"),
|
||||
"clearColorBuffer",
|
||||
new Signature("(IIIII)V")
|
||||
);
|
||||
private final Inject inject;
|
||||
|
||||
public ClearColorBuffer(Inject inject)
|
||||
{
|
||||
this.inject = inject;
|
||||
}
|
||||
|
||||
public void inject() throws InjectionException
|
||||
{
|
||||
injectColorBufferHooks();
|
||||
}
|
||||
|
||||
private void injectColorBufferHooks() throws InjectionException
|
||||
{
|
||||
net.runelite.asm.pool.Method fillRectangle = InjectUtil.findStaticMethod(inject, "Rasterizer2D_fillRectangle").getPoolMethod();
|
||||
|
||||
int count = 0;
|
||||
int replaced = 0;
|
||||
|
||||
for (ClassFile cf : inject.getVanilla().getClasses())
|
||||
{
|
||||
for (Method m : cf.getMethods())
|
||||
{
|
||||
if (!m.isStatic())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Code c = m.getCode();
|
||||
if (c == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Instructions ins = c.getInstructions();
|
||||
ListIterator<Instruction> it = ins.getInstructions().listIterator();
|
||||
|
||||
for (; it.hasNext(); )
|
||||
{
|
||||
Instruction i = it.next();
|
||||
if (!(i instanceof InvokeStatic))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!((InvokeStatic) i).getMethod().equals(fillRectangle))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int indexToReturnTo = it.nextIndex();
|
||||
count++;
|
||||
it.previous();
|
||||
Instruction current = it.previous();
|
||||
if (current instanceof LDC && ((LDC) current).getConstantAsInt() == 0)
|
||||
{
|
||||
int varIdx = 0;
|
||||
for (; ; )
|
||||
{
|
||||
current = it.previous();
|
||||
if (current instanceof ILoad && ((ILoad) current).getVariableIndex() == 3 - varIdx)
|
||||
{
|
||||
varIdx++;
|
||||
log.debug(varIdx + " we can count yay");
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (varIdx == 4)
|
||||
{
|
||||
for (; !(current instanceof InvokeStatic); )
|
||||
{
|
||||
current = it.next();
|
||||
}
|
||||
assert it.nextIndex() == indexToReturnTo;
|
||||
|
||||
it.set(new InvokeStatic(ins, clearBuffer));
|
||||
replaced++;
|
||||
log.debug("Found drawRectangle at {}. Found: {}, replaced {}", m.getName(), count, replaced);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.debug("Welp, guess this wasn't it chief " + m);
|
||||
}
|
||||
}
|
||||
|
||||
while (it.nextIndex() != indexToReturnTo)
|
||||
{
|
||||
it.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,264 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.injector.raw;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Set;
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.Method;
|
||||
import net.runelite.asm.attributes.code.Instruction;
|
||||
import net.runelite.asm.attributes.code.Instructions;
|
||||
import net.runelite.asm.attributes.code.Label;
|
||||
import net.runelite.asm.attributes.code.instruction.types.JumpingInstruction;
|
||||
import net.runelite.asm.attributes.code.instruction.types.PushConstantInstruction;
|
||||
import net.runelite.asm.attributes.code.instructions.GetStatic;
|
||||
import net.runelite.asm.attributes.code.instructions.IMul;
|
||||
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import net.runelite.injector.Inject;
|
||||
import static net.runelite.injector.InjectHookMethod.HOOKS;
|
||||
import static net.runelite.injector.InjectUtil.findStaticMethod;
|
||||
import net.runelite.injector.InjectionException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DrawAfterWidgets
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(DrawAfterWidgets.class);
|
||||
|
||||
private final Inject inject;
|
||||
|
||||
public DrawAfterWidgets(Inject inject)
|
||||
{
|
||||
this.inject = inject;
|
||||
}
|
||||
|
||||
public void inject() throws InjectionException
|
||||
{
|
||||
injectDrawAfterWidgets();
|
||||
}
|
||||
|
||||
private void injectDrawAfterWidgets() throws InjectionException
|
||||
{
|
||||
/*
|
||||
This call has to be injected using raw injection because the
|
||||
drawWidgets method gets inlined in some revisions. If it wouldn't be,
|
||||
mixins would be used to add the call to the end of drawWidgets.
|
||||
|
||||
--> This hook depends on the positions of "if (535573958 * kl != -1)" and "jz.db();".
|
||||
|
||||
|
||||
Revision 180 - client.gs():
|
||||
______________________________________________________
|
||||
|
||||
@Export("drawLoggedIn")
|
||||
final void drawLoggedIn() {
|
||||
if(rootInterface != -1) {
|
||||
ClientPreferences.method1809(rootInterface);
|
||||
}
|
||||
|
||||
int var1;
|
||||
for(var1 = 0; var1 < rootWidgetCount; ++var1) {
|
||||
if(__client_od[var1]) {
|
||||
__client_ot[var1] = true;
|
||||
}
|
||||
|
||||
__client_oq[var1] = __client_od[var1];
|
||||
__client_od[var1] = false;
|
||||
}
|
||||
|
||||
__client_oo = cycle;
|
||||
__client_lq = -1;
|
||||
__client_ln = -1;
|
||||
UserComparator6.__fg_jh = null;
|
||||
if(rootInterface != -1) {
|
||||
rootWidgetCount = 0;
|
||||
Interpreter.method1977(rootInterface, 0, 0, SoundCache.canvasWidth, Huffman.canvasHeight, 0, 0, -1);
|
||||
}
|
||||
|
||||
< -- here appearantly
|
||||
|
||||
Rasterizer2D.Rasterizer2D_resetClip();
|
||||
______________________________________________________
|
||||
*/
|
||||
|
||||
boolean injected = false;
|
||||
|
||||
Method noClip = findStaticMethod(inject, "Rasterizer2D_resetClip"); // !!!!!
|
||||
|
||||
if (noClip == null)
|
||||
{
|
||||
throw new InjectionException("Mapped method \"Rasterizer2D_resetClip\" could not be found.");
|
||||
}
|
||||
|
||||
net.runelite.asm.pool.Method poolNoClip = noClip.getPoolMethod();
|
||||
|
||||
for (ClassFile c : inject.getVanilla().getClasses())
|
||||
{
|
||||
for (Method m : c.getMethods())
|
||||
{
|
||||
if (m.getCode() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Instructions instructions = m.getCode().getInstructions();
|
||||
|
||||
Set<Label> labels = new HashSet<>();
|
||||
|
||||
// Let's find "invokestatic <some class>.noClip()" and its label
|
||||
ListIterator<Instruction> labelIterator = instructions.getInstructions().listIterator();
|
||||
while (labelIterator.hasNext())
|
||||
{
|
||||
Instruction i = labelIterator.next();
|
||||
|
||||
if (!(i instanceof InvokeStatic))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
InvokeStatic is = (InvokeStatic) i;
|
||||
|
||||
if (!is.getMethod().equals(poolNoClip))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
labelIterator.previous();
|
||||
Instruction i2 = labelIterator.previous();
|
||||
labelIterator.next();
|
||||
labelIterator.next();
|
||||
|
||||
// Find the label that marks the code path for the instruction
|
||||
if (!(i2 instanceof Label))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// There can be several noClip invocations in a method, so let's catch them all
|
||||
labels.add((Label) i2);
|
||||
}
|
||||
|
||||
if (labels.isEmpty())
|
||||
{
|
||||
// If we get here, we're either in the wrong method
|
||||
// or Jagex has removed the "if (535573958 * kl != -1)"
|
||||
// logger.debug("Could not find the label for jumping to the " + noClip + " call in " + m);
|
||||
continue;
|
||||
}
|
||||
|
||||
Set<Label> labelsToInjectAfter = new HashSet<>();
|
||||
|
||||
ListIterator<Instruction> jumpIterator = instructions.getInstructions().listIterator();
|
||||
while (jumpIterator.hasNext())
|
||||
{
|
||||
Instruction i = jumpIterator.next();
|
||||
|
||||
if (!(i instanceof JumpingInstruction))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
JumpingInstruction ji = (JumpingInstruction) i;
|
||||
|
||||
Label label = null;
|
||||
|
||||
for (Label l : labels)
|
||||
{
|
||||
if (ji.getJumps().contains(l))
|
||||
{
|
||||
label = l;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (label == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
jumpIterator.previous();
|
||||
|
||||
Set<Instruction> insns = new HashSet<>();
|
||||
insns.add(jumpIterator.previous());
|
||||
insns.add(jumpIterator.previous());
|
||||
insns.add(jumpIterator.previous());
|
||||
insns.add(jumpIterator.previous());
|
||||
|
||||
// Get the iterator back to i's position
|
||||
jumpIterator.next();
|
||||
jumpIterator.next();
|
||||
jumpIterator.next();
|
||||
jumpIterator.next();
|
||||
jumpIterator.next();
|
||||
|
||||
/*
|
||||
Check that these instruction types are passed into the if-statement:
|
||||
|
||||
ICONST_M1
|
||||
GETSTATIC client.kr : I
|
||||
LDC 634425425
|
||||
IMUL
|
||||
|
||||
We cannot depend on the order of these because of the obfuscation,
|
||||
so let's make it easier by just checking that they are there.
|
||||
*/
|
||||
if (insns.stream().filter(i2 -> i2 instanceof PushConstantInstruction).count() != 2
|
||||
|| insns.stream().filter(i2 -> i2 instanceof IMul).count() != 1
|
||||
|| insns.stream().filter(i2 -> i2 instanceof GetStatic).count() != 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// At this point, we have found the real injection point
|
||||
labelsToInjectAfter.add(label);
|
||||
}
|
||||
|
||||
for (Label l : labelsToInjectAfter)
|
||||
{
|
||||
InvokeStatic invoke = new InvokeStatic(instructions,
|
||||
new net.runelite.asm.pool.Method(
|
||||
new net.runelite.asm.pool.Class(HOOKS),
|
||||
"drawAfterWidgets",
|
||||
new Signature("()V")
|
||||
)
|
||||
);
|
||||
|
||||
instructions.addInstruction(instructions.getInstructions().indexOf(l) + 1, invoke);
|
||||
|
||||
logger.info("injectDrawAfterWidgets injected a call after " + l);
|
||||
|
||||
injected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!injected)
|
||||
{
|
||||
throw new InjectionException("injectDrawAfterWidgets failed to inject!");
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user