Browse Source

Push patchbisect

Ave 2 years ago
No known key found for this signature in database GPG Key ID: 9356ABAA42C842B
  1. 18
  2. 107


@ -2,7 +2,7 @@
#### Toolchain setup
- Get apktool (due to 2 bugs present in v2.3.4, you're strongly recommended to use v2.4.0 or higher)
- Get apktool (due to 2 bugs present in v2.3.4, you're strongly recommended to use v2.4.0 or higher, I compile latest from source).
- Get a keystore, see [here](, step 1.
- If you want Mutant Standard emoji patches, get 72x72 PNG copies of latest version of mutant standard emojis with codepoints. I have a zip [here](
- If you want Blob emoji patches, get 72x72 PNG copies of blobmojis with codepoints. I personally resized the png/128 folder in this [repo]( (`find /home/ave/blobmoji/png/72 -iname '*.png' -exec convert \{\} -verbose -resize 72x72\> \{\} \;`).
@ -51,8 +51,20 @@ To get the diff, run `diff -crB -x "dist" -x "res/raw" -x "build" CleanFolder Pa
#### Porting patches
You can use `` to easily attempt to port patches.
You can use `` to easily attempt to port patches. This is what I use to port between every single version.
It's not really intelligent and doesn't do much more than manually preparing necessary patch, checking if an existing patch can be applied to a given version, replacing relevant variables required for porting various patches and eliminating offsets caused by updates, but it saves a lot of time if used carefully.
Example command: `python3 /home/ave/Downloads/dic/com.discord-841`
Example command: `python3 /home/ave/workbench/ctc/com.discord-968`
#### Figuring out which patch is causing your build to break
You can use `` to have a `git bisect`-like system of switching between patchsets to find which patch(es) is(/are) the ones that cause issues.
Simply let it do its thing, do whatever test you want to make, type `y` if it works or `n` if it doesn't and finally hit return, rinse and repeat until you find what's wrong.
There's no automated checking to allow checking for a wide range of issues like build issues, app crashing on boot, or simple functionality breakages.
Keep in mind that this doesn't account for the case of multiple patches being used together causing issues, nor does it account for conflicting patches.
Example command: `python3 /home/ave/workbench/ctc/com.discord-968`


@ -0,0 +1,107 @@
#!/bin/env python3
import re
import sys
import os
import subprocess
# Example invocation:
# python3 /home/ave/workbench/ctc/com.discord-968
# This is really bad code that I wrote between 3am and 6am
# Please do not criticize or judge my programming skills by this.
apk_folder = sys.argv[1]
cutthecord_folder = os.path.dirname(os.path.realpath(__file__))
def apply_patch(patch, reverse=False):
patch_path = os.path.join(cutthecord_folder, "patches", patch,
with open(patch_path) as f:
patch_contents =
cmd = f"patch -p1 {'-R' if reverse else ''} --no-backup-if-mismatch --force",
shell=True, input=patch_contents, text=True,
cwd=apk_folder, capture_output=True)
re_versioncode_yml = re.compile(r'versionCode: \'([0-9]+)\'')
re_versionname_yml = re.compile(r'versionName: (.+)$')
# Get version code and name
with open(os.path.join(apk_folder, "apktool.yml")) as f:
file_contents =
to_versioncode = re_versioncode_yml.findall(file_contents)[0]
to_versionname = re_versionname_yml.findall(file_contents)[0]
unsure = []
# Load list of patches
for patch in os.listdir(os.path.join(cutthecord_folder, "patches")):
# Ignore non-dirs
if not os.path.isdir(os.path.join(cutthecord_folder, "patches", patch)):
patch_path = os.path.join(cutthecord_folder, "patches", patch,
# Check if patch exists for from_version, if it doesn't, warn user
if not os.path.isfile(patch_path) and patch not in ["necessary"]:
# Don't warn on instructional patches
if patch not in ["customfont", "customring",
"bettertm", "bettertmlight",
print(f"SKIPPED: No {to_versionname} version found for {patch}.")
# Append patch name to the list
failcount = 1
applied = []
good = []
bad = []
while unsure:
print("So far...")
print(f"Unsure patches: {', '.join(unsure)}")
print(f"Good patches: {', '.join(good)}")
print(f"Bad patches: {', '.join(bad)}")
count_this_round = int(len(unsure) / failcount)
for i in range(0, count_this_round):
patch_name = unsure[i]
print(f"Applying: {patch_name}")
# Very cursed lines of code.
is_working = ""
while is_working not in ["y", "n"]:
is_working = input("Is the current patchset working? (y/n) ")
# <3 3.8
# while (is_working := input("Is the current patchset working? (y/n) ")) not in ["y", "n"]:
# continue
if is_working == "y":
unsure = list(set(unsure) - set(applied))
failcount = 1
elif count_this_round > 1:
failcount += 1
unsure = list(set(unsure) - set(applied))
failcount = 1
for patch_name in applied:
apply_patch(patch_name, True)
print("Done, all patches identified as good or bad.")
print(f"Good: {', '.join(good)}")
print(f"Bad: {', '.join(bad)}")