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