diff --git a/BUILDING.md b/BUILDING.md index 8a9c248..80a7dea 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -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](https://stackoverflow.com/a/14994354/3286892), 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](https://mutant.lavatech.top/72x72.zip). - If you want Blob emoji patches, get 72x72 PNG copies of blobmojis with codepoints. I personally resized the png/128 folder in this [repo](https://github.com/C1710/blobmoji) (`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 `patchport.py` to easily attempt to port patches. +You can use `patchport.py` 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 patchport.py /home/ave/Downloads/dic/com.discord-841` \ No newline at end of file +Example command: `python3 patchport.py /home/ave/workbench/ctc/com.discord-968` + +#### Figuring out which patch is causing your build to break + +You can use `patchbisect.py` 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 patchbisect.py /home/ave/workbench/ctc/com.discord-968` diff --git a/patchbisect.py b/patchbisect.py new file mode 100644 index 0000000..dbd9d43 --- /dev/null +++ b/patchbisect.py @@ -0,0 +1,107 @@ +#!/bin/env python3 +import re +import sys +import os +import subprocess + +# Example invocation: +# python3 patchbisect.py /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, + f"{to_versioncode}.patch") + with open(patch_path) as f: + patch_contents = f.read() + cmd = f"patch -p1 {'-R' if reverse else ''} --no-backup-if-mismatch --force" + subprocess.run(cmd, + 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 = f.read() + 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)): + continue + + patch_path = os.path.join(cutthecord_folder, "patches", patch, + f"{to_versioncode}.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", + "blobs"]: + print(f"SKIPPED: No {to_versionname} version found for {patch}.") + continue + + # Append patch name to the list + unsure.append(patch) + + +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}") + apply_patch(patch_name) + applied.append(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": + good.extend(applied) + unsure = list(set(unsure) - set(applied)) + failcount = 1 + elif count_this_round > 1: + failcount += 1 + else: + bad.extend(applied) + unsure = list(set(unsure) - set(applied)) + failcount = 1 + + for patch_name in applied: + apply_patch(patch_name, True) + + applied.clear() + +print("Done, all patches identified as good or bad.") +print(f"Good: {', '.join(good)}") +print(f"Bad: {', '.join(bad)}")