Merge pull request #5 from Ganom/master

fork: rebase.
This commit is contained in:
Ganom
2019-11-07 02:34:34 -05:00
committed by GitHub
712 changed files with 53505 additions and 52997 deletions

View File

@@ -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) * 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 ```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: * Make your changes in a new git branch:

25
.github/release-drafter.yml vendored Normal file
View 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

View File

@@ -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 }}"

View File

@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@v1
- name: Make gradlew executable - name: Make gradlew executable
run: chmod +x ./gradlew run: chmod +x ./gradlew
- name: Update Gradle Wrapper - name: Update Gradle Wrapper
@@ -22,23 +22,23 @@ jobs:
PULL_REQUEST_TITLE: 'project: Update gradle wrapper' PULL_REQUEST_TITLE: 'project: Update gradle wrapper'
PULL_REQUEST_BODY: This is an auto-generated PR with an updated gradle version PULL_REQUEST_BODY: This is an auto-generated PR with an updated gradle version
COMMIT_MESSAGE: 'project: Update gradle wrapper' COMMIT_MESSAGE: 'project: Update gradle wrapper'
PULL_REQUEST_LABELS: automated pull request, gradle PULL_REQUEST_LABELS: automated-pull-request, gradle
update-dependencies: update-dependencies:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@v1
- name: Make gradlew executable - name: Make gradlew executable
run: chmod +x ./gradlew run: chmod +x ./gradlew
- name: Update Gradle dependencies - name: Update Gradle dependencies
run: ./gradlew useLatestVersions --console=plain run: ./gradlew useLatestVersions --console=plain
- name: Create Gradle dependencies update Pull Request - name: Create Gradle dependencies update Pull Request
uses: Owain94/create-pull-request@master uses: Owain94/create-pull-request@master
env: env:
GITHUB_TOKEN: ${{ secrets.OpenOSRS }} GITHUB_TOKEN: ${{ secrets.OpenOSRS }}
PULL_REQUEST_BRANCH: GRADLE-DEPENDENCY-UPDATE PULL_REQUEST_BRANCH: GRADLE-DEPENDENCY-UPDATE
PULL_REQUEST_TITLE: 'project: Update gradle dependencies' PULL_REQUEST_TITLE: 'project: Update gradle dependencies'
PULL_REQUEST_BODY: This is an auto-generated PR with an updated gradle dependencies versions PULL_REQUEST_BODY: This is an auto-generated PR with an updated gradle dependencies versions
COMMIT_MESSAGE: 'project: Update gradle dependencies' COMMIT_MESSAGE: 'project: Update gradle dependencies'
PULL_REQUEST_LABELS: automated pull request, gradle PULL_REQUEST_LABELS: automated-pull-request, gradle

15
.github/workflows/greetings.yml vendored Normal file
View 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
View 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
View 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
View 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 }}

View File

@@ -9,9 +9,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@v1
- name: Set up JDK 11 - name: Set up JDK 11
uses: actions/setup-java@master uses: actions/setup-java@v1
with: with:
java-version: 11 java-version: 11
- name: Make gradlew executable - name: Make gradlew executable
@@ -25,7 +25,7 @@ jobs:
- name: Building scraper - name: Building scraper
run: ./gradlew :wiki-scraper:build --console=plain run: ./gradlew :wiki-scraper:build --console=plain
- name: Downloading jagex cache - name: Downloading jagex cache
run: ./gradlew :cache-client:update --console=plain run: ./gradlew :cache-client:download --console=plain
- name: Scraping NPC stats - name: Scraping NPC stats
run: ./gradlew :wiki-scraper:npcStatsScrape --console=plain run: ./gradlew :wiki-scraper:npcStatsScrape --console=plain
- name: Create NPC stats Pull Request - name: Create NPC stats Pull Request
@@ -36,4 +36,4 @@ jobs:
PULL_REQUEST_TITLE: 'Client: Update NPC stats' PULL_REQUEST_TITLE: 'Client: Update NPC stats'
PULL_REQUEST_BODY: This is an auto-generated PR with changes from the OSRS wiki PULL_REQUEST_BODY: This is an auto-generated PR with changes from the OSRS wiki
COMMIT_MESSAGE: 'Client: Update NPC stats' COMMIT_MESSAGE: 'Client: Update NPC stats'
PULL_REQUEST_LABELS: automated pull request, NPC stats PULL_REQUEST_LABELS: automated-pull-request, NPC stats

View File

@@ -19,4 +19,4 @@ jobs:
exempt-issue-label: 'awaiting-approval' exempt-issue-label: 'awaiting-approval'
exempt-pr-label: 'awaiting-approval' exempt-pr-label: 'awaiting-approval'
days-before-stale: 60 days-before-stale: 60
days-before-close: 30 days-before-close: 30

View File

@@ -3,7 +3,7 @@
# OpenOSRS # OpenOSRS
[![Build Status](https://github.com/open-osrs/runelite/workflows/OpenOSRS%20-%20CI/badge.svg)](https://github.com/open-osrs/runelite/actions) [![Build Status](https://github.com/open-osrs/runelite/workflows/OpenOSRS%20-%20CI%20(push)/badge.svg)](https://github.com/open-osrs/runelite/actions?query=workflow%3A%22OpenOSRS+-+CI+%28push%29%22)
[![HitCount](http://hits.dwyl.io/open-osrs/runelite.svg)](http://hits.dwyl.io/open-osrs/runelite) [![HitCount](http://hits.dwyl.io/open-osrs/runelite.svg)](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. [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 ## 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). 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 [![YourKit](https://www.yourkit.com/images/yklogo.png)](https://www.yourkit.com/java/profiler/)

View File

@@ -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
View 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")
}
}
}
}
}
}

View File

@@ -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
View 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)
}

View File

@@ -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()
}
}
}
}
}

View File

@@ -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[])
}
}

View 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}/")
}
}
}
}
}

View 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)
}
}
}

View 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}"
}

View 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()
}
}
}
}
}

View 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())
}
}

View 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()
}

View File

@@ -1 +0,0 @@
implementation-class=FernflowerPlugin

View File

@@ -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()
}
}

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2019 ThatGamerBlue * Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * 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 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
description = 'Injected Client'
compileJava { description = "Cache Client"
dependsOn ':injector-plugin:assemble'
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() { classpath = project.sourceSets.main.get().runtimeClasspath
copy { main = "net.runelite.cache.client.CacheClient"
File f = file("build/classes/java/main") args(listOf(ProjectVersions.rsversion))
f.deleteDir()
f.mkdirs()
from ("${injectedClassesPath}")
into ("build/classes/java/main")
} }
} }
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

View File

@@ -85,7 +85,7 @@ public class CacheClient implements AutoCloseable
private CompletableFuture<HandshakeResponseType> handshakeFuture; private CompletableFuture<HandshakeResponseType> handshakeFuture;
private final Queue<PendingFileRequest> requests = new ArrayDeque<>(); 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"); Path path = Paths.get(System.getProperty("user.home"), "jagexcache" + File.separator + "oldschool" + File.separator + "LIVE");
final File jagexcache = new File(String.valueOf(path)); final File jagexcache = new File(String.valueOf(path));
@@ -95,7 +95,7 @@ public class CacheClient implements AutoCloseable
try (Store store = new Store(jagexcache)) try (Store store = new Store(jagexcache))
{ {
store.load(); store.load();
CacheClient c = new CacheClient(store, clientRevision); CacheClient c = new CacheClient(store, Integer.parseInt(args[0]));
c.connect(); c.connect();
CompletableFuture<HandshakeResponseType> handshake = c.handshake(); CompletableFuture<HandshakeResponseType> handshake = c.handshake();
handshake.get(); handshake.get();

View File

@@ -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')
}

View 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
View File

@@ -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()
])
}
}

View File

@@ -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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * 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. * 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; plugins {
import net.runelite.asm.Type; id(Plugins.jarTest.first) version Plugins.jarTest.second
import net.runelite.asm.attributes.annotation.Annotation; }
import net.runelite.asm.attributes.annotation.Element;
import org.objectweb.asm.AnnotationVisitor; description = "Cache"
import org.objectweb.asm.Opcodes;
dependencies {
public class ClassAnnotationVisitor extends AnnotationVisitor annotationProcessor(Libraries.lombok)
{
private final ClassFile classFile; api(project(":http-api"))
private final Type type;
private final Annotation annotation; compileOnly(Libraries.lombok)
public ClassAnnotationVisitor(ClassFile classFile, Type type) implementation(Libraries.gson)
{ implementation(Libraries.guava)
super(Opcodes.ASM5); implementation(Libraries.commonsCli)
implementation(Libraries.nettyBuffer)
this.classFile = classFile; implementation(Libraries.antlr)
this.type = type; implementation(Libraries.apacheCommonsCompress)
implementation(Libraries.slf4jApi)
annotation = new Annotation(classFile.getAnnotations());
annotation.setType(type); testImplementation(Libraries.junit)
} testImplementation(group = "net.runelite.rs", name = "cache", version = "${ProjectVersions.cacheversion}")
testImplementation(Libraries.slf4jSimple)
@Override }
public void visit(String name, Object value)
{ tasks {
Element element = new Element(annotation); "processTestResources"(ProcessResources::class) {
val tokens = mapOf(
element.setName(name); "rs.version" to ProjectVersions.rsversion.toString(),
element.setValue(value); "cache.version" to ProjectVersions.cacheversion.toString()
)
annotation.addElement(element);
} inputs.properties(tokens)
@Override from("src/test/resources") {
public void visitEnd() include("cache.properties")
{
classFile.getAnnotations().addAnnotation(annotation); filter<ReplaceTokens>("tokens" to tokens)
} }
}
} }

View File

@@ -31,38 +31,37 @@ import lombok.Data;
@Data @Data
public class NpcDefinition public class NpcDefinition
{ {
public final int id; public final int id;
public short[] recolorToFind;
public int rotation = 32;
public String name = "null"; public String name = "null";
public short[] recolorToReplace; public int size = 1;
public int[] models; public int[] models;
public int[] models_2; public int[] chatheadModels;
public int stanceAnimation = -1; public int standingAnimation = -1;
public int anInt2165 = -1; public int rotateLeftAnimation = -1;
public int tileSpacesOccupied = 1; public int rotateRightAnimation = -1;
public int walkAnimation = -1; public int walkingAnimation = -1;
public short[] retextureToReplace;
public int rotate90RightAnimation = -1;
public boolean aBool2170 = true;
public int resizeX = 128;
public int contrast = 0;
public int rotate180Animation = -1; public int rotate180Animation = -1;
public int varbitIndex = -1; public int rotate90RightAnimation = -1;
public String[] options = new String[5];
public boolean renderOnMinimap = true;
public int combatLevel = -1;
public int rotate90LeftAnimation = -1; public int rotate90LeftAnimation = -1;
public int resizeY = 128; public short[] recolorToFind;
public boolean hasRenderPriority = false; public short[] recolorToReplace;
public int ambient = 0;
public int headIcon = -1;
public int[] configs;
public short[] retextureToFind; 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 int varpIndex = -1;
public boolean isClickable = true; public boolean isInteractable = true;
public int anInt2189 = -1; public boolean rotationFlag = true;
public boolean aBool2190 = false; public boolean isPet;
public Map<Integer, Object> params = null; public Map<Integer, Object> params;
} }

View File

@@ -24,13 +24,12 @@
*/ */
package net.runelite.cache.definitions.loaders; package net.runelite.cache.definitions.loaders;
import java.util.HashMap;
import net.runelite.cache.definitions.NpcDefinition; import net.runelite.cache.definitions.NpcDefinition;
import net.runelite.cache.io.InputStream; import net.runelite.cache.io.InputStream;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.HashMap;
public class NpcLoader public class NpcLoader
{ {
private static final Logger logger = LoggerFactory.getLogger(NpcLoader.class); private static final Logger logger = LoggerFactory.getLogger(NpcLoader.class);
@@ -67,7 +66,6 @@ public class NpcLoader
{ {
def.models[index] = stream.readUnsignedShort(); def.models[index] = stream.readUnsignedShort();
} }
} }
else if (opcode == 2) else if (opcode == 2)
{ {
@@ -75,37 +73,37 @@ public class NpcLoader
} }
else if (opcode == 12) else if (opcode == 12)
{ {
def.tileSpacesOccupied = stream.readUnsignedByte(); def.size = stream.readUnsignedByte();
} }
else if (opcode == 13) else if (opcode == 13)
{ {
def.stanceAnimation = stream.readUnsignedShort(); def.standingAnimation = stream.readUnsignedShort();
} }
else if (opcode == 14) else if (opcode == 14)
{ {
def.walkAnimation = stream.readUnsignedShort(); def.walkingAnimation = stream.readUnsignedShort();
} }
else if (opcode == 15) else if (opcode == 15)
{ {
def.anInt2165 = stream.readUnsignedShort(); def.rotateLeftAnimation = stream.readUnsignedShort();
} }
else if (opcode == 16) else if (opcode == 16)
{ {
def.anInt2189 = stream.readUnsignedShort(); def.rotateRightAnimation = stream.readUnsignedShort();
} }
else if (opcode == 17) else if (opcode == 17)
{ {
def.walkAnimation = stream.readUnsignedShort(); def.walkingAnimation = stream.readUnsignedShort();
def.rotate180Animation = stream.readUnsignedShort(); def.rotate180Animation = stream.readUnsignedShort();
def.rotate90RightAnimation = stream.readUnsignedShort(); def.rotate90RightAnimation = stream.readUnsignedShort();
def.rotate90LeftAnimation = stream.readUnsignedShort(); def.rotate90LeftAnimation = stream.readUnsignedShort();
} }
else if (opcode >= 30 && opcode < 35) else if (opcode >= 30 && opcode < 35)
{ {
def.options[opcode - 30] = stream.readString(); def.actions[opcode - 30] = stream.readString();
if (def.options[opcode - 30].equalsIgnoreCase("Hidden")) if (def.actions[opcode - 30].equalsIgnoreCase("Hidden"))
{ {
def.options[opcode - 30] = null; def.actions[opcode - 30] = null;
} }
} }
else if (opcode == 40) else if (opcode == 40)
@@ -137,17 +135,17 @@ public class NpcLoader
else if (opcode == 60) else if (opcode == 60)
{ {
length = stream.readUnsignedByte(); length = stream.readUnsignedByte();
def.models_2 = new int[length]; def.chatheadModels = new int[length];
for (index = 0; index < length; ++index) for (index = 0; index < length; ++index)
{ {
def.models_2[index] = stream.readUnsignedShort(); def.chatheadModels[index] = stream.readUnsignedShort();
} }
} }
else if (opcode == 93) else if (opcode == 93)
{ {
def.renderOnMinimap = false; def.isMinimapVisible = false;
} }
else if (opcode == 95) else if (opcode == 95)
{ {
@@ -155,11 +153,11 @@ public class NpcLoader
} }
else if (opcode == 97) else if (opcode == 97)
{ {
def.resizeX = stream.readUnsignedShort(); def.widthScale = stream.readUnsignedShort();
} }
else if (opcode == 98) else if (opcode == 98)
{ {
def.resizeY = stream.readUnsignedShort(); def.heightScale = stream.readUnsignedShort();
} }
else if (opcode == 99) else if (opcode == 99)
{ {
@@ -179,18 +177,18 @@ public class NpcLoader
} }
else if (opcode == 103) else if (opcode == 103)
{ {
def.rotation = stream.readUnsignedShort(); def.rotationSpeed = stream.readUnsignedShort();
} }
else if (opcode == 106) else if (opcode == 106)
{ {
def.varbitIndex = stream.readUnsignedShort(); def.varbitId = stream.readUnsignedShort();
if ('\uffff' == def.varbitIndex) if (def.varbitId == 65535)
{ {
def.varbitIndex = -1; def.varbitId = -1;
} }
def.varpIndex = stream.readUnsignedShort(); def.varpIndex = stream.readUnsignedShort();
if ('\uffff' == def.varpIndex) if (def.varpIndex == 65535)
{ {
def.varpIndex = -1; def.varpIndex = -1;
} }
@@ -212,26 +210,26 @@ public class NpcLoader
} }
else if (opcode == 107) else if (opcode == 107)
{ {
def.isClickable = false; def.isInteractable = false;
} }
else if (opcode == 109) else if (opcode == 109)
{ {
def.aBool2170 = false; def.rotationFlag = false;
} }
else if (opcode == 111) else if (opcode == 111)
{ {
def.aBool2190 = true; def.isPet = true;
} }
else if (opcode == 118) else if (opcode == 118)
{ {
def.varbitIndex = stream.readUnsignedShort(); def.varbitId = stream.readUnsignedShort();
if ('\uffff' == def.varbitIndex) if (def.varbitId == 65535)
{ {
def.varbitIndex = -1; def.varbitId = -1;
} }
def.varpIndex = stream.readUnsignedShort(); def.varpIndex = stream.readUnsignedShort();
if ('\uffff' == def.varpIndex) if (def.varpIndex == 65535)
{ {
def.varpIndex = -1; def.varpIndex = -1;
} }

View File

@@ -47,45 +47,45 @@ public class NpcSaver
out.writeByte(2); out.writeByte(2);
out.writeString(npc.name); out.writeString(npc.name);
} }
if (npc.tileSpacesOccupied != 1) if (npc.size != 1)
{ {
out.writeByte(12); out.writeByte(12);
out.writeByte(npc.tileSpacesOccupied); out.writeByte(npc.size);
} }
if (npc.stanceAnimation != -1) if (npc.standingAnimation != -1)
{ {
out.writeByte(13); out.writeByte(13);
out.writeShort(npc.stanceAnimation); out.writeShort(npc.standingAnimation);
} }
if (npc.walkAnimation != -1) if (npc.walkingAnimation != -1)
{ {
out.writeByte(14); out.writeByte(14);
out.writeShort(npc.walkAnimation); out.writeShort(npc.walkingAnimation);
} }
if (npc.anInt2165 != -1) if (npc.rotateLeftAnimation != -1)
{ {
out.writeByte(15); out.writeByte(15);
out.writeShort(npc.anInt2165); out.writeShort(npc.rotateLeftAnimation);
} }
if (npc.anInt2189 != -1) if (npc.rotateRightAnimation != -1)
{ {
out.writeByte(16); out.writeByte(16);
out.writeShort(npc.anInt2189); out.writeShort(npc.rotateRightAnimation);
} }
if (npc.rotate180Animation != -1 || npc.rotate90LeftAnimation != -1 || npc.rotate90RightAnimation != -1) if (npc.rotate180Animation != -1 || npc.rotate90LeftAnimation != -1 || npc.rotate90RightAnimation != -1)
{ {
out.writeByte(17); out.writeByte(17);
out.writeShort(npc.walkAnimation); out.writeShort(npc.walkingAnimation);
out.writeShort(npc.rotate180Animation); out.writeShort(npc.rotate180Animation);
out.writeShort(npc.rotate90RightAnimation); out.writeShort(npc.rotate90RightAnimation);
out.writeShort(npc.rotate90LeftAnimation); out.writeShort(npc.rotate90LeftAnimation);
} }
for (int i = 0; i < 5; ++i) for (int i = 0; i < 5; ++i)
{ {
if (npc.options[i] != null) if (npc.actions[i] != null)
{ {
out.writeByte(30 + i); out.writeByte(30 + i);
out.writeString(npc.options[i]); out.writeString(npc.actions[i]);
} }
} }
if (npc.recolorToFind != null && npc.recolorToReplace != null) if (npc.recolorToFind != null && npc.recolorToReplace != null)
@@ -108,16 +108,16 @@ public class NpcSaver
out.writeShort(npc.retextureToReplace[i]); out.writeShort(npc.retextureToReplace[i]);
} }
} }
if (npc.models_2 != null) if (npc.chatheadModels != null)
{ {
out.writeByte(60); out.writeByte(60);
out.writeByte(npc.models_2.length); out.writeByte(npc.chatheadModels.length);
for (int modelId : npc.models_2) for (int modelId : npc.chatheadModels)
{ {
out.writeShort(modelId); out.writeShort(modelId);
} }
} }
if (!npc.renderOnMinimap) if (!npc.isMinimapVisible)
{ {
out.writeByte(93); out.writeByte(93);
} }
@@ -127,9 +127,9 @@ public class NpcSaver
out.writeShort(npc.combatLevel); out.writeShort(npc.combatLevel);
} }
out.writeByte(97); out.writeByte(97);
out.writeShort(npc.resizeX); out.writeShort(npc.widthScale);
out.writeByte(98); out.writeByte(98);
out.writeShort(npc.resizeY); out.writeShort(npc.heightScale);
if (npc.hasRenderPriority) if (npc.hasRenderPriority)
{ {
out.writeByte(99); out.writeByte(99);
@@ -144,23 +144,23 @@ public class NpcSaver
out.writeShort(npc.headIcon); out.writeShort(npc.headIcon);
} }
out.writeByte(103); out.writeByte(103);
out.writeShort(npc.rotation); out.writeShort(npc.rotationSpeed);
if (!npc.isClickable) if (!npc.isInteractable)
{ {
out.writeByte(107); out.writeByte(107);
} }
if (!npc.aBool2170) if (!npc.rotationFlag)
{ {
out.writeByte(109); out.writeByte(109);
} }
if (npc.aBool2190) if (npc.isPet)
{ {
out.writeByte(111); out.writeByte(111);
} }
if (npc.configs != null) if (npc.configs != null)
{ {
out.writeByte(118); out.writeByte(118);
out.writeShort(npc.varbitIndex); out.writeShort(npc.varbitId);
out.writeShort(npc.varpIndex); out.writeShort(npc.varpIndex);
int[] c = npc.configs; int[] c = npc.configs;

View File

@@ -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()
}
}

View 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)
}
}
}

View 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();
}
}

View File

@@ -31,13 +31,12 @@ import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.pool.Class; import net.runelite.asm.pool.Class;
import net.runelite.asm.signature.Signature; import net.runelite.asm.signature.Signature;
import static net.runelite.deob.DeobAnnotations.*; import static net.runelite.deob.DeobAnnotations.*;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
public class ClassFile public class ClassFile implements Annotated, Named
{ {
private ClassGroup group; private ClassGroup group;
@@ -100,10 +99,9 @@ public class ClassFile
visitor.visit(version, access, name.getName(), null, super_class.getName(), ints); visitor.visit(version, access, name.getName(), null, super_class.getName(), ints);
visitor.visitSource(source, null); visitor.visitSource(source, null);
for (Annotation annotation : annotations.getAnnotations()) for (Annotation annotation : annotations)
{ {
AnnotationVisitor av = visitor.visitAnnotation(annotation.getType().toString(), true); annotation.accept(visitor.visitAnnotation(annotation.getType().toString(), true));
annotation.accept(av);
} }
for (Field field : fields) for (Field field : fields)

View File

@@ -27,13 +27,16 @@ package net.runelite.asm;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer;
import net.runelite.asm.attributes.Code; import net.runelite.asm.attributes.Code;
import net.runelite.asm.signature.Signature; import net.runelite.asm.signature.Signature;
import static net.runelite.deob.DeobAnnotations.*; 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 List<ClassFile> classes = new ArrayList<>(); // to keep order
private final Map<String, ClassFile> classMap = new HashMap<>(); private final Map<String, ClassFile> classMap = new HashMap<>();
@@ -156,4 +159,17 @@ public class ClassGroup
return findClass(name); 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);
}
} }

View File

@@ -27,15 +27,13 @@ package net.runelite.asm;
import net.runelite.asm.attributes.Annotations; import net.runelite.asm.attributes.Annotations;
import net.runelite.asm.attributes.annotation.Annotation; import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.deob.DeobAnnotations; import net.runelite.deob.DeobAnnotations;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE; import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PROTECTED; import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC; 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; public static final int ACCESS_MODIFIERS = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED;
@@ -53,15 +51,14 @@ public class Field
this.name = name; this.name = name;
this.type = type; this.type = type;
annotations = new Annotations(); this.annotations = new Annotations();
} }
public void accept(FieldVisitor visitor) public void accept(FieldVisitor visitor)
{ {
for (Annotation annotation : annotations.getAnnotations()) for (Annotation annotation : annotations.getAnnotations())
{ {
AnnotationVisitor av = visitor.visitAnnotation(annotation.getType().toString(), true); annotation.accept(visitor.visitAnnotation(annotation.getType().toString(), true));
annotation.accept(av);
} }
visitor.visitEnd(); visitor.visitEnd();

View File

@@ -25,12 +25,14 @@
package net.runelite.asm; package net.runelite.asm;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import net.runelite.asm.pool.Class; import net.runelite.asm.pool.Class;
import net.runelite.deob.DeobAnnotations; import net.runelite.deob.DeobAnnotations;
import org.jetbrains.annotations.NotNull;
public class Interfaces public class Interfaces implements Iterable<Class>
{ {
private final ClassFile classFile; private final ClassFile classFile;
@@ -107,4 +109,10 @@ public class Interfaces
return names; return names;
} }
@NotNull
public Iterator<Class> iterator()
{
return this.interfaces.iterator();
}
} }

View File

@@ -36,7 +36,6 @@ import net.runelite.asm.attributes.code.Parameter;
import net.runelite.asm.attributes.code.instruction.types.LVTInstruction; import net.runelite.asm.attributes.code.instruction.types.LVTInstruction;
import net.runelite.asm.signature.Signature; import net.runelite.asm.signature.Signature;
import net.runelite.deob.DeobAnnotations; import net.runelite.deob.DeobAnnotations;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Label; import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.MethodVisitor;
import static org.objectweb.asm.Opcodes.ACC_FINAL; 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_STATIC;
import static org.objectweb.asm.Opcodes.ACC_SYNCHRONIZED; 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; public static final int ACCESS_MODIFIERS = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED;
@@ -92,8 +91,7 @@ public class Method
for (Annotation annotation : annotations.getAnnotations()) for (Annotation annotation : annotations.getAnnotations())
{ {
AnnotationVisitor av = visitor.visitAnnotation(annotation.getType().toString(), true); annotation.accept(visitor.visitAnnotation(annotation.getType().toString(), true));
annotation.accept(av);
} }
if (code != null) if (code != null)

View File

@@ -0,0 +1,6 @@
package net.runelite.asm;
public interface Named
{
String getName();
}

View File

@@ -26,13 +26,16 @@
package net.runelite.asm.attributes; package net.runelite.asm.attributes;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import net.runelite.asm.Type; import net.runelite.asm.Type;
import net.runelite.asm.attributes.annotation.Annotation; import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.attributes.annotation.Element; 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<>(); private final List<Annotation> annotations = new ArrayList<>();
@@ -40,7 +43,7 @@ public class Annotations
{ {
return annotations; return annotations;
} }
public void addAnnotation(Annotation annotation) public void addAnnotation(Annotation annotation)
{ {
annotations.add(annotation); annotations.add(annotation);
@@ -55,7 +58,7 @@ public class Annotations
{ {
annotations.clear(); annotations.clear();
} }
public Annotation find(Type type) public Annotation find(Type type)
{ {
for (Annotation a : annotations) for (Annotation a : annotations)
@@ -68,18 +71,22 @@ public class Annotations
{ {
return annotations.size(); return annotations.size();
} }
public Annotation addAnnotation(Type type, String name, Object value) public Annotation addAnnotation(Type type, String name, Object value)
{ {
Annotation annotation = new Annotation(this); Annotation annotation = new Annotation(type);
annotation.setType(type);
addAnnotation(annotation); addAnnotation(annotation);
Element element = new Element(annotation); Element element = new SimpleElement(name, value);
element.setName(name);
element.setValue(value);
annotation.addElement(element); annotation.addElement(element);
return annotation; return annotation;
} }
@NotNull
@Override
public Iterator<Annotation> iterator()
{
return this.annotations.iterator();
}
} }

View File

@@ -41,11 +41,11 @@ public class Code
private int maxStack; private int maxStack;
private Instructions instructions; private Instructions instructions;
private final Exceptions exceptions; private final Exceptions exceptions;
public Code(Method method) public Code(Method method)
{ {
this.method = method; this.method = method;
exceptions = new Exceptions(this); exceptions = new Exceptions(this);
instructions = new Instructions(this); instructions = new Instructions(this);
} }
@@ -59,12 +59,12 @@ public class Code
{ {
return maxStack; return maxStack;
} }
public void setMaxStack(int maxStack) public void setMaxStack(int maxStack)
{ {
this.maxStack = maxStack; this.maxStack = maxStack;
} }
private int getMaxLocalsFromSig() private int getMaxLocalsFromSig()
{ {
Method m = getMethod(); Method m = getMethod();
@@ -77,12 +77,11 @@ public class Code
/** /**
* calculates the size of the lvt required for this method * calculates the size of the lvt required for this method
* @return
*/ */
public int getMaxLocals() public int getMaxLocals()
{ {
int max = -1; int max = -1;
for (Instruction ins : instructions.getInstructions()) for (Instruction ins : instructions.getInstructions())
{ {
if (ins instanceof LVTInstruction) if (ins instanceof LVTInstruction)
@@ -96,19 +95,19 @@ public class Code
} }
} }
} }
int fromSig = getMaxLocalsFromSig(); int fromSig = getMaxLocalsFromSig();
if (fromSig > max) if (fromSig > max)
max = fromSig; max = fromSig;
return max; return max;
} }
public Exceptions getExceptions() public Exceptions getExceptions()
{ {
return exceptions; return exceptions;
} }
public Instructions getInstructions() public Instructions getInstructions()
{ {
return instructions; return instructions;

View File

@@ -26,30 +26,26 @@
package net.runelite.asm.attributes.annotation; package net.runelite.asm.attributes.annotation;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import net.runelite.asm.Type; import net.runelite.asm.Type;
import net.runelite.asm.attributes.Annotations; import org.jetbrains.annotations.NotNull;
import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.AnnotationVisitor;
public class Annotation public class Annotation extends Element<List<Element>> implements Iterable<Element>
{ {
private final Annotations annotations; private final Type type;
private Type type;
private final List<Element> elements = new ArrayList<>();
public Annotation(Annotations annotations) public Annotation(Type type)
{ {
this.annotations = annotations; this.value = new ArrayList<>();
this.type = type;
} }
public Annotations getAnnotations() public Annotation(String name, Type type)
{
return annotations;
}
public void setType(Type type)
{ {
this.value = new ArrayList<>();
this.name = name;
this.type = type; this.type = type;
} }
@@ -60,23 +56,44 @@ public class Annotation
public List<Element> getElements() public List<Element> getElements()
{ {
return elements; return value;
} }
public Element getElement() public Element getElement()
{ {
return elements.get(0); return value.get(0);
} }
public void addElement(Element element) 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) public void accept(AnnotationVisitor visitor)
{ {
for (Element element : elements) if (visitor == null)
visitor.visit(element.getName(), element.getValue()); {
return;
}
for (Element element : this)
{
accept(visitor, element.name, element.value);
}
visitor.visitEnd(); visitor.visitEnd();
} }
@NotNull
@Override
public Iterator<Element> iterator()
{
return this.value.iterator();
}
} }

View File

@@ -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();
}
}

View File

@@ -25,21 +25,14 @@
package net.runelite.asm.attributes.annotation; package net.runelite.asm.attributes.annotation;
public class Element import java.util.List;
{ import org.objectweb.asm.AnnotationVisitor;
private final Annotation annotation;
private String name;
private Object value;
public Element(Annotation annotation)
{
this.annotation = annotation;
}
public Annotation getAnnotation() public abstract class Element<T>
{ {
return annotation; String name = "value";
}
T value;
public String getName() public String getName()
{ {
@@ -51,18 +44,48 @@ public class Element
this.name = name; this.name = name;
} }
public Object getValue() public T getValue()
{ {
return value; return value;
} }
public void setValue(Object value) public void setValue(T value)
{ {
this.value = value; this.value = value;
} }
public String getString() public String getString()
{ {
return value.toString(); 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);
}
}
} }

View File

@@ -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;
}
}

View File

@@ -26,11 +26,14 @@ package net.runelite.asm.attributes.code;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.ListIterator;
import java.util.Map; import java.util.Map;
import net.runelite.asm.attributes.Code; 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 Code code;
private final List<Instruction> instructions = new ArrayList<>(); private final List<Instruction> instructions = new ArrayList<>();
@@ -186,4 +189,25 @@ public class Instructions
return i; 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);
}
} }

View File

@@ -52,8 +52,7 @@ public abstract class ArrayStore extends Instruction implements ArrayStoreInstru
if (r.getInstruction() instanceof GetFieldInstruction) if (r.getInstruction() instanceof GetFieldInstruction)
{ {
GetFieldInstruction gf = (GetFieldInstruction) r.getInstruction(); GetFieldInstruction gf = (GetFieldInstruction) r.getInstruction();
Field f = gf.getMyField(); return gf.getMyField();
return f;
} }
return null; return null;
@@ -89,7 +88,7 @@ public abstract class ArrayStore extends Instruction implements ArrayStoreInstru
Field f1 = gf1.getMyField(), Field f1 = gf1.getMyField(),
f2 = gf2.getMyField(); f2 = gf2.getMyField();
assert MappingExecutorUtil.isMaybeEqual(f1, f2); assert MappingExecutorUtil.isMaybeEqual(f1, f2);
if (f1 != null && f2 != null) if (f1 != null && f2 != null)

View File

@@ -24,7 +24,6 @@
*/ */
package net.runelite.asm.attributes.code.instructions; package net.runelite.asm.attributes.code.instructions;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import net.runelite.asm.ClassFile; import net.runelite.asm.ClassFile;
@@ -86,7 +85,7 @@ public class InvokeStatic extends Instruction implements InvokeInstruction
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public List<net.runelite.asm.Method> getMethods() 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 @Override

View File

@@ -70,10 +70,9 @@ public class MethodContext
return contexts.get(i); return contexts.get(i);
} }
@SuppressWarnings("unchecked")
public Collection<InstructionContext> getInstructionContexts() public Collection<InstructionContext> getInstructionContexts()
{ {
return (Collection) contexts.values(); return contexts.values();
} }
public void reset() public void reset()

View File

@@ -25,44 +25,59 @@
package net.runelite.asm.visitors; package net.runelite.asm.visitors;
import net.runelite.asm.Method;
import net.runelite.asm.Type; import net.runelite.asm.Type;
import net.runelite.asm.attributes.annotation.Annotation; 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.AnnotationVisitor;
import org.objectweb.asm.Opcodes; 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; private final Annotation annotation;
public MethodAnnotationVisitor(Method method, Type type) AnnotationElementVisitor(Annotation annotation)
{ {
super(Opcodes.ASM5); super(Opcodes.ASM5);
this.method = method; this.annotation = annotation;
this.type = type;
annotation = new Annotation(method.getAnnotations());
annotation.setType(type);
} }
@Override @Override
public void visit(String name, Object value) public void visit(String name, Object value)
{ {
Element element = new Element(annotation); SimpleElement element = new SimpleElement(name, value);
element.setName(name);
element.setValue(value);
annotation.addElement(element); annotation.addElement(element);
} }
@Override @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);
} }
} }

View File

@@ -22,12 +22,12 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package net.runelite.asm.visitors; package net.runelite.asm.visitors;
import net.runelite.asm.ClassFile; import net.runelite.asm.ClassFile;
import net.runelite.asm.Field; import net.runelite.asm.Field;
import net.runelite.asm.Type; import net.runelite.asm.Type;
import net.runelite.asm.attributes.annotation.Annotation;
import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute; import org.objectweb.asm.Attribute;
import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.FieldVisitor;
@@ -35,25 +35,25 @@ import org.objectweb.asm.Opcodes;
public class ClassFieldVisitor extends FieldVisitor public class ClassFieldVisitor extends FieldVisitor
{ {
private final ClassFile classFile;
private final Field field; 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); 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); cf.addField(field);
field.setAccessFlags(access);
field.setValue(value);
} }
@Override @Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) public AnnotationVisitor visitAnnotation(String desc, boolean visible)
{ {
Type type = new Type(desc); Annotation element = new Annotation(new Type(desc));
return new FieldAnnotationVisitor(field, type); this.field.getAnnotations().addAnnotation(element);
return new AnnotationElementVisitor(element);
} }
@Override @Override
@@ -61,10 +61,4 @@ public class ClassFieldVisitor extends FieldVisitor
{ {
System.out.println(attr); System.out.println(attr);
} }
@Override
public void visitEnd()
{
classFile.addField(field);
}
} }

View File

@@ -27,6 +27,7 @@ package net.runelite.asm.visitors;
import net.runelite.asm.ClassFile; import net.runelite.asm.ClassFile;
import net.runelite.asm.Type; import net.runelite.asm.Type;
import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.signature.Signature; import net.runelite.asm.signature.Signature;
import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassVisitor;
@@ -55,7 +56,7 @@ public class ClassFileVisitor extends ClassVisitor
classFile.setSuperName(superName); classFile.setSuperName(superName);
classFile.setVersion(version); classFile.setVersion(version);
classFile.setAccess(access); classFile.setAccess(access);
for (String inter : interfaces) for (String inter : interfaces)
classFile.getInterfaces().addInterface(new net.runelite.asm.pool.Class(inter)); classFile.getInterfaces().addInterface(new net.runelite.asm.pool.Class(inter));
} }
@@ -69,8 +70,10 @@ public class ClassFileVisitor extends ClassVisitor
@Override @Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) public AnnotationVisitor visitAnnotation(String desc, boolean visible)
{ {
Type type = new Type(desc); Annotation annotation = new Annotation(new Type(desc));
return new ClassAnnotationVisitor(classFile, type); classFile.getAnnotations().addAnnotation(annotation);
return new AnnotationElementVisitor(annotation);
} }
@Override @Override

View File

@@ -32,6 +32,7 @@ import net.runelite.asm.ClassFile;
import net.runelite.asm.Method; import net.runelite.asm.Method;
import net.runelite.asm.Type; import net.runelite.asm.Type;
import net.runelite.asm.attributes.Code; 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.Exceptions;
import net.runelite.asm.attributes.code.Instruction; import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.InstructionType; 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.ICONST_M1;
import static org.objectweb.asm.Opcodes.LCONST_0; import static org.objectweb.asm.Opcodes.LCONST_0;
import static org.objectweb.asm.Opcodes.LCONST_1; import static org.objectweb.asm.Opcodes.LCONST_1;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CodeVisitor extends MethodVisitor public class CodeVisitor extends MethodVisitor
{ {
private static final Logger logger = LoggerFactory.getLogger(CodeVisitor.class);
private final ClassFile classFile; private final ClassFile classFile;
private final Method method; private final Method method;
private Code code; 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); super(Opcodes.ASM5);
@@ -111,8 +108,9 @@ public class CodeVisitor extends MethodVisitor
@Override @Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) public AnnotationVisitor visitAnnotation(String desc, boolean visible)
{ {
Type type = new Type(desc); Annotation element = new Annotation(new Type(desc));
return new MethodAnnotationVisitor(method, type); this.method.getAnnotations().addAnnotation(element);
return new AnnotationElementVisitor(element);
} }
@Override @Override
@@ -333,7 +331,7 @@ public class CodeVisitor extends MethodVisitor
if (cst instanceof org.objectweb.asm.Type) if (cst instanceof org.objectweb.asm.Type)
{ {
org.objectweb.asm.Type t = (org.objectweb.asm.Type) cst; 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); LDC ldc = new LDC(code.getInstructions(), entry);

View File

@@ -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);
}
}

View File

@@ -95,7 +95,7 @@ public class DeobAnnotations
{ {
return null; return null;
} }
return (Number) an.getElement().getValue(); return (Number) an.getElement().getValue();
} }

View File

@@ -193,6 +193,7 @@ public class Renamer implements Deobfuscator
} }
@Override @Override
@SuppressWarnings("unchecked")
public void run(ClassGroup group) public void run(ClassGroup group)
{ {
group.buildClassGraph(); group.buildClassGraph();

View File

@@ -37,6 +37,7 @@ import net.runelite.asm.Method;
import net.runelite.asm.attributes.Annotations; import net.runelite.asm.attributes.Annotations;
import net.runelite.asm.attributes.annotation.Annotation; import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.attributes.annotation.Element; 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.Instruction;
import net.runelite.asm.attributes.code.InstructionType; import net.runelite.asm.attributes.code.InstructionType;
import net.runelite.asm.attributes.code.Instructions; import net.runelite.asm.attributes.code.Instructions;
@@ -113,7 +114,6 @@ public class ConstantParameter implements Deobfuscator
List<StackContext> pops = invokeCtx.getPops(); List<StackContext> pops = invokeCtx.getPops();
outer:
// object is popped first, then param 1, 2, 3, etc. double and long take two slots. // object is popped first, then param 1, 2, 3, etc. double and long take two slots.
for (int lvtOffset = offset, parameterIndex = 0; for (int lvtOffset = offset, parameterIndex = 0;
parameterIndex < method.getDescriptor().size(); parameterIndex < method.getDescriptor().size();
@@ -451,9 +451,7 @@ public class ConstantParameter implements Deobfuscator
} }
// Add garbage value // Add garbage value
Element element = new Element(obfuscatedSignature); Element element = new SimpleElement("garbageValue", value.toString());
element.setName("garbageValue");
element.setValue(value.toString());
obfuscatedSignature.addElement(element); obfuscatedSignature.addElement(element);
} }
} }
@@ -464,12 +462,12 @@ public class ConstantParameter implements Deobfuscator
public void run(ClassGroup group) public void run(ClassGroup group)
{ {
Execution execution = new Execution(group); Execution execution = new Execution(group);
execution.addExecutionVisitor(i -> findParameters(i)); execution.addExecutionVisitor(this::findParameters);
execution.populateInitialMethods(); execution.populateInitialMethods();
execution.run(); execution.run();
execution = new Execution(group); execution = new Execution(group);
execution.addMethodContextVisitor(mc -> findDeadParameters(mc)); execution.addMethodContextVisitor(this::findDeadParameters);
execution.populateInitialMethods(); execution.populateInitialMethods();
execution.run(); execution.run();

View File

@@ -32,6 +32,7 @@ import net.runelite.asm.Method;
import net.runelite.asm.attributes.Annotations; import net.runelite.asm.attributes.Annotations;
import net.runelite.asm.attributes.annotation.Annotation; import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.attributes.annotation.Element; import net.runelite.asm.attributes.annotation.Element;
import net.runelite.asm.attributes.annotation.SimpleElement;
import net.runelite.deob.DeobAnnotations; import net.runelite.deob.DeobAnnotations;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -39,7 +40,7 @@ import org.slf4j.LoggerFactory;
public class AnnotationMapper public class AnnotationMapper
{ {
private static final Logger logger = LoggerFactory.getLogger(AnnotationMapper.class); private static final Logger logger = LoggerFactory.getLogger(AnnotationMapper.class);
private final ClassGroup source, target; private final ClassGroup source, target;
private final ParallelExecutorMapping mapping; private final ParallelExecutorMapping mapping;
@@ -119,20 +120,17 @@ public class AnnotationMapper
if (from.getAnnotations() == null) if (from.getAnnotations() == null)
return count; return count;
for (Annotation a : from.getAnnotations()) for (Annotation a : from.getAnnotations())
{ {
if (isCopyable(a)) if (isCopyable(a))
{ {
Annotation annotation = new Annotation(to); Annotation annotation = new Annotation(a.getType());
annotation.setType(a.getType());
to.addAnnotation(annotation); to.addAnnotation(annotation);
for (Element e : a.getElements()) for (Element e : a.getElements())
{ {
Element element = new Element(annotation); Element element = new SimpleElement(e.getName(), e.getValue());
element.setName(e.getName());
element.setValue(e.getValue());
annotation.addElement(element); annotation.addElement(element);
} }
@@ -155,7 +153,6 @@ public class AnnotationMapper
private boolean isCopyable(Annotation a) private boolean isCopyable(Annotation a)
{ {
return a.getType().equals(DeobAnnotations.EXPORT) return a.getType().equals(DeobAnnotations.EXPORT)
|| a.getType().equals(DeobAnnotations.IMPLEMENTS) || a.getType().equals(DeobAnnotations.IMPLEMENTS);
|| a.getType().equals(DeobAnnotations.HOOK);
} }
} }

View File

@@ -8,6 +8,7 @@ import net.runelite.asm.Method;
import net.runelite.asm.attributes.Annotations; import net.runelite.asm.attributes.Annotations;
import net.runelite.asm.attributes.annotation.Annotation; import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.attributes.annotation.Element; import net.runelite.asm.attributes.annotation.Element;
import net.runelite.asm.attributes.annotation.SimpleElement;
import net.runelite.deob.Deob; import net.runelite.deob.Deob;
import net.runelite.deob.DeobAnnotations; import net.runelite.deob.DeobAnnotations;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -23,6 +24,7 @@ public class AnnotationAdder
private final ClassGroup group; private final ClassGroup group;
private final Logger log = LoggerFactory.getLogger(AnnotationAdder.class); private final Logger log = LoggerFactory.getLogger(AnnotationAdder.class);
@SuppressWarnings("unchecked")
public void run() public void run()
{ {
int impl = 0; int impl = 0;
@@ -50,12 +52,9 @@ public class AnnotationAdder
{ {
Annotations an = c.getAnnotations(); Annotations an = c.getAnnotations();
Annotation implAn = new Annotation(an); Annotation implAn = new Annotation(DeobAnnotations.IMPLEMENTS);
implAn.setType(DeobAnnotations.IMPLEMENTS);
Element value = new Element(implAn); Element value = new SimpleElement(c.getClassName());
value.setValue(c.getClassName());
value.setName("value");
implAn.addElement(value); implAn.addElement(value);
an.addAnnotation(implAn); an.addAnnotation(implAn);
@@ -81,12 +80,9 @@ public class AnnotationAdder
Annotation a = an.find(DeobAnnotations.EXPORT); Annotation a = an.find(DeobAnnotations.EXPORT);
if (a == null) if (a == null)
{ {
a = new Annotation(an); a = new Annotation(DeobAnnotations.EXPORT);
a.setType(DeobAnnotations.EXPORT);
Element value = new Element(a); Element value = new SimpleElement(fieldName);
value.setValue(fieldName);
value.setName("value");
a.addElement(value); a.addElement(value);
an.addAnnotation(a); an.addAnnotation(a);
@@ -114,12 +110,9 @@ public class AnnotationAdder
Annotation a = an.find(DeobAnnotations.EXPORT); Annotation a = an.find(DeobAnnotations.EXPORT);
if (a == null) if (a == null)
{ {
a = new Annotation(an); a = new Annotation(DeobAnnotations.EXPORT);
a.setType(DeobAnnotations.EXPORT);
Element value = new Element(a); Element value = new SimpleElement(methodName);
value.setValue(methodName);
value.setName("value");
a.addElement(value); a.addElement(value);
an.addAnnotation(a); an.addAnnotation(a);

View File

@@ -34,6 +34,7 @@ import net.runelite.asm.Type;
import net.runelite.asm.attributes.Annotations; import net.runelite.asm.attributes.Annotations;
import net.runelite.asm.attributes.annotation.Annotation; import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.attributes.annotation.Element; import net.runelite.asm.attributes.annotation.Element;
import net.runelite.asm.attributes.annotation.SimpleElement;
public class AnnotationCopier public class AnnotationCopier
{ {
@@ -97,15 +98,12 @@ public class AnnotationCopier
{ {
if (!isType(a.getType())) if (!isType(a.getType()))
continue; continue;
Annotation a2 = new Annotation(an2); Annotation a2 = new Annotation(a.getType());
a2.setType(a.getType());
for (Element element : a.getElements()) for (Element element : a.getElements())
{ {
Element element2 = new Element(a2); Element element2 = new SimpleElement(element.getName(), element.getValue());
element2.setName(element.getName());
element2.setValue(element.getValue());
a2.addElement(element2); a2.addElement(element2);
} }

View File

@@ -34,13 +34,9 @@ import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.deob.DeobAnnotations; import net.runelite.deob.DeobAnnotations;
import net.runelite.deob.deobfuscators.Renamer; import net.runelite.deob.deobfuscators.Renamer;
import net.runelite.deob.util.NameMappings; import net.runelite.deob.util.NameMappings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AnnotationRenamer public class AnnotationRenamer
{ {
private static final Logger logger = LoggerFactory.getLogger(AnnotationRenamer.class);
private ClassGroup group; private ClassGroup group;
public AnnotationRenamer(ClassGroup group) public AnnotationRenamer(ClassGroup group)

View File

@@ -25,9 +25,11 @@
package net.runelite.deob.util; package net.runelite.deob.util;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Collection;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
@@ -79,6 +81,41 @@ public class JarUtil
return group; 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 public static void saveJar(ClassGroup group, File jarfile) throws IOException
{ {
try (JarOutputStream jout = new JarOutputStream(new FileOutputStream(jarfile), new Manifest())) try (JarOutputStream jout = new JarOutputStream(new FileOutputStream(jarfile), new Manifest()))

View File

@@ -276,8 +276,7 @@ public class HookImporter
{ {
for (Element e : a.getElements()) for (Element e : a.getElements())
{ {
String str = (String) e.getValue(); return (String) e.getValue();
return str;
} }
} }
} }
@@ -288,7 +287,7 @@ public class HookImporter
private Signature getObfuscatedMethodSignature(Method method) private Signature getObfuscatedMethodSignature(Method method)
{ {
String sig = getAnnotation(method.getAnnotations(), OBFUSCATED_SIGNATURE); 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 return toObSignature(new Signature(sig)); // if it is annoted, use that
} }

View File

@@ -3,4 +3,4 @@ org.gradle.warning.mode=all
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.console=rich org.gradle.console=rich
org.gradle.configureondemand=true org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 org.gradle.jvmargs=-XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

2
gradlew vendored
View File

@@ -44,7 +44,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"` 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. # 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. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD="maximum"

