Skip to content

Commit 05b11cf

Browse files
Merge remote-tracking branch 'origin/dev' into arsclib-test
# Conflicts: # src/main/kotlin/app/morphe/cli/command/PatchCommand.kt
2 parents 4cd1b9b + 44943da commit 05b11cf

6 files changed

Lines changed: 609 additions & 36 deletions

File tree

.github/workflows/build_pull_request.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,10 @@ jobs:
2323
env:
2424
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2525
run: ./gradlew build --no-daemon
26+
27+
- name: Upload artifacts
28+
uses: actions/upload-artifact@v6
29+
with:
30+
name: morphe-patches
31+
path: build/libs/morphe-cli-*-all.jar
32+

docs/1_usage.md

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ You can list patches, patch an app, uninstall, and install an app.
77
## 🚀 Show all commands
88

99
```bash
10-
java -jar morphe-cli.jar -h
10+
java -jar morphe-cli.jar --help
1111
```
1212

1313
## 📃 List patches
@@ -21,13 +21,7 @@ java -jar morphe-cli.jar list-patches --with-packages --with-versions --with-opt
2121
To patch an app using the default list of patches, use the `patch` command:
2222

2323
```bash
24-
java -jar morphe-cli.jar patch -p patches.mpp input.apk
25-
```
26-
27-
You can also use multiple MPP files:
28-
29-
```bash
30-
java -jar morphe-cli.jar patch -p patches.mpp -p another-patches.mpp input.apk
24+
java -jar morphe-cli.jar patch --patches patches.mpp input.apk
3125
```
3226

3327
To change the default set of enabled or disabled patches, use the option `-e` or `-d` to enable or disable specific patches.
@@ -37,7 +31,7 @@ To only enable specific patches, you can use the option `--exclusive` combined w
3731
Remember that the options `-e` and `-d` match the patch's name exactly. Here is an example:
3832

3933
```bash
40-
java -jar morphe-cli.jar patch -p patches.mpp --exclusive -e "Patch name" -e "Another patch name" input.apk
34+
java -jar morphe-cli.jar patch --patches patches.mpp --exclusive -e "Patch name" -e "Another patch name" input.apk
4135
```
4236

4337
You can also use the options `--ei` or `--di` to enable or disable patches by their index.
@@ -51,13 +45,13 @@ java -jar morphe-cli.jar list-patches patches.mpp
5145
Then you can use the indices to enable or disable patches:
5246

5347
```bash
54-
java -jar morphe-cli.jar patch -p patches.mpp --ei 123 --di 456 input.apk
48+
java -jar morphe-cli.jar patch --patches patches.mpp --ei 123 --di 456 input.apk
5549
```
5650

5751
You can combine the option `-e`, `-d`, `--ei`, `--di` and `--exclusive`. Here is an example:
5852

5953
```bash
60-
java -jar morphe-cli.jar patch -p patches.mpp --exclusive -e "Patch name" --ei 123 input.apk
54+
java -jar morphe-cli.jar patch --patches patches.mpp --exclusive -e "Patch name" --ei 123 input.apk
6155
```
6256

6357

