forked from anixart-mod/patcher
206 lines
7.0 KiB
Python
206 lines
7.0 KiB
Python
import os
|
||
import sys
|
||
import json
|
||
import requests
|
||
import colorama
|
||
import importlib
|
||
import traceback
|
||
import subprocess
|
||
from tqdm import tqdm
|
||
|
||
def init() -> dict:
|
||
for directory in ["original", "modified", "patches", "tools", "decompiled"]:
|
||
if not os.path.exists(directory):
|
||
os.makedirs(directory)
|
||
|
||
with open("./config.json", "r") as config_file:
|
||
conf = json.load(config_file)
|
||
|
||
if not os.path.exists("./tools/apktool.jar"):
|
||
try:
|
||
print("Скачивание Apktool...")
|
||
jar_response = requests.get(conf["tools"]["apktool_jar_url"], stream=True)
|
||
jar_path = "tools/apktool.jar"
|
||
with open(jar_path, "wb") as f:
|
||
for chunk in jar_response.iter_content(chunk_size=8192):
|
||
f.write(chunk)
|
||
|
||
wrapper_response = requests.get(conf["tools"]["apktool_wrapper_url"])
|
||
wrapper_path = "tools/apktool"
|
||
with open(wrapper_path, "w") as f:
|
||
f.write(wrapper_response.text)
|
||
os.chmod(wrapper_path, 0o755)
|
||
|
||
except Exception as e:
|
||
print(f"Ошибка при скачивании Apktool: {e}")
|
||
exit(1)
|
||
|
||
try:
|
||
result = subprocess.run(
|
||
["java", "-version"], capture_output=True, text=True, check=True
|
||
)
|
||
|
||
version_line = result.stderr.splitlines()[0]
|
||
if "1.8" in version_line or any(f"{i}." in version_line for i in range(9, 100)):
|
||
print("Java 8 или более поздняя версия установлена.")
|
||
else:
|
||
print("Java 8 или более поздняя версия не установлена.")
|
||
sys.exit(1)
|
||
except subprocess.CalledProcessError:
|
||
print("Java не установлена. Установите Java 8 или более позднюю версию.")
|
||
exit(1)
|
||
|
||
return conf
|
||
|
||
def select_apk() -> str:
|
||
apks = []
|
||
for file in os.listdir("original"):
|
||
if file.endswith(".apk") and os.path.isfile(os.path.join("original", file)):
|
||
apks.append(file)
|
||
|
||
if not apks:
|
||
print("Нет файлов .apk в текущей директории")
|
||
sys.exit(1)
|
||
|
||
if len(apks) == 1:
|
||
apk = apks[0]
|
||
print(f"Выбран файл {apk}")
|
||
return apk
|
||
|
||
while True:
|
||
print("Выберете файл для модификации")
|
||
for index, apk in enumerate(apks):
|
||
print(f"{index + 1}. {apk}")
|
||
print("0. Exit")
|
||
|
||
try:
|
||
selected_index = int(input("\nВведите номер файла: "))
|
||
if selected_index == 0:
|
||
sys.exit(0)
|
||
elif selected_index > len(apks):
|
||
print("Неверный номер файла")
|
||
else:
|
||
apk = apks[selected_index - 1]
|
||
print(f"Выбран файл {apk}")
|
||
return apk
|
||
except ValueError:
|
||
print("Неверный формат ввода")
|
||
except KeyboardInterrupt:
|
||
print("Прервано пользователем")
|
||
sys.exit(0)
|
||
|
||
|
||
def decompile_apk(apk: str):
|
||
print("Декомпилируем apk...")
|
||
try:
|
||
result = subprocess.run(
|
||
"tools/apktool d -f -o decompiled " + os.path.join("original", apk),
|
||
shell=True,
|
||
check=True,
|
||
text=True,
|
||
stdout=subprocess.DEVNULL,
|
||
stderr=subprocess.PIPE,
|
||
)
|
||
except subprocess.CalledProcessError as e:
|
||
print("Ошибка при выполнении команды:")
|
||
print(e.stderr)
|
||
sys.exit(1)
|
||
|
||
|
||
def compile_apk(apk: str):
|
||
print("Компилируем apk...")
|
||
try:
|
||
subprocess.run(
|
||
"tools/apktool b decompiled -o " + os.path.join("modified", apk),
|
||
shell=True,
|
||
check=True,
|
||
text=True,
|
||
stdout=subprocess.DEVNULL,
|
||
stderr=subprocess.PIPE,
|
||
)
|
||
subprocess.run(
|
||
"zipalign -v 4 " + os.path.join("modified", apk) + " " + os.path.join("modified", apk.replace(".apk", "-aligned.apk")),
|
||
shell=True,
|
||
check=True,
|
||
text=True,
|
||
stdout=subprocess.DEVNULL,
|
||
stderr=subprocess.PIPE,
|
||
)
|
||
subprocess.run(
|
||
"apksigner sign " +
|
||
"--v1-signing-enabled false " +
|
||
"--v2-signing-enabled true " +
|
||
"--v3-signing-enabled true " +
|
||
"--ks keystore.jks " +
|
||
"--ks-pass file:keystore.pass " +
|
||
"--out " + os.path.join("modified", apk.replace(".apk", "-mod.apk")) +
|
||
" " + os.path.join("modified", apk.replace(".apk", "-aligned.apk")),
|
||
shell=True,
|
||
check=True,
|
||
text=True,
|
||
stdout=subprocess.DEVNULL,
|
||
stderr=subprocess.PIPE,
|
||
)
|
||
except subprocess.CalledProcessError as e:
|
||
print("Ошибка при выполнении команды:")
|
||
print(e.stderr)
|
||
sys.exit(1)
|
||
|
||
|
||
class Patch:
|
||
def __init__(self, name, pkg):
|
||
self.name = name
|
||
self.package = pkg
|
||
self.applied = False
|
||
try:
|
||
self.priority = pkg.priority
|
||
except AttributeError:
|
||
self.priority = 0
|
||
|
||
def apply(self, conf: dict) -> bool:
|
||
try:
|
||
self.applied = self.package.apply(conf)
|
||
return True
|
||
except Exception as e:
|
||
print(f"Ошибка при применении патча {self.name}: {e}")
|
||
print(type(e), e.args)
|
||
traceback.print_exc()
|
||
return False
|
||
|
||
|
||
conf = init()
|
||
apk = select_apk()
|
||
patch = decompile_apk(apk)
|
||
|
||
patches = []
|
||
for filename in os.listdir("patches/"):
|
||
if filename.endswith(".py") and filename != "__init__.py" and not filename.startswith("todo_"):
|
||
module_name = filename[:-3]
|
||
module = importlib.import_module(f"patches.{module_name}")
|
||
patches.append(Patch(module_name, module))
|
||
|
||
patches.sort(key=lambda x: x.package.priority, reverse=True)
|
||
|
||
for patch in tqdm(patches, colour="green", desc="Применение патчей"):
|
||
tqdm.write(f"Применение патча: {patch.name}")
|
||
patch.apply(conf)
|
||
|
||
statuses = {}
|
||
for patch in patches:
|
||
statuses[patch.name] = patch.applied
|
||
marker = colorama.Fore.GREEN + "✔" if patch.applied else colorama.Fore.RED + "✘"
|
||
print(f"{marker}{colorama.Style.RESET_ALL} {patch.name}")
|
||
|
||
if all(statuses.values()):
|
||
print(f"{colorama.Fore.GREEN}Все патчи успешно применены{colorama.Style.RESET_ALL}")
|
||
compile_apk(apk)
|
||
elif any(statuses.values()):
|
||
print(f"{colorama.Fore.YELLOW}⚠{colorama.Style.RESET_ALL} Некоторые патчи не были успешно применены")
|
||
if input("Продолжить? (y/n): ").lower() == "y":
|
||
compile_apk(apk)
|
||
else:
|
||
print(colorama.Fore.RED + "Операция отменена" + colorama.Style.RESET_ALL)
|
||
else:
|
||
print(f"{colorama.Fore.RED}Ни один патч не был успешно применен{colorama.Style.RESET_ALL}")
|
||
sys.exit(1)
|