2
gradlew.bat vendored
View File

@@ -30,7 +30,7 @@ set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% 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. @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 @rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome if defined JAVA_HOME goto findJavaFromJavaHome

View File

@@ -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()
])
}
}

View File

@@ -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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * 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 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package net.runelite.injector;
import net.runelite.asm.ClassFile; import org.apache.tools.ant.filters.ReplaceTokens
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;
public class InjectConstructTest description = "Web API"
{
@Test
public void testInjectConstruct() throws Exception
{
ClassFile targetClass = new ClassFile();
targetClass.setName("test");
ClassFile vanillaClass = new ClassFile(); dependencies {
vanillaClass.setName("ab"); annotationProcessor(Libraries.lombok)
Method constructor = new Method(vanillaClass, "<init>", new Signature("()V"));
vanillaClass.addMethod(constructor);
Inject inject = mock(Inject.class); compileOnly(Libraries.javaxInject)
when(inject.findVanillaForInterface(Matchers.any(Class.class))) compileOnly(Libraries.lombok)
.thenReturn(vanillaClass);
InjectConstruct injectConstruct = new InjectConstruct(inject);
injectConstruct.injectConstruct(targetClass, APIClass.class.getDeclaredMethod("create"));
assertNotNull(targetClass.findMethod("create")); implementation(Libraries.gson)
} implementation(Libraries.guava)
implementation(Libraries.okhttp3)
interface APIClass implementation(Libraries.rxjava)
{ implementation(Libraries.apacheCommonsCsv)
APIClass create(); 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)
}
}
} }