@@ -79,6 +73,8 @@ java -jar morphe-cli.jar patch -p patches.mpp --exclusive -e "Patch name" --ei 1
7973
> adb install input.apk
8074
> ```
8175
76+
## 📃 Patch options
77+
8278
Patches can have options you can set using the option `-O` alongside the option to include the patch by name or index.
8379
To know the options of a patch, use the option `--with-options` when listing patches:
8480
@@ -91,15 +87,36 @@ For example, to set the options for the patch with the name `Patch name`
9187
with the key `key1` and `key2` to `value1` and `value2` respectively, use the following command:
9288

9389
```bash
94-
java -jar morphe-cli.jar patch -p patches.mpp -e "Patch name" -Okey1=value1 -Okey2=value2 input.apk
90+
java -jar morphe-cli.jar patch --patches patches.mpp -e "Patch name" -Okey1=value1 -Okey2=value2 input.apk
9591
```
9692

9793
If you want to set the option value to `null`, you can omit the value:
9894

9995
```bash
100-
java -jar morphe-cli.jar patch -p patches.mpp -i "Patch name" -Okey1 input.apk
96+
java -jar morphe-cli.jar patch --patches patches.mpp -i "Patch name" -Okey1 input.apk
97+
```
98+
99+
## 📃 Patch option json
100+
101+
Generate a template patch options file, or update your existing file (remove invalid json, add missing json):
102+
```bash
103+
java -jar morphe-cli.jar options-create --patches patches.mpp --out options.json
101104
```
102105

106+
After modifying the json file to include/exclude patches or set any patch options, use the file with `--options-file`:
107+
```bash
108+
java -jar morphe-cli.jar patch --patches patches.mpp --options-file options.json input.apk
109+
```
110+
111+
To patch with an options.json and update the json (same functionality as `options-create` above),
112+
then add parameter `--options-update`:
113+
```bash
114+
java -jar morphe-cli.jar patch --patches patches.mpp --options-file options.json --options-update input.apk
115+
```
116+
117+
118+
## 📃 List patches
119+
103120
> [!WARNING]
104121
> Option values are usually typed. If you set a value with the wrong type, the patch can fail.
105122
> The value types can be seen when listing patches with the option `--with-options`.
@@ -131,7 +148,7 @@ java -jar morphe-cli.jar patch -p patches.mpp -i "Patch name" -Okey1 input.apk
131148
> Example command with an escaped integer as a string:
132149
>
133150
> ```bash
134-
> java -jar morphe-cli.jar -p patches.mpp -e "Patch name" -OstringKey=\'1\' input.apk
151+
> java -jar morphe-cli.jar --patches patches.mpp -e "Patch name" -OstringKey=\'1\' input.apk
135152
> ```
136153
## 📦 Install an app manually
137154

src/main/kotlin/app/morphe/cli/command/MainCommand.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ private object CLIVersionProvider : IVersionProvider {
3939
PatchCommand::class,
4040
ListPatchesCommand::class,
4141
ListCompatibleVersions::class,
42+
OptionsCommand::class,
4243
UtilityCommand::class,
4344
]
4445
)
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package app.morphe.cli.command
2+
3+
import app.morphe.cli.command.model.PatchBundle
4+
import app.morphe.cli.command.model.findMatchingBundle
5+
import app.morphe.cli.command.model.mergeWithBundle
6+
import app.morphe.cli.command.model.withUpdatedBundle
7+
import app.morphe.patcher.patch.loadPatchesFromJar
8+
import kotlinx.serialization.json.Json
9+
import picocli.CommandLine
10+
import picocli.CommandLine.Command
11+
import picocli.CommandLine.Model.CommandSpec
12+
import picocli.CommandLine.Spec
13+
import java.io.File
14+
import java.util.concurrent.Callable
15+
import java.util.logging.Logger
16+
17+
@Command(
18+
name = "options-create",
19+
description = ["Create an options JSON file for the patches and options."],
20+
)
21+
internal object OptionsCommand : Callable<Int> {
22+
23+
private const val EXIT_CODE_SUCCESS = 0
24+
private const val EXIT_CODE_ERROR = 1
25+
26+
private val logger = Logger.getLogger(this::class.java.name)
27+
28+
@Spec
29+
private lateinit var spec: CommandSpec
30+
31+
@CommandLine.Option(
32+
names = ["-p", "--patches"],
33+
description = ["One or more paths to MPP files."],
34+
required = true,
35+
)
36+
@Suppress("unused")
37+
private fun setPatchesFile(patchesFiles: Set<File>) {
38+
patchesFiles.firstOrNull { !it.exists() }?.let {
39+
throw CommandLine.ParameterException(spec.commandLine(), "${it.name} can't be found")
40+
}
41+
this.patchesFiles = patchesFiles
42+
}
43+
44+
private var patchesFiles = emptySet<File>()
45+
46+
@CommandLine.Option(
47+
names = ["-o", "--out"],
48+
description = ["Path to the output JSON file."],
49+
required = true,
50+
)
51+
private lateinit var outputFile: File
52+
53+
@CommandLine.Option(
54+
names = ["-f", "--filter-package-name"],
55+
description = ["Filter patches by compatible package name."],
56+
)
57+
private var packageName: String? = null
58+
59+
private val json = Json { prettyPrint = true }
60+
61+
override fun call(): Int {
62+
return try {
63+
logger.info("Loading patches")
64+
65+
val patches = loadPatchesFromJar(patchesFiles)
66+
67+
val filtered = packageName?.let { pkg ->
68+
patches.filter { patch ->
69+
patch.compatiblePackages?.any { (name, _) -> name == pkg } ?: true
70+
}.toSet()
71+
} ?: patches
72+
73+
// Read existing bundles list if the file already exists
74+
val existingBundles: List<PatchBundle>? = if (outputFile.exists()) {
75+
try {
76+
Json.decodeFromString<List<PatchBundle>>(outputFile.readText())
77+
} catch (e: Exception) {
78+
logger.warning("Could not parse existing file, creating fresh: ${e.message}")
79+
null
80+
}
81+
} else null
82+
83+
// Find the bundle matching the current .mpp file(s), merge with it (or create fresh)
84+
val existingBundle = existingBundles?.findMatchingBundle(patchesFiles)
85+
val updatedBundle = filtered.mergeWithBundle(
86+
existing = existingBundle,
87+
sourceFiles = patchesFiles,
88+
)
89+
90+
// Replace the matching entry in the list (or start a new list)
91+
val updatedBundles = existingBundles?.withUpdatedBundle(updatedBundle)
92+
?: listOf(updatedBundle)
93+
94+
outputFile.absoluteFile.parentFile?.mkdirs()
95+
outputFile.writeText(json.encodeToString(updatedBundles))
96+
97+
if (existingBundle != null) {
98+
val existingNames = existingBundle.patches.keys.map { it.lowercase() }.toSet()
99+
val newNames = updatedBundle.patches.keys.map { it.lowercase() }.toSet()
100+
val added = newNames - existingNames
101+
val removed = existingNames - newNames
102+
val kept = newNames.intersect(existingNames)
103+
logger.info("Updated bundle in options file at ${outputFile.path}")
104+
logger.info(" ${kept.size} patches preserved, ${added.size} added, ${removed.size} removed")
105+
} else {
106+
logger.info("Created new bundle in options file at ${outputFile.path} with ${updatedBundle.patches.size} patches")
107+
}
108+
109+
EXIT_CODE_SUCCESS
110+
} catch (e: Exception) {
111+
logger.severe("Failed to export options: ${e.message}")
112+
EXIT_CODE_ERROR
113+
}
114+
}
115+
}

0 commit comments

Comments
 (0)