From 1a7ad17c87cf70c518dd434e9df9f05216fe32df Mon Sep 17 00:00:00 2001 From: clienthax Date: Tue, 10 Aug 2021 19:56:58 +0100 Subject: [PATCH] Add xml-port support. --- Dockerfile | 7 +- resources/patchport-state.json | 2 +- resources/scripts/ctcci/ctcci.py | 110 ++++++++++--------------- resources/scripts/patchport.py | 137 ++++++++++++++++++++++++------- 4 files changed, 154 insertions(+), 102 deletions(-) diff --git a/Dockerfile b/Dockerfile index f62451f..397063c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,6 +17,9 @@ RUN mkdir ${CTC_FOLDER} RUN mkdir ${CTC_FOLDER}/tools RUN wget -O ${CTC_FOLDER}/tools/apktool.jar https://f001.backblazeb2.com/file/avepub/apktool-cli-all.jar +# Grab xml-patch (https://github.com/dnault/xml-patch) +RUN wget -O ${CTC_FOLDER}/tools/xml-patch.jar https://jcenter.bintray.com/com/github/dnault/xml-patch/0.3.1/xml-patch-0.3.1.jar + # Grab emoji sets ENV EMOJI_PATH=${CTC_FOLDER}/emojis ENV EMOJI_MUTANT_PATH=${EMOJI_PATH}/mutant @@ -49,14 +52,10 @@ RUN keytool -genkey -alias test \ -dname "CN=Test, OU=Test, O=Test, L=Test, S=Test, C=Test" \ -storepass password -keypass password -# Git will moan about this otherwise.. -RUN git config pull.rebase false - # Make folder for git repo RUN mkdir ${CTC_FOLDER}/gitrepo WORKDIR ${CTC_FOLDER}/gitrepo - # paths in ctcconfig in docker # container fs # /opt/ctc/gitrepo diff --git a/resources/patchport-state.json b/resources/patchport-state.json index 497642f..865816f 100644 --- a/resources/patchport-state.json +++ b/resources/patchport-state.json @@ -1 +1 @@ -{"versionname": "49.13", "versioncode": "1340"} \ No newline at end of file +{"versionname": "87.2 - Alpha", "versioncode": "87202"} \ No newline at end of file diff --git a/resources/scripts/ctcci/ctcci.py b/resources/scripts/ctcci/ctcci.py index b372d43..73a987d 100644 --- a/resources/scripts/ctcci/ctcci.py +++ b/resources/scripts/ctcci/ctcci.py @@ -14,14 +14,20 @@ FORCE = False DEFAULT_PATCHES = ["necessary", "branding", "customversion"] OPTIONAL_PATCHES = ["necessary", "blobs"] +def xmlpatch(patch_file, workdir, patch_name=""): + if not os.path.exists(patch_file): + # Allow missing patches + return + + print(f"Applying {patch_file}") + + subprocess.run(f"java -cp /opt/ctc/tools/xml-patch.jar com.github.dnault.xmlpatch.BatchPatcher --patch {patch_file} --srcdir {workdir}", + shell=True, check=True, cwd=workdir) def patch(patch_file, workdir, patch_name=""): if not os.path.exists(patch_file): - # Allow missing optional patches - if patch_name in OPTIONAL_PATCHES: - return - - raise FileNotFoundError(f"No patches with name \"{patch_file}\" :(") + # Allow missing patches + return print(f"Applying {patch_file}") @@ -78,42 +84,34 @@ shutil.copytree(BASE_APK_PATH, WORK_APK_PATH) # Go through patches and apply every single one of them for patch_name in PATCHES: - pre_script = os.path.join(REPO_FOLDER, "resources/patches", - patch_name, f"{VERSION}-pre.sh") + pre_script = os.path.join(REPO_FOLDER, "resources/patches", patch_name, f"{VERSION}-pre.sh") if os.path.exists(pre_script): - subprocess.run(f"bash {pre_script}", - shell=True, - cwd=WORK_APK_PATH) + subprocess.run(f"bash {pre_script}", shell=True, cwd=WORK_APK_PATH) + pre_script = os.path.join(REPO_FOLDER, "resources/xmlpatches", patch_name, f"{VERSION}-pre.sh") + if os.path.exists(pre_script): + subprocess.run(f"bash {pre_script}", shell=True, cwd=WORK_APK_PATH) # Apply custom emoji patches if patch_name in ["mutant", "blobs"]: print(f"Applying {patch_name} emoji patch") - patch_script = os.path.join(REPO_FOLDER, "resources/patches", - patch_name, "emojireplace.py") - subprocess.run(f"{PYTHON_BIN} {patch_script}", - shell=True, - cwd=WORK_APK_PATH) + patch_script = os.path.join(REPO_FOLDER, "resources/patches", patch_name, "emojireplace.py") + subprocess.run(f"{PYTHON_BIN} {patch_script}", shell=True, cwd=WORK_APK_PATH) # Apply custom emoji patches elif patch_name == "customtheme": print(f"Applying splash patch") - splash = os.path.join(REPO_FOLDER, "resources/patches", - patch_name, "asset_loading.png") - patch_script = os.path.join(REPO_FOLDER, "resources/patches", - patch_name, "fixsplash.sh") + splash = os.path.join(REPO_FOLDER, "resources/patches", patch_name, "asset_loading.png") + patch_script = os.path.join(REPO_FOLDER, "resources/patches", patch_name, "fixsplash.sh") - subprocess.run(f"bash {patch_script} {splash}", - shell=True, - cwd=WORK_APK_PATH) + subprocess.run(f"bash {patch_script} {splash}", shell=True, cwd=WORK_APK_PATH) - patch_file = os.path.join(REPO_FOLDER, "resources/patches", - patch_name, f"{VERSION}.patch") + patch_file = os.path.join(REPO_FOLDER, "resources/patches", patch_name, f"{VERSION}.patch") + xml_patch_file = os.path.join(REPO_FOLDER, "resources/xmlpatches", patch_name, f"{VERSION}.xml") # Apply custom version patches if patch_name == "customversion": print(f"Applying custom version") - patch_script = os.path.join(REPO_FOLDER, "resources/patches", - patch_name, "addpatch.py") + patch_script = os.path.join(REPO_FOLDER, "resources/patches", patch_name, "addpatch.py") subprocess.run(f"{PYTHON_BIN} {patch_script} {patch_file} " f"{' '.join(PATCHES)}", @@ -126,44 +124,31 @@ for patch_name in PATCHES: print(f"Applying branding icon patch") if BRANCH in ICONS: shutil.copyfile(ICONS[BRANCH], - os.path.join(WORK_APK_PATH, "res", - "mipmap-xxxhdpi", "logo_debug.png")) + os.path.join(WORK_APK_PATH, "res", "mipmap-xxxhdpi", "logo_debug.png")) elif "default" in ICONS: shutil.copyfile(ICONS["default"], - os.path.join(WORK_APK_PATH, "res", - "mipmap-xxxhdpi", "logo_debug.png")) + os.path.join(WORK_APK_PATH, "res", "mipmap-xxxhdpi", "logo_debug.png")) if BRANCH in DYN_ICONS: shutil.copyfile(DYN_ICONS[BRANCH]["bg"], - os.path.join(WORK_APK_PATH, "res", - "mipmap-xxxhdpi", - "ic_launcher_background.png")) + os.path.join(WORK_APK_PATH, "res", "mipmap-xxxhdpi", "ic_launcher_background.png")) shutil.copyfile(DYN_ICONS[BRANCH]["fg"], - os.path.join(WORK_APK_PATH, "res", - "mipmap-xxxhdpi", - "ic_launcher_foreground.png")) + os.path.join(WORK_APK_PATH, "res", "mipmap-xxxhdpi", "ic_launcher_foreground.png")) elif "default" in ICONS: shutil.copyfile(DYN_ICONS["default"]["bg"], - os.path.join(WORK_APK_PATH, "res", - "mipmap-xxxhdpi", - "ic_launcher_background.png")) + os.path.join(WORK_APK_PATH, "res", "mipmap-xxxhdpi", "ic_launcher_background.png")) shutil.copyfile(DYN_ICONS["default"]["fg"], - os.path.join(WORK_APK_PATH, "res", - "mipmap-xxxhdpi", - "ic_launcher_foreground.png")) + os.path.join(WORK_APK_PATH, "res", "mipmap-xxxhdpi", "ic_launcher_foreground.png")) - patch_script = os.path.join(REPO_FOLDER, "resources/patches", - patch_name, "customicon.sh") + patch_script = os.path.join(REPO_FOLDER, "resources/patches", patch_name, "customicon.sh") subprocess.run(f"bash {patch_script}", shell=True, cwd=WORK_APK_PATH) - patch_script = os.path.join(REPO_FOLDER, "resources/patches", - patch_name, "customdynamicicon.sh") + patch_script = os.path.join(REPO_FOLDER, "resources/patches", patch_name, "customdynamicicon.sh") subprocess.run(f"bash {patch_script}", shell=True, cwd=WORK_APK_PATH) - patch_script = os.path.join(REPO_FOLDER, "resources/patches", - patch_name, "addpatch.py") + patch_script = os.path.join(REPO_FOLDER, "resources/patches", patch_name, "addpatch.py") # Hell code app_name = APP_NAMES.get(BRANCH, APP_NAMES.get("default", "CutTheCord")) @@ -176,13 +161,9 @@ for patch_name in PATCHES: patch(patch_file.replace(".patch", "-custom.patch"), WORK_APK_PATH) elif patch_name in ["bettertm", "bettertmlight"]: print(f"Applying bettertm emoji patch") - patch_dir = os.path.join(REPO_FOLDER, "resources/patches", - patch_name) - patch_script = os.path.join(REPO_FOLDER, "resources/patches", - patch_name, "bettertm.sh") - subprocess.run(f"bash {patch_script} {patch_dir}", - shell=True, - cwd=WORK_APK_PATH) + patch_dir = os.path.join(REPO_FOLDER, "resources/patches", patch_name) + patch_script = os.path.join(REPO_FOLDER, "resources/patches", patch_name, "bettertm.sh") + subprocess.run(f"bash {patch_script} {patch_dir}", shell=True, cwd=WORK_APK_PATH) # Apply custom ringtone elif patch_name == "customring": print(f"Applying custom ringtone") @@ -191,9 +172,7 @@ for patch_name in PATCHES: else: CUSTOM_RINGTONE = RINGTONES["default"] - shutil.copyfile(CUSTOM_RINGTONE, - os.path.join(WORK_APK_PATH, "res", - "raw", "call_ringing.mp3")) + shutil.copyfile(CUSTOM_RINGTONE, os.path.join(WORK_APK_PATH, "res", "raw", "call_ringing.mp3")) # Apply custom fonts elif patch_name == "customfont": print(f"Applying custom font") @@ -203,19 +182,18 @@ for patch_name in PATCHES: fonts = FONTS["default"] for font in fonts: - shutil.copyfile(fonts[font], - os.path.join(WORK_APK_PATH, "res", - "font", font)) + shutil.copyfile(fonts[font], os.path.join(WORK_APK_PATH, "res", "font", font)) # Apply any other patches else: patch(patch_file, WORK_APK_PATH, patch_name) + xmlpatch(xml_patch_file, WORK_APK_PATH, patch_name) - post_script = os.path.join(REPO_FOLDER, "resources/patches", - patch_name, f"{VERSION}-post.sh") + post_script = os.path.join(REPO_FOLDER, "resources/patches", patch_name, f"{VERSION}-post.sh") if os.path.exists(post_script): - subprocess.run(f"bash {post_script}", - shell=True, - cwd=WORK_APK_PATH) + subprocess.run(f"bash {post_script}", shell=True, cwd=WORK_APK_PATH) + post_script = os.path.join(REPO_FOLDER, "resources/xmlpatches", patch_name, f"{VERSION}-post.sh") + if os.path.exists(post_script): + subprocess.run(f"bash {post_script}", shell=True, cwd=WORK_APK_PATH) # Pack the APK subprocess.run(f"{APKTOOL_BIN} b discord", diff --git a/resources/scripts/patchport.py b/resources/scripts/patchport.py index 134b787..b9e485d 100644 --- a/resources/scripts/patchport.py +++ b/resources/scripts/patchport.py @@ -75,7 +75,7 @@ def fix_offset(patch_contents): return patch_out - +# TODO replace with xml-patch version def make_necessary(version_name, version_code): # OH GOD OH FUCK shutil.rmtree(tmp_folder, ignore_errors=True) @@ -88,11 +88,9 @@ def make_necessary(version_name, version_code): with open(os.path.join(tmp_folder, "AndroidManifest.xml")) as fin: filec = fin.read() incorrect_versioncode = re_versioncode_xml.findall(filec)[0] - filec = filec.replace(incorrect_versioncode, - f'platformBuildVersionCode="{to_versioncode}"') + filec = filec.replace(incorrect_versioncode, f'platformBuildVersionCode="{to_versioncode}"') incorrect_versionname = re_versionname_xml.findall(filec)[0] - filec = filec.replace(incorrect_versionname, - f'platformBuildVersionName="{to_versionname}"') + filec = filec.replace(incorrect_versionname, f'platformBuildVersionName="{to_versionname}"') with open(os.path.join(tmp_folder, "AndroidManifest.xml"), "w") as fout: fout.write(filec) @@ -134,37 +132,30 @@ with open(os.path.join(apk_folder, "res", "values", "strings.xml")) as f: failures = [] -for patch in os.listdir(os.path.join(cutthecord_folder, "resources/patches")): +for patch in os.listdir(os.path.join(cutthecord_folder, "resources/xmlpatches")): if debug: - print(f"going over patch: {patch}") + print(f"going over xml patch: {patch}") # Ignore non-dirs - if not os.path.isdir(os.path.join(cutthecord_folder, "resources/patches", patch)): + if not os.path.isdir(os.path.join(cutthecord_folder, "resources/xmlpatches", patch)): if debug: print(f"patch is not a folder, skipping: {patch}") continue - pre_in_path = os.path.join(cutthecord_folder, "resources/patches", patch, - f"{from_versioncode}-pre.sh") - post_in_path = os.path.join(cutthecord_folder, "resources/patches", patch, - f"{from_versioncode}-post.sh") - pre_out_path = os.path.join(cutthecord_folder, "resources/patches", patch, - f"{to_versioncode}-pre.sh") - post_out_path = os.path.join(cutthecord_folder, "resources/patches", patch, - f"{to_versioncode}-post.sh") + pre_in_path = os.path.join(cutthecord_folder, "resources/xmlpatches", patch, f"{from_versioncode}-pre.sh") + post_in_path = os.path.join(cutthecord_folder, "resources/xmlpatches", patch, f"{from_versioncode}-post.sh") + pre_out_path = os.path.join(cutthecord_folder, "resources/xmlpatches", patch, f"{to_versioncode}-pre.sh") + post_out_path = os.path.join(cutthecord_folder, "resources/xmlpatches", patch, f"{to_versioncode}-post.sh") - patch_path = os.path.join(cutthecord_folder, "resources/patches", patch, - f"{from_versioncode}.patch") - out_path = os.path.join(cutthecord_folder, "resources/patches", patch, - f"{to_versioncode}.patch") - readme_path = os.path.join(cutthecord_folder, "resources/patches", patch, "README.md") + patch_path = os.path.join(cutthecord_folder, "resources/xmlpatches", patch, f"{from_versioncode}.xml") + out_path = os.path.join(cutthecord_folder, "resources/xmlpatches", patch, f"{to_versioncode}.xml") + tmp_out_path = os.path.join("/tmp/", patch, f"{to_versioncode}.xml") + readme_path = os.path.join(cutthecord_folder, "resources/xmlpatches", patch, "README.md") # Handle copying of versioned scripts, untested and dirty! - script_path = os.path.join(cutthecord_folder, "resources/patches", patch, - f"{from_versioncode}.sh") + script_path = os.path.join(cutthecord_folder, "resources/xmlpatches", patch, f"{from_versioncode}.sh") if os.path.exists(script_path): - script_out_path = os.path.join(cutthecord_folder, "resources/patches", patch, - f"{to_versioncode}.sh") + script_out_path = os.path.join(cutthecord_folder, "resources/xmlpatches", patch, f"{to_versioncode}.sh") with open(script_path) as f: with open(script_out_path, "w") as f2: f2.write(f.read()) @@ -172,9 +163,7 @@ for patch in os.listdir(os.path.join(cutthecord_folder, "resources/patches")): # 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"]: + if patch not in ["customfont", "customring", "bettertm", "bettertmlight", "blobs"]: print(f"SKIPPED: No {from_versionname} version found for {patch}.") continue @@ -188,6 +177,94 @@ for patch in os.listdir(os.path.join(cutthecord_folder, "resources/patches")): shutil.copyfile(post_in_path, post_out_path) print(f"POST COPIED: {patch}'s post script was copied.") + + # Create necessary instead of porting it. + if patch == "necessary": + patch_contents = make_necessary(to_versioncode, to_versionname) + else: + # Get a modified version of the patch + patch_contents = modify_patch(patch, patch_path) + + # Pass the new patch to patch command and get it to attempt to patch + with open(tmp_out_path, "w") as f: + f.write(patch_contents) + out = subprocess.run("java -cp /opt/ctc/tools/xml-patch.jar com.github.dnault.xmlpatch.BatchPatcher --patch {tmp_out_path} --srcdir {apk_folder} --destdir /tmp/", shell=True, + cwd=apk_folder, text=True, + capture_output=True) + + # Check for issues + if "ERROR" in out.stdout or "EXCEPTION" in out.stdout: + print(f"FAILED: {patch} failed, please fix by hand.") + failures.append(patch) + out_path += "-failed" + + if debug: + print(out.stdout) + + # Apply patch to main APK folder too + if patch in ["necessary"]: + apply_patch(patch_contents) + + if from_versionname != to_versionname: + # Add supported version to readme of that patch, hacky + # https://stackoverflow.com/a/35130508/3286892 + with open(readme_path, 'r') as f: + readme_text = f.read().replace(f'- {from_versionname}\n', f'- {from_versionname}\n' f'- {to_versionname}\n') + with open(readme_path, "w") as f: + f.write(readme_text) + + # Save ported patch + with open(out_path, "w") as f: + f.write(patch_contents) + + if not out_path.endswith("-failed"): + print(f"PORTED: {patch} was successfully ported.") + +for patch in os.listdir(os.path.join(cutthecord_folder, "resources/patches")): + if debug: + print(f"going over patch: {patch}") + + # Ignore non-dirs + if not os.path.isdir(os.path.join(cutthecord_folder, "resources/patches", patch)): + if debug: + print(f"patch is not a folder, skipping: {patch}") + continue + + pre_in_path = os.path.join(cutthecord_folder, "resources/patches", patch, f"{from_versioncode}-pre.sh") + post_in_path = os.path.join(cutthecord_folder, "resources/patches", patch, f"{from_versioncode}-post.sh") + pre_out_path = os.path.join(cutthecord_folder, "resources/patches", patch, f"{to_versioncode}-pre.sh") + post_out_path = os.path.join(cutthecord_folder, "resources/patches", patch, f"{to_versioncode}-post.sh") + + patch_path = os.path.join(cutthecord_folder, "resources/patches", patch, f"{from_versioncode}.patch") + out_path = os.path.join(cutthecord_folder, "resources/patches", patch, f"{to_versioncode}.patch") + readme_path = os.path.join(cutthecord_folder, "resources/patches", patch, "README.md") + + # Handle copying of versioned scripts, untested and dirty! + script_path = os.path.join(cutthecord_folder, "resources/patches", patch, f"{from_versioncode}.sh") + if os.path.exists(script_path): + script_out_path = os.path.join(cutthecord_folder, "resources/patches", patch, f"{to_versioncode}.sh") + with open(script_path) as f: + with open(script_out_path, "w") as f2: + f2.write(f.read()) + + # 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 {from_versionname} version found for {patch}.") + continue + + # Check if pre-script exists, if it does copy it + if os.path.isfile(pre_in_path): + shutil.copyfile(pre_in_path, pre_out_path) + print(f"PRE COPIED: {patch}'s pre script was copied.") + + # Check if post-script exists, if it does copy it + if os.path.isfile(post_in_path): + shutil.copyfile(post_in_path, post_out_path) + print(f"POST COPIED: {patch}'s post script was copied.") + + # Create necessary instead of porting it. if patch == "necessary": patch_contents = make_necessary(to_versioncode, to_versionname) @@ -226,9 +303,7 @@ for patch in os.listdir(os.path.join(cutthecord_folder, "resources/patches")): # Add supported version to readme of that patch, hacky # https://stackoverflow.com/a/35130508/3286892 with open(readme_path, 'r') as f: - readme_text = f.read().replace(f'- {from_versionname}\n', - f'- {from_versionname}\n' - f'- {to_versionname}\n') + readme_text = f.read().replace(f'- {from_versionname}\n', f'- {from_versionname}\n' f'- {to_versionname}\n') with open(readme_path, "w") as f: f.write(readme_text)