View File

@@ -25,13 +25,9 @@
package net.runelite.http.api; package net.runelite.http.api;
import com.google.gson.Gson; 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.HttpUrl;
import okhttp3.Interceptor; import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
@@ -60,12 +56,9 @@ public class RuneLiteAPI
public static final String RUNELITE_AUTH = "RUNELITE-AUTH"; public static final String RUNELITE_AUTH = "RUNELITE-AUTH";
public static final OkHttpClient CLIENT; public static final OkHttpClient CLIENT;
public static final Gson GSON = new GsonBuilder() public static final Gson GSON = new Gson();
.setPrettyPrinting() public static final MediaType JSON = MediaType.parse("application/json");
.registerTypeAdapter(ItemStats.class, TypeAdapters.ITEMSTATS) public static String userAgent;
.registerTypeAdapter(ItemEquipmentStats.class, TypeAdapters.EQUIPMENTSTATS)
.registerTypeAdapter(ItemPrice.class, TypeAdapters.ITEMPRICE)
.create();
private static final String BASE = "https://api.runelite.net"; private static final String BASE = "https://api.runelite.net";
private static final String WSBASE = "https://api.runelite.net/ws"; 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 String MAVEN_METADATA = "http://repo.runelite.net/net/runelite/runelite-parent/maven-metadata.xml";
private static final Properties properties = new Properties(); private static final Properties properties = new Properties();
private static String userAgent;
private static String version; private static String version;
private static String upstreamVersion; private static String upstreamVersion;
private static int rsVersion; private static int rsVersion;

