You've already forked revanced-patcher
mirror of
https://github.com/revanced/revanced-patcher
synced 2025-09-13 18:30:49 +02:00
Compare commits
23 Commits
arsclib-re
...
v1.0.0-dev
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3cb1e01587 | ||
![]() |
cb4ee207e1 | ||
![]() |
ca6b94d943 | ||
![]() |
6cdb6887d4 | ||
![]() |
ab6453ca8a | ||
![]() |
e8182c17ad | ||
![]() |
49beec9fc6 | ||
![]() |
3ab42a932c | ||
![]() |
4d98cbc9e8 | ||
![]() |
87bbde5e06 | ||
![]() |
8db8893ab1 | ||
![]() |
00c6ab7faf | ||
![]() |
460d62a24c | ||
![]() |
89e4b9f762 | ||
![]() |
a8fd7c00c3 | ||
![]() |
1769132a9e | ||
![]() |
6c0f0823c9 | ||
![]() |
23e897a7a9 | ||
![]() |
7e67daf878 | ||
![]() |
593c83f29f | ||
![]() |
72e123dd01 | ||
![]() |
599a401ed9 | ||
![]() |
3f8500b059 |
9
.gitattributes
vendored
9
.gitattributes
vendored
@@ -1,9 +0,0 @@
|
|||||||
#
|
|
||||||
# https://help.github.com/articles/dealing-with-line-endings/
|
|
||||||
#
|
|
||||||
# Linux start script should use lf
|
|
||||||
/gradlew text eol=lf
|
|
||||||
|
|
||||||
# These are Windows script files and should use crlf
|
|
||||||
*.bat text eol=crlf
|
|
||||||
|
|
72
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
72
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
@@ -1,72 +0,0 @@
|
|||||||
name: 🐞 Bug report
|
|
||||||
description: Report a very clearly broken issue.
|
|
||||||
title: 'bug: <title>'
|
|
||||||
labels: [bug]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
# ReVanced bug report
|
|
||||||
|
|
||||||
Important to note that your issue may have already been reported before. Please check for existing issues [here](https://github.com/revanced/revanced-patcher/labels/bug).
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
attributes:
|
|
||||||
label: Type
|
|
||||||
options:
|
|
||||||
- Crash
|
|
||||||
- Cosmetic
|
|
||||||
- Other
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Bug description
|
|
||||||
description: How did you find the bug? Any additional details that might help?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Steps to reproduce
|
|
||||||
description: Add the steps to reproduce this bug including your environment.
|
|
||||||
placeholder: Step 1. Download some files. Step 2. ...
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Relevant log output
|
|
||||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
|
||||||
render: shell
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Screenshots or videos
|
|
||||||
description: Add screenshots or videos that show the bug here.
|
|
||||||
placeholder: Drag and drop the screenshots/videos into this box.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Solution
|
|
||||||
description: If applicable, add a possible solution.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Additional context
|
|
||||||
description: Add additional context here.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: checkboxes
|
|
||||||
id: acknowledgements
|
|
||||||
attributes:
|
|
||||||
label: Acknowledgements
|
|
||||||
description: Your issue will be closed if you haven't done these steps.
|
|
||||||
options:
|
|
||||||
- label: I have searched the existing issues and this is a new and no duplicate or related to another open issue.
|
|
||||||
required: true
|
|
||||||
- label: I have written a short but informative title.
|
|
||||||
required: true
|
|
||||||
- label: I filled out all of the requested information in this issue properly.
|
|
||||||
required: true
|
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
8
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,8 +0,0 @@
|
|||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: 📃 Documentation
|
|
||||||
url: https://github.com/revanced/revanced-documentation/
|
|
||||||
about: Don't know how or where to start? Check out our documentation!
|
|
||||||
- name: 🗨 Discussions
|
|
||||||
url: https://github.com/revanced/revanced-suggestions/discussions
|
|
||||||
about: Got something you think should change or be added? Search for or start a new discussion!
|
|
58
.github/ISSUE_TEMPLATE/feature-issue.yml
vendored
58
.github/ISSUE_TEMPLATE/feature-issue.yml
vendored
@@ -1,58 +0,0 @@
|
|||||||
name: ⭐ Feature request
|
|
||||||
description: Create a detailed feature request.
|
|
||||||
title: 'feat: <title>'
|
|
||||||
labels: [feature-request]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
# ReVanced feature request
|
|
||||||
|
|
||||||
Do not submit requests for patches here. Please submit them [here](https://github.com/orgs/revanced/discussions/categories/patches) instead.
|
|
||||||
Important to note that your feature request may have already been made before. Please check for existing feature requests [here](https://github.com/revanced/revanced-patcher/labels/feature-request).
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
attributes:
|
|
||||||
label: Type
|
|
||||||
options:
|
|
||||||
- Functionality
|
|
||||||
- Cosmetic
|
|
||||||
- Other
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Issue
|
|
||||||
description: What is the current problem. Why does it require a feature request?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Feature
|
|
||||||
description: Describe your feature in detail. How does it solve the issue?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Motivation
|
|
||||||
description: Why should your feature should be considered?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Additional context
|
|
||||||
description: Add additional context here.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: checkboxes
|
|
||||||
id: acknowledgements
|
|
||||||
attributes:
|
|
||||||
label: Acknowledgements
|
|
||||||
description: Your issue will be closed if you haven't done these steps.
|
|
||||||
options:
|
|
||||||
- label: I have searched the existing issues and this is a new and no duplicate or related to another open issue.
|
|
||||||
required: true
|
|
||||||
- label: I have written a short but informative title.
|
|
||||||
required: true
|
|
||||||
- label: I filled out all of the requested information in this issue properly.
|
|
||||||
required: true
|
|
2
.github/config.yml
vendored
2
.github/config.yml
vendored
@@ -1,2 +0,0 @@
|
|||||||
firstPRMergeComment: >
|
|
||||||
Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) if you want to receive a contributor role.
|
|
25
.github/workflows/pull_request.yml
vendored
25
.github/workflows/pull_request.yml
vendored
@@ -1,25 +0,0 @@
|
|||||||
name: PR to main
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- dev
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
MESSAGE: merge branch `${{ github.head_ref || github.ref_name }}` to `main`
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
pull-request:
|
|
||||||
name: Open pull request
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Open pull request
|
|
||||||
uses: repo-sync/pull-request@v2
|
|
||||||
with:
|
|
||||||
destination_branch: 'main'
|
|
||||||
pr_title: 'chore: ${{ env.MESSAGE }}'
|
|
||||||
pr_body: 'This pull request will ${{ env.MESSAGE }}.'
|
|
||||||
pr_draft: true
|
|
38
.github/workflows/release.yml
vendored
38
.github/workflows/release.yml
vendored
@@ -1,7 +1,5 @@
|
|||||||
name: Release
|
name: Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
@@ -10,36 +8,32 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- dev
|
- dev
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
name: Release
|
name: Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
# Make sure the release step uses its own credentials:
|
|
||||||
# https://github.com/cycjimmy/semantic-release-action#private-packages
|
|
||||||
persist-credentials: false
|
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Cache
|
- name: Setup JDK
|
||||||
uses: actions/cache@v3
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
path: |
|
java-version: '8'
|
||||||
${{ runner.home }}/.gradle/caches
|
distribution: 'adopt'
|
||||||
${{ runner.home }}/.gradle/wrapper
|
cache: gradle
|
||||||
.gradle
|
- name: Setup Node.js
|
||||||
build
|
uses: actions/setup-node@v2
|
||||||
node_modules
|
with:
|
||||||
key: ${{ runner.os }}-gradle-npm-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'package-lock.json') }}
|
node-version: "lts/*"
|
||||||
|
- name: Make gradlew executable
|
||||||
|
run: chmod +x gradlew
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
env:
|
run: ./gradlew build
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: ./gradlew clean --no-daemon
|
|
||||||
- name: Setup semantic-release
|
- name: Setup semantic-release
|
||||||
run: npm install
|
run: npm install -g semantic-release @semantic-release/git @semantic-release/changelog gradle-semantic-release-plugin -D
|
||||||
- name: Release
|
- name: Release
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: npm exec semantic-release
|
run: npx semantic-release
|
||||||
|
7
.gitignore
vendored
7
.gitignore
vendored
@@ -74,7 +74,6 @@ cmake-build-*/
|
|||||||
|
|
||||||
# IntelliJ
|
# IntelliJ
|
||||||
out/
|
out/
|
||||||
.idea/
|
|
||||||
|
|
||||||
# mpeltonen/sbt-idea plugin
|
# mpeltonen/sbt-idea plugin
|
||||||
.idea_modules/
|
.idea_modules/
|
||||||
@@ -116,9 +115,3 @@ gradle-app.setting
|
|||||||
|
|
||||||
# Avoid ignoring test resources
|
# Avoid ignoring test resources
|
||||||
!src/test/resources/*
|
!src/test/resources/*
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
|
|
||||||
# Gradle props, to avoid sharing the gpr key
|
|
||||||
gradle.properties
|
|
||||||
|
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
10
.idea/codeStyles/Project.xml
generated
Normal file
10
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<JetCodeStyleSettings>
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</JetCodeStyleSettings>
|
||||||
|
<codeStyleSettings language="kotlin">
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</codeStyleSettings>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</state>
|
||||||
|
</component>
|
7
.idea/discord.xml
generated
Normal file
7
.idea/discord.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DiscordProjectSettings">
|
||||||
|
<option name="show" value="PROJECT_FILES" />
|
||||||
|
<option name="description" value="" />
|
||||||
|
</component>
|
||||||
|
</project>
|
15
.idea/git_toolbox_prj.xml
generated
Normal file
15
.idea/git_toolbox_prj.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GitToolBoxProjectSettings">
|
||||||
|
<option name="commitMessageIssueKeyValidationOverride">
|
||||||
|
<BoolValueOverride>
|
||||||
|
<option name="enabled" value="true" />
|
||||||
|
</BoolValueOverride>
|
||||||
|
</option>
|
||||||
|
<option name="commitMessageValidationEnabledOverride">
|
||||||
|
<BoolValueOverride>
|
||||||
|
<option name="enabled" value="true" />
|
||||||
|
</BoolValueOverride>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
10
.idea/misc.xml
generated
Normal file
10
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
|
<file type="web" url="file://$PROJECT_DIR$" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
12
.idea/vcs.xml
generated
Normal file
12
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CommitMessageInspectionProfile">
|
||||||
|
<profile version="1.0">
|
||||||
|
<inspection_tool class="CommitFormat" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="CommitNamingConvention" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
14
.releaserc
14
.releaserc
@@ -20,18 +20,6 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
"@semantic-release/github"
|
||||||
"@saithodev/semantic-release-backmerge",
|
|
||||||
{
|
|
||||||
backmergeBranches: [{"from": "main", "to": "dev"}],
|
|
||||||
clearWorkspace: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"@semantic-release/github",
|
|
||||||
{
|
|
||||||
successComment: false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
1409
CHANGELOG.md
1409
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1 @@
|
|||||||
# 💉 ReVanced Patcher
|
# Patcher
|
||||||
|
|
||||||
ReVanced Patcher used to patch Android applications.
|
|
||||||
|
42
arsclib-utils/.gitignore
vendored
42
arsclib-utils/.gitignore
vendored
@@ -1,42 +0,0 @@
|
|||||||
.gradle
|
|
||||||
build/
|
|
||||||
!gradle/wrapper/gradle-wrapper.jar
|
|
||||||
!**/src/main/**/build/
|
|
||||||
!**/src/test/**/build/
|
|
||||||
|
|
||||||
### IntelliJ IDEA ###
|
|
||||||
.idea/modules.xml
|
|
||||||
.idea/jarRepositories.xml
|
|
||||||
.idea/compiler.xml
|
|
||||||
.idea/libraries/
|
|
||||||
*.iws
|
|
||||||
*.iml
|
|
||||||
*.ipr
|
|
||||||
out/
|
|
||||||
!**/src/main/**/out/
|
|
||||||
!**/src/test/**/out/
|
|
||||||
|
|
||||||
### Eclipse ###
|
|
||||||
.apt_generated
|
|
||||||
.classpath
|
|
||||||
.factorypath
|
|
||||||
.project
|
|
||||||
.settings
|
|
||||||
.springBeans
|
|
||||||
.sts4-cache
|
|
||||||
bin/
|
|
||||||
!**/src/main/**/bin/
|
|
||||||
!**/src/test/**/bin/
|
|
||||||
|
|
||||||
### NetBeans ###
|
|
||||||
/nbproject/private/
|
|
||||||
/nbbuild/
|
|
||||||
/dist/
|
|
||||||
/nbdist/
|
|
||||||
/.nb-gradle/
|
|
||||||
|
|
||||||
### VS Code ###
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
### Mac OS ###
|
|
||||||
.DS_Store
|
|
@@ -1,18 +0,0 @@
|
|||||||
plugins {
|
|
||||||
kotlin("jvm")
|
|
||||||
`maven-publish`
|
|
||||||
}
|
|
||||||
|
|
||||||
group = "app.revanced"
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation("io.github.reandroid:ARSCLib:1.1.7")
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
withSourcesJar()
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
jvmToolchain(11)
|
|
||||||
}
|
|
@@ -1,72 +0,0 @@
|
|||||||
package app.revanced.arsc
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception thrown when there is an error with APK resources.
|
|
||||||
*
|
|
||||||
* @param message The exception message.
|
|
||||||
* @param throwable The corresponding [Throwable].
|
|
||||||
*/
|
|
||||||
sealed class ApkResourceException(message: String, throwable: Throwable? = null) : Exception(message, throwable) {
|
|
||||||
/**
|
|
||||||
* An exception when locking resources.
|
|
||||||
*
|
|
||||||
* @param message The exception message.
|
|
||||||
* @param throwable The corresponding [Throwable].
|
|
||||||
*/
|
|
||||||
class Locked(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception when writing resources.
|
|
||||||
*
|
|
||||||
* @param message The exception message.
|
|
||||||
* @param throwable The corresponding [Throwable].
|
|
||||||
*/
|
|
||||||
class Write(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception when reading resources.
|
|
||||||
*
|
|
||||||
* @param message The exception message.
|
|
||||||
* @param throwable The corresponding [Throwable].
|
|
||||||
*/
|
|
||||||
class Read(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
|
|
||||||
/**
|
|
||||||
* An exception when decoding resources.
|
|
||||||
*
|
|
||||||
* @param message The exception message.
|
|
||||||
* @param throwable The corresponding [Throwable].
|
|
||||||
*/
|
|
||||||
class Decode(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception when encoding resources.
|
|
||||||
*
|
|
||||||
* @param message The exception message.
|
|
||||||
* @param throwable The corresponding [Throwable].
|
|
||||||
*/
|
|
||||||
class Encode(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception thrown when a reference could not be resolved.
|
|
||||||
*
|
|
||||||
* @param reference The invalid reference.
|
|
||||||
* @param throwable The corresponding [Throwable].
|
|
||||||
*/
|
|
||||||
class InvalidReference(reference: String, throwable: Throwable? = null) :
|
|
||||||
ApkResourceException("Failed to resolve: $reference", throwable) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception thrown when a reference could not be resolved.
|
|
||||||
*
|
|
||||||
* @param type The type of the reference.
|
|
||||||
* @param name The name of the reference.
|
|
||||||
* @param throwable The corresponding [Throwable].
|
|
||||||
*/
|
|
||||||
constructor(type: String, name: String, throwable: Throwable? = null) : this("@$type/$name", throwable)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception thrown when the Apk file not have a resource table, but was expected to have one.
|
|
||||||
*/
|
|
||||||
class MissingResourceTable : ApkResourceException("Apk does not have a resource table.")
|
|
||||||
}
|
|
@@ -1,28 +0,0 @@
|
|||||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
|
||||||
|
|
||||||
package app.revanced.arsc.archive
|
|
||||||
|
|
||||||
import app.revanced.arsc.resource.ResourceContainer
|
|
||||||
import com.reandroid.apk.ApkModule
|
|
||||||
import com.reandroid.apk.DexFileInputSource
|
|
||||||
import com.reandroid.archive.InputSource
|
|
||||||
import java.io.File
|
|
||||||
import java.io.Flushable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class for reading/writing files in an [ApkModule].
|
|
||||||
*
|
|
||||||
* @param module The [ApkModule] to operate on.
|
|
||||||
*/
|
|
||||||
class Archive(internal val module: ApkModule) : Flushable {
|
|
||||||
val mainPackageResources = ResourceContainer(this, module.tableBlock)
|
|
||||||
|
|
||||||
fun save(output: File) {
|
|
||||||
flush()
|
|
||||||
module.writeApk(output)
|
|
||||||
}
|
|
||||||
fun readDexFiles(): MutableList<DexFileInputSource> = module.listDexFiles()
|
|
||||||
fun write(inputSource: InputSource) = module.apkArchive.add(inputSource) // Overwrites existing files.
|
|
||||||
fun read(name: String): InputSource? = module.apkArchive.getInputSource(name)
|
|
||||||
override fun flush() = mainPackageResources.flush()
|
|
||||||
}
|
|
@@ -1,7 +0,0 @@
|
|||||||
package app.revanced.arsc.logging
|
|
||||||
interface Logger {
|
|
||||||
fun error(msg: String)
|
|
||||||
fun warn(msg: String)
|
|
||||||
fun info(msg: String)
|
|
||||||
fun trace(msg: String)
|
|
||||||
}
|
|
@@ -1,166 +0,0 @@
|
|||||||
package app.revanced.arsc.resource
|
|
||||||
|
|
||||||
import app.revanced.arsc.ApkResourceException
|
|
||||||
import com.reandroid.arsc.coder.EncodeResult
|
|
||||||
import com.reandroid.arsc.coder.ValueDecoder
|
|
||||||
import com.reandroid.arsc.value.Entry
|
|
||||||
import com.reandroid.arsc.value.ValueType
|
|
||||||
import com.reandroid.arsc.value.array.ArrayBag
|
|
||||||
import com.reandroid.arsc.value.array.ArrayBagItem
|
|
||||||
import com.reandroid.arsc.value.plurals.PluralsBag
|
|
||||||
import com.reandroid.arsc.value.plurals.PluralsBagItem
|
|
||||||
import com.reandroid.arsc.value.plurals.PluralsQuantity
|
|
||||||
import com.reandroid.arsc.value.style.StyleBag
|
|
||||||
import com.reandroid.arsc.value.style.StyleBagItem
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A resource value.
|
|
||||||
*/
|
|
||||||
sealed class Resource {
|
|
||||||
internal abstract fun write(entry: Entry, resources: ResourceContainer)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val Resource.isComplex get() = when (this) {
|
|
||||||
is Scalar -> false
|
|
||||||
is Complex -> true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A simple resource.
|
|
||||||
*/
|
|
||||||
open class Scalar internal constructor(private val valueType: ValueType, private val value: Int) : Resource() {
|
|
||||||
protected open fun data(resources: ResourceContainer) = value
|
|
||||||
|
|
||||||
override fun write(entry: Entry, resources: ResourceContainer) {
|
|
||||||
entry.setValueAsRaw(valueType, data(resources))
|
|
||||||
}
|
|
||||||
|
|
||||||
internal open fun toArrayItem(resources: ResourceContainer) = ArrayBagItem.create(valueType, data(resources))
|
|
||||||
internal open fun toStyleItem(resources: ResourceContainer) = StyleBagItem.create(valueType, data(resources))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A marker class for complex resources.
|
|
||||||
*/
|
|
||||||
sealed class Complex : Resource()
|
|
||||||
|
|
||||||
private fun encoded(encodeResult: EncodeResult?) = encodeResult?.let { Scalar(it.valueType, it.value) }
|
|
||||||
?: throw ApkResourceException.Encode("Failed to encode value")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a color.
|
|
||||||
*
|
|
||||||
* @param hex The hex value of the color.
|
|
||||||
* @return The encoded [Resource].
|
|
||||||
*/
|
|
||||||
fun color(hex: String) = encoded(ValueDecoder.encodeColor(hex))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a dimension or fraction.
|
|
||||||
*
|
|
||||||
* @param value The dimension value such as 24dp.
|
|
||||||
* @return The encoded [Resource].
|
|
||||||
*/
|
|
||||||
fun dimension(value: String) = encoded(ValueDecoder.encodeDimensionOrFraction(value))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a boolean resource.
|
|
||||||
*
|
|
||||||
* @param value The boolean.
|
|
||||||
* @return The encoded [Resource].
|
|
||||||
*/
|
|
||||||
fun boolean(value: Boolean) = Scalar(ValueType.INT_BOOLEAN, if (value) -Int.MAX_VALUE else 0)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a float.
|
|
||||||
*
|
|
||||||
* @param n The number to encode.
|
|
||||||
* @return The encoded [Resource].
|
|
||||||
*/
|
|
||||||
fun float(n: Float) = Scalar(ValueType.FLOAT, n.toBits())
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an integer [Resource].
|
|
||||||
*
|
|
||||||
* @param n The number to encode.
|
|
||||||
* @return The integer [Resource].
|
|
||||||
*/
|
|
||||||
fun integer(n: Int) = Scalar(ValueType.INT_DEC, n)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a reference [Resource].
|
|
||||||
*
|
|
||||||
* @param resourceId The target resource.
|
|
||||||
* @return The reference resource.
|
|
||||||
*/
|
|
||||||
fun reference(resourceId: Int) = Scalar(ValueType.REFERENCE, resourceId)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve and create a reference [Resource].
|
|
||||||
*
|
|
||||||
* @see reference
|
|
||||||
* @param ref The reference string to resolve.
|
|
||||||
* @param resourceTable The resource table to resolve the reference with.
|
|
||||||
* @return The reference resource.
|
|
||||||
*/
|
|
||||||
fun reference(resourceTable: ResourceTable, ref: String) = reference(resourceTable.resolve(ref))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An array [Resource].
|
|
||||||
*
|
|
||||||
* @param elements The elements of the array.
|
|
||||||
*/
|
|
||||||
class Array(private val elements: Collection<Scalar>) : Complex() {
|
|
||||||
override fun write(entry: Entry, resources: ResourceContainer) {
|
|
||||||
ArrayBag.create(entry).addAll(elements.map { it.toArrayItem(resources) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A style resource.
|
|
||||||
*
|
|
||||||
* @param elements The attributes to override.
|
|
||||||
* @param parent A reference to the parent style.
|
|
||||||
*/
|
|
||||||
class Style(private val elements: Map<String, Scalar>, private val parent: String? = null) : Complex() {
|
|
||||||
override fun write(entry: Entry, resources: ResourceContainer) {
|
|
||||||
val resTable = resources.resourceTable
|
|
||||||
val style = StyleBag.create(entry)
|
|
||||||
parent?.let {
|
|
||||||
style.parentId = resTable.resolve(parent)
|
|
||||||
}
|
|
||||||
|
|
||||||
style.putAll(
|
|
||||||
elements.asIterable().associate {
|
|
||||||
StyleBag.resolve(resTable.encodeMaterials, it.key) to it.value.toStyleItem(resources)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A quantity string [Resource].
|
|
||||||
*
|
|
||||||
* @param elements A map of the quantity to the corresponding string.
|
|
||||||
*/
|
|
||||||
class Plurals(private val elements: Map<String, String>) : Complex() {
|
|
||||||
override fun write(entry: Entry, resources: ResourceContainer) {
|
|
||||||
val plurals = PluralsBag.create(entry)
|
|
||||||
|
|
||||||
plurals.putAll(elements.asIterable().associate { (k, v) ->
|
|
||||||
PluralsQuantity.value(k) to PluralsBagItem.string(resources.getOrCreateString(v))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A string [Resource].
|
|
||||||
*
|
|
||||||
* @param value The string value.
|
|
||||||
*/
|
|
||||||
class StringResource(val value: String) : Scalar(ValueType.STRING, 0) {
|
|
||||||
private fun tableString(resources: ResourceContainer) = resources.getOrCreateString(value)
|
|
||||||
|
|
||||||
override fun data(resources: ResourceContainer) = tableString(resources).index
|
|
||||||
override fun toArrayItem(resources: ResourceContainer) = ArrayBagItem.string(tableString(resources))
|
|
||||||
override fun toStyleItem(resources: ResourceContainer) = StyleBagItem.string(tableString(resources))
|
|
||||||
}
|
|
@@ -1,167 +0,0 @@
|
|||||||
package app.revanced.arsc.resource
|
|
||||||
|
|
||||||
import app.revanced.arsc.ApkResourceException
|
|
||||||
import app.revanced.arsc.archive.Archive
|
|
||||||
import com.reandroid.apk.xmlencoder.EncodeUtil
|
|
||||||
import com.reandroid.arsc.chunk.TableBlock
|
|
||||||
import com.reandroid.arsc.chunk.xml.ResXmlDocument
|
|
||||||
import com.reandroid.arsc.value.Entry
|
|
||||||
import com.reandroid.arsc.value.ResConfig
|
|
||||||
import java.io.Closeable
|
|
||||||
import java.io.File
|
|
||||||
import java.io.Flushable
|
|
||||||
|
|
||||||
class ResourceContainer(private val archive: Archive, internal val tableBlock: TableBlock) : Flushable {
|
|
||||||
private val packageBlock = tableBlock.pickOne() // Pick the main package block.
|
|
||||||
internal lateinit var resourceTable: ResourceTable // TODO: Set this.
|
|
||||||
|
|
||||||
private val lockedResourceFileNames = mutableSetOf<String>()
|
|
||||||
|
|
||||||
private fun lock(resourceFile: ResourceFile) {
|
|
||||||
if (resourceFile.name in lockedResourceFileNames) {
|
|
||||||
throw ApkResourceException.Locked("Resource file ${resourceFile.name} is already locked.")
|
|
||||||
}
|
|
||||||
|
|
||||||
lockedResourceFileNames.add(resourceFile.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun unlock(resourceFile: ResourceFile) {
|
|
||||||
lockedResourceFileNames.remove(resourceFile.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun <T : ResourceFile> openResource(name: String): ResourceFileEditor<T> {
|
|
||||||
val inputSource = archive.read(name)
|
|
||||||
?: throw ApkResourceException.Read("Resource file $name not found.")
|
|
||||||
|
|
||||||
val resourceFile = when {
|
|
||||||
ResXmlDocument.isResXmlBlock(inputSource.openStream()) -> {
|
|
||||||
val xmlDocument = archive.module
|
|
||||||
.loadResXmlDocument(inputSource)
|
|
||||||
.decodeToXml(resourceTable.entryStore, packageBlock.id)
|
|
||||||
|
|
||||||
ResourceFile.XmlResourceFile(name, xmlDocument)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
val bytes = inputSource.openStream().use { it.readAllBytes() }
|
|
||||||
|
|
||||||
ResourceFile.BinaryResourceFile(name, bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
return ResourceFileEditor(resourceFile as T).also {
|
|
||||||
lockedResourceFileNames.add(name)
|
|
||||||
}
|
|
||||||
} catch (e: ClassCastException) {
|
|
||||||
throw ApkResourceException.Decode("Resource file $name is not ${resourceFile::class}.", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class ResourceFileEditor<T : ResourceFile> internal constructor(
|
|
||||||
private val resourceFile: T,
|
|
||||||
) : Closeable {
|
|
||||||
fun use(block: (T) -> Unit) = block(resourceFile)
|
|
||||||
override fun close() {
|
|
||||||
lockedResourceFileNames.remove(resourceFile.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun flush() {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a resource file, creating it if the file does not exist.
|
|
||||||
*
|
|
||||||
* @param path The resource file path.
|
|
||||||
* @return The corresponding [ResourceFiles],
|
|
||||||
*/
|
|
||||||
fun openFile(path: String) = ResourceFiles(createHandle(path), archive)
|
|
||||||
|
|
||||||
private fun getPackageBlock() = packageBlock ?: throw ApkResourceException.MissingResourceTable
|
|
||||||
|
|
||||||
internal fun getOrCreateString(value: String) =
|
|
||||||
tableBlock?.stringPool?.getOrCreate(value) ?: throw ApkResourceException.MissingResourceTable
|
|
||||||
|
|
||||||
private fun Entry.set(resource: Resource) {
|
|
||||||
val existingEntryNameReference = specReference
|
|
||||||
|
|
||||||
// Sets this.specReference if the entry is not yet initialized.
|
|
||||||
// Sets this.specReference to 0 if the resource type of the existing entry changes.
|
|
||||||
ensureComplex(resource.isComplex)
|
|
||||||
|
|
||||||
if (existingEntryNameReference != 0) {
|
|
||||||
// Preserve the entry name by restoring the previous spec block reference (if present).
|
|
||||||
specReference = existingEntryNameReference
|
|
||||||
}
|
|
||||||
|
|
||||||
resource.write(this, this@ResourceContainer)
|
|
||||||
resourceTable.registerChanged(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve an [Entry] from the resource table.
|
|
||||||
*
|
|
||||||
* @param type The resource type.
|
|
||||||
* @param name The resource name.
|
|
||||||
* @param qualifiers The variant to use.
|
|
||||||
*/
|
|
||||||
private fun getEntry(type: String, name: String, qualifiers: String?): Entry? {
|
|
||||||
val resourceId = try {
|
|
||||||
resourceTable.resolve("@$type/$name")
|
|
||||||
} catch (_: ApkResourceException.InvalidReference) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val config = ResConfig.parse(qualifiers)
|
|
||||||
return tableBlock?.resolveReference(resourceId)?.singleOrNull { it.resConfig == config }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a [ResourceFiles.Handle] that can be used to open a [ResourceFiles].
|
|
||||||
* This may involve looking it up in the resource table to find the actual location in the archive.
|
|
||||||
*
|
|
||||||
* @param path The path of the resource.
|
|
||||||
*/
|
|
||||||
private fun createHandle(path: String): ResourceFiles.Handle {
|
|
||||||
if (path.startsWith("res/values")) throw ApkResourceException.Decode("Decoding the resource table as a file is not supported")
|
|
||||||
|
|
||||||
var onClose = {}
|
|
||||||
var archivePath = path
|
|
||||||
|
|
||||||
if (tableBlock != null && path.startsWith("res/") && path.count { it == '/' } == 2) {
|
|
||||||
val file = File(path)
|
|
||||||
|
|
||||||
val qualifiers = EncodeUtil.getQualifiersFromResFile(file)
|
|
||||||
val type = EncodeUtil.getTypeNameFromResFile(file)
|
|
||||||
val name = file.nameWithoutExtension
|
|
||||||
|
|
||||||
// The resource file names that the app developers used may have been minified, so we have to resolve it with the resource table.
|
|
||||||
// Example: res/drawable-hdpi/icon.png -> res/4a.png
|
|
||||||
getEntry(type, name, qualifiers)?.resValue?.valueAsString?.let {
|
|
||||||
archivePath = it
|
|
||||||
} ?: run {
|
|
||||||
// An entry for this specific resource file was not found in the resource table, so we have to register it after we save.
|
|
||||||
onClose = { setResource(type, name, StringResource(archivePath), qualifiers) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResourceFiles.Handle(path, archivePath, onClose)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setResource(type: String, entryName: String, resource: Resource, qualifiers: String? = null) =
|
|
||||||
getPackageBlock().getOrCreate(qualifiers, type, entryName).also { it.set(resource) }.resourceId
|
|
||||||
|
|
||||||
fun setResources(type: String, resources: Map<String, Resource>, configuration: String? = null) {
|
|
||||||
getPackageBlock().getOrCreateSpecTypePair(type).getOrCreateTypeBlock(configuration).apply {
|
|
||||||
resources.forEach { (entryName, resource) -> getOrCreateEntry(entryName).set(resource) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun flush() {
|
|
||||||
packageBlock?.name = archive
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,91 +0,0 @@
|
|||||||
package app.revanced.arsc.resource
|
|
||||||
|
|
||||||
import app.revanced.arsc.ApkResourceException
|
|
||||||
import app.revanced.arsc.archive.Archive
|
|
||||||
import com.reandroid.archive.InputSource
|
|
||||||
import com.reandroid.xml.XMLDocument
|
|
||||||
import com.reandroid.xml.XMLException
|
|
||||||
import java.io.*
|
|
||||||
|
|
||||||
|
|
||||||
abstract class ResourceFile(val name: String) {
|
|
||||||
internal var realName: String? = null
|
|
||||||
|
|
||||||
class XmlResourceFile(name: String, val document: XMLDocument) : ResourceFile(name)
|
|
||||||
class BinaryResourceFile(name: String, var bytes: ByteArray) : ResourceFile(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ResourceFiles private constructor(
|
|
||||||
) : Closeable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantiate a [ResourceFiles].
|
|
||||||
*
|
|
||||||
* @param handle The [Handle] associated with this file.
|
|
||||||
* @param archive The [Archive] that the file resides in.
|
|
||||||
*/
|
|
||||||
internal constructor(handle: Handle, archive: Archive) : this(
|
|
||||||
handle,
|
|
||||||
archive,
|
|
||||||
try {
|
|
||||||
archive.read(handle.archivePath)
|
|
||||||
} catch (e: XMLException) {
|
|
||||||
throw ApkResourceException.Decode("Failed to decode XML while reading ${handle.virtualPath}", e)
|
|
||||||
} catch (e: IOException) {
|
|
||||||
throw ApkResourceException.Decode("Could not read ${handle.virtualPath}", e)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val DEFAULT_BUFFER_SIZE = 1024
|
|
||||||
}
|
|
||||||
|
|
||||||
var contents = readResult?.data ?: ByteArray(0)
|
|
||||||
set(value) {
|
|
||||||
pendingWrite = true
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
|
|
||||||
val exists = readResult != null
|
|
||||||
|
|
||||||
override fun toString() = handle.virtualPath
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
if (pendingWrite) {
|
|
||||||
val path = handle.archivePath
|
|
||||||
|
|
||||||
if (isXmlResource) archive.writeXml(
|
|
||||||
path,
|
|
||||||
try {
|
|
||||||
XMLDocument.load(inputStream())
|
|
||||||
} catch (e: XMLException) {
|
|
||||||
throw ApkResourceException.Encode("Failed to parse XML while writing ${handle.virtualPath}", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
) else archive.writeRaw(path, contents)
|
|
||||||
}
|
|
||||||
|
|
||||||
handle.onClose()
|
|
||||||
|
|
||||||
|
|
||||||
archive.unlock(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun inputStream(): InputStream = ByteArrayInputStream(contents)
|
|
||||||
|
|
||||||
fun outputStream(bufferSize: Int = DEFAULT_BUFFER_SIZE): OutputStream =
|
|
||||||
object : ByteArrayOutputStream(bufferSize) {
|
|
||||||
override fun close() {
|
|
||||||
this@ResourceFiles.contents = if (buf.size > count) buf.copyOf(count) else buf
|
|
||||||
super.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param virtualPath The resource file path. Example: /res/drawable-hdpi/icon.png.
|
|
||||||
* @param archivePath The actual file path in the archive. Example: res/4a.png.
|
|
||||||
* @param onClose An action to perform when the file associated with this handle is closed
|
|
||||||
*/
|
|
||||||
internal data class Handle(val virtualPath: String, val archivePath: String, val onClose: () -> Unit)
|
|
||||||
}
|
|
@@ -1,100 +0,0 @@
|
|||||||
package app.revanced.arsc.resource
|
|
||||||
|
|
||||||
import app.revanced.arsc.ApkResourceException
|
|
||||||
import com.reandroid.apk.xmlencoder.EncodeException
|
|
||||||
import com.reandroid.apk.xmlencoder.EncodeMaterials
|
|
||||||
import com.reandroid.arsc.util.FrameworkTable
|
|
||||||
import com.reandroid.arsc.value.Entry
|
|
||||||
import com.reandroid.common.TableEntryStore
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A high-level API for resolving resources in the resource table, which spans the entire ApkBundle.
|
|
||||||
*/
|
|
||||||
class ResourceTable(base: ResourceContainer, all: Sequence<ResourceContainer>) {
|
|
||||||
private val packageName = base.tableBlock!!.name
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [TableEntryStore] used to decode XML.
|
|
||||||
*/
|
|
||||||
internal val entryStore = TableEntryStore()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The [EncodeMaterials] to use for resolving resources and encoding XML.
|
|
||||||
*/
|
|
||||||
internal val encodeMaterials: EncodeMaterials = object : EncodeMaterials() {
|
|
||||||
/*
|
|
||||||
Our implementation is more efficient because it does not have to loop through every single entry group
|
|
||||||
when the resource id cannot be found in the TableIdentifier, which does not update when you create a new resource.
|
|
||||||
It also looks at the entire table instead of just the current package.
|
|
||||||
*/
|
|
||||||
override fun resolveLocalResourceId(type: String, name: String) = resolveLocal(type, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The resource mappings which are generated when the ApkBundle is created.
|
|
||||||
*/
|
|
||||||
private val tableIdentifier = encodeMaterials.tableIdentifier
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A table of all the resources that have been changed or added.
|
|
||||||
*/
|
|
||||||
private val modifiedResources = HashMap<String, HashMap<String, Int>>()
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve a resource id for the specified resource.
|
|
||||||
* Cannot resolve resources from the android framework.
|
|
||||||
*
|
|
||||||
* @param type The type of the resource.
|
|
||||||
* @param name The name of the resource.
|
|
||||||
* @return The id of the resource.
|
|
||||||
*/
|
|
||||||
fun resolveLocal(type: String, name: String) =
|
|
||||||
modifiedResources[type]?.get(name)
|
|
||||||
?: tableIdentifier.get(packageName, type, name)?.resourceId
|
|
||||||
?: throw ApkResourceException.InvalidReference(
|
|
||||||
type,
|
|
||||||
name
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve a resource id for the specified resource.
|
|
||||||
*
|
|
||||||
* @param reference The resource reference string.
|
|
||||||
* @return The id of the resource.
|
|
||||||
*/
|
|
||||||
fun resolve(reference: String) = try {
|
|
||||||
encodeMaterials.resolveReference(reference)
|
|
||||||
} catch (e: EncodeException) {
|
|
||||||
throw ApkResourceException.InvalidReference(reference, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify the [ResourceTable] that an [Entry] has been created or modified.
|
|
||||||
*/
|
|
||||||
internal fun registerChanged(entry: Entry) {
|
|
||||||
modifiedResources.getOrPut(entry.typeName, ::HashMap)[entry.name] = entry.resourceId
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
all.forEach {
|
|
||||||
it.tableBlock?.let { table ->
|
|
||||||
entryStore.add(table)
|
|
||||||
tableIdentifier.load(table)
|
|
||||||
}
|
|
||||||
|
|
||||||
it.resourceTable = this
|
|
||||||
}
|
|
||||||
|
|
||||||
base.also {
|
|
||||||
encodeMaterials.currentPackage = it.tableBlock
|
|
||||||
|
|
||||||
it.tableBlock!!.frameWorks.forEach { fw ->
|
|
||||||
if (fw is FrameworkTable) {
|
|
||||||
entryStore.add(fw)
|
|
||||||
encodeMaterials.addFramework(fw)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,56 +0,0 @@
|
|||||||
package app.revanced.arsc.xml
|
|
||||||
|
|
||||||
import app.revanced.arsc.resource.ResourceContainer
|
|
||||||
import app.revanced.arsc.resource.boolean
|
|
||||||
import com.reandroid.apk.xmlencoder.EncodeException
|
|
||||||
import com.reandroid.apk.xmlencoder.XMLEncodeSource
|
|
||||||
import com.reandroid.arsc.chunk.xml.ResXmlDocument
|
|
||||||
import com.reandroid.xml.XMLDocument
|
|
||||||
import com.reandroid.xml.XMLElement
|
|
||||||
import com.reandroid.xml.source.XMLDocumentSource
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Archive input source to lazily encode an [XMLDocument] after it has been modified.
|
|
||||||
*
|
|
||||||
* @param name The file name of this input source.
|
|
||||||
* @param document The [XMLDocument] to encode.
|
|
||||||
* @param resources The [ResourceContainer] to use for encoding.
|
|
||||||
*/
|
|
||||||
internal class LazyXMLEncodeSource(
|
|
||||||
name: String,
|
|
||||||
val document: XMLDocument,
|
|
||||||
private val resources: ResourceContainer
|
|
||||||
) : XMLEncodeSource(resources.resourceTable.encodeMaterials, XMLDocumentSource(name, document)) {
|
|
||||||
private var encoded = false
|
|
||||||
|
|
||||||
override fun getResXmlBlock(): ResXmlDocument {
|
|
||||||
if (encoded) return super.getResXmlBlock()
|
|
||||||
|
|
||||||
XMLEncodeSource(resources.resourceTable.encodeMaterials, XMLDocumentSource(name, document))
|
|
||||||
|
|
||||||
fun XMLElement.registerIds() {
|
|
||||||
listAttributes().forEach { attr ->
|
|
||||||
if (!attr.value.startsWith("@+id/")) return@forEach
|
|
||||||
|
|
||||||
val name = attr.value.split('/').last()
|
|
||||||
resources.setResource("id", name, boolean(false))
|
|
||||||
attr.value = "@id/$name"
|
|
||||||
}
|
|
||||||
|
|
||||||
listChildElements().forEach { it.registerIds() }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle all @+id/id_name references in the document.
|
|
||||||
document.documentElement.registerIds()
|
|
||||||
|
|
||||||
encoded = true
|
|
||||||
|
|
||||||
// This will call XMLEncodeSource.getResXmlBlock(),
|
|
||||||
// which will encode the document if it has not already been encoded.
|
|
||||||
try {
|
|
||||||
return super.getResXmlBlock()
|
|
||||||
} catch (e: EncodeException) {
|
|
||||||
throw EncodeException("Failed to encode $name", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +1,52 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.8.20" apply false
|
kotlin("jvm") version "1.6.10"
|
||||||
|
java
|
||||||
|
`maven-publish`
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "app.revanced"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlin("stdlib"))
|
||||||
|
implementation("org.ow2.asm:asm:9.2")
|
||||||
|
implementation("org.ow2.asm:asm-util:9.2")
|
||||||
|
implementation("org.ow2.asm:asm-tree:9.2")
|
||||||
|
implementation("org.ow2.asm:asm-commons:9.2")
|
||||||
|
implementation("io.github.microutils:kotlin-logging:2.1.21")
|
||||||
|
testImplementation("ch.qos.logback:logback-classic:1.2.11") // use your own logger!
|
||||||
|
testImplementation(kotlin("test"))
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
testLogging {
|
||||||
|
events("PASSED", "SKIPPED", "FAILED")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
withSourcesJar()
|
||||||
|
withJavadocJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
name = "GitHubPackages"
|
||||||
|
url = uri("https://maven.pkg.github.com/ReVancedTeam/revanced-patcher")
|
||||||
|
credentials {
|
||||||
|
username = System.getenv("GITHUB_ACTOR")
|
||||||
|
password = System.getenv("GITHUB_TOKEN")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
publications {
|
||||||
|
register<MavenPublication>("gpr") {
|
||||||
|
from(components["java"])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,2 @@
|
|||||||
org.gradle.parallel=true
|
|
||||||
org.gradle.caching=true
|
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 11.0.4
|
version = 1.0.0-dev.3
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,7 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||||
networkTimeout=10000
|
|
||||||
validateDistributionUrl=true
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user