View File

@@ -61,49 +61,20 @@ public class DiscordEmbed
VideoEmbed video; VideoEmbed video;
ProviderEmbed provider; ProviderEmbed provider;
AuthorEmbed author; AuthorEmbed author;
@Builder.Default final List<FieldEmbed> fields = new ArrayList<>();
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); return new DiscordMessage(username, content, avatarUrl, this);
}
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;
}
} }
} }

View File

@@ -29,16 +29,12 @@ package net.runelite.http.api.discord;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.ToString; import lombok.ToString;
@Getter @Getter
@Setter @Setter
@Builder
@AllArgsConstructor
@ToString @ToString
public class DiscordMessage public class DiscordMessage
{ {
@@ -48,24 +44,14 @@ public class DiscordMessage
String avatarUrl; String avatarUrl;
@SerializedName("tts") @SerializedName("tts")
boolean textToSpeech; 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)
{ {
this.username = username;
} this.content = content;
this.avatarUrl = avatar_url;
public DiscordMessage(String username, String content, String avatar_url) this.embeds.add(embed);
{
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);
} }
public void setUsername(String username) public void setUsername(String username)
@@ -79,15 +65,4 @@ public class DiscordMessage
this.username = null; this.username = null;
} }
} }
public static class DiscordMessageBuilder
{
List<DiscordEmbed> embeds = new ArrayList<>();
public DiscordMessageBuilder embed(DiscordEmbed embed)
{
embeds.add(embed);
return this;
}
}
} }

View File

@@ -30,10 +30,10 @@ import java.util.UUID;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.http.api.RuneLiteAPI; import net.runelite.http.api.RuneLiteAPI;
import static net.runelite.http.api.RuneLiteAPI.JSON;
import okhttp3.Call; import okhttp3.Call;
import okhttp3.Callback; import okhttp3.Callback;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.RequestBody; import okhttp3.RequestBody;
import okhttp3.Response; import okhttp3.Response;
@@ -42,7 +42,6 @@ import okhttp3.Response;
@AllArgsConstructor @AllArgsConstructor
public class GrandExchangeClient public class GrandExchangeClient
{ {
private static final MediaType JSON = MediaType.parse("application/json");
private static final Gson GSON = RuneLiteAPI.GSON; private static final Gson GSON = RuneLiteAPI.GSON;
private final UUID uuid; private final UUID uuid;

View File

@@ -36,10 +36,10 @@ import java.util.UUID;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.http.api.RuneLiteAPI; import net.runelite.http.api.RuneLiteAPI;
import static net.runelite.http.api.RuneLiteAPI.JSON;
import okhttp3.Call; import okhttp3.Call;
import okhttp3.Callback; import okhttp3.Callback;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.RequestBody; import okhttp3.RequestBody;
import okhttp3.Response; import okhttp3.Response;
@@ -48,7 +48,6 @@ import okhttp3.Response;
@AllArgsConstructor @AllArgsConstructor
public class LootTrackerClient public class LootTrackerClient
{ {
private static final MediaType JSON = MediaType.parse("application/json");
private static final Gson GSON = RuneLiteAPI.GSON; private static final Gson GSON = RuneLiteAPI.GSON;
private final UUID uuid; private final UUID uuid;

View File

@@ -31,10 +31,10 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.List; import java.util.List;
import net.runelite.http.api.RuneLiteAPI; import net.runelite.http.api.RuneLiteAPI;
import static net.runelite.http.api.RuneLiteAPI.JSON;
import okhttp3.Call; import okhttp3.Call;
import okhttp3.Callback; import okhttp3.Callback;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.RequestBody; import okhttp3.RequestBody;
import okhttp3.Response; import okhttp3.Response;
@@ -43,8 +43,6 @@ import org.slf4j.LoggerFactory;
public class XteaClient public class XteaClient
{ {
private static final MediaType JSON = MediaType.parse("application/json");
private static final Logger logger = LoggerFactory.getLogger(XteaClient.class); private static final Logger logger = LoggerFactory.getLogger(XteaClient.class);
public void submit(XteaRequest xteaRequest) public void submit(XteaRequest xteaRequest)

View File

@@ -1,4 +1,3 @@
runelite.version=@projectver@ runelite.version=@projectver@
rs.version=@rsver@ rs.version=@rsver@
runelite.commit=@gitcommit@ runelite.commit=@gitcommit@
runelite.dirty=@gitdirty@

View File

@@ -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
}

View 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)
}

View File

@@ -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')
}
}

View File

@@ -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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * 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 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * 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")
}
} }

View File

@@ -51,6 +51,7 @@ import net.runelite.http.api.config.ConfigEntry;
import net.runelite.http.api.config.Configuration; import net.runelite.http.api.config.Configuration;
import org.bson.Document; import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
@@ -66,11 +67,12 @@ public class ConfigService
@Autowired @Autowired
public ConfigService( 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"); MongoCollection<Document> collection = database.getCollection("config");
this.mongoCollection = collection; this.mongoCollection = collection;

View File

@@ -98,6 +98,11 @@ public class OSBGrandExchangeService
Integer itemId = entry.getKey(); Integer itemId = entry.getKey();
OsbuddySummaryItem item = entry.getValue(); OsbuddySummaryItem item = entry.getValue();
if (item.getBuy_average() <= 0 || item.getSell_average() <= 0 || item.getOverall_average() <= 0)
{
continue;
}
query query
.addParameter("itemId", itemId) .addParameter("itemId", itemId)
.addParameter("buyAverage", item.getBuy_average()) .addParameter("buyAverage", item.getBuy_average())

View File

@@ -32,10 +32,11 @@ redis:
mongo: mongo:
jndiName: java:comp/env/mongodb/runelite jndiName: java:comp/env/mongodb/runelite
database: runelite
# Twitter client for feed # Twitter client for feed
runelite: runelite:
twitter: twitter:
consumerkey: consumerkey:
secretkey: secretkey:
listid: 968949795153948673 listid: 1185897074786742273

View 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
}
}

View File

@@ -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()
}
}
}

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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");
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

Some files were not shown because too many files have changed in this diff Show More