Добавление патча сжатия png, обновление документации

This commit is contained in:
2025-08-28 12:56:50 +03:00
parent 99ef353500
commit 73fa374423
12 changed files with 151 additions and 28 deletions
+22
View File
@@ -0,0 +1,22 @@
MIT License
Copyright (c) 2025 wowlikon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+56
View File
@@ -0,0 +1,56 @@
# Anixarty autopatcher
### Описание:
Автоматический патчер для приложения anixart.
---
### Структура проекта:
- `main.py` Главный файл
- `patches` Модули патчей
- `tools` Инструменты для модификации
- `patches/resources` Ресурсы, используемые патчами
### Установка и использование:
1. Клонируйте репозиторий:
```bash
git clone https://git.wowlikon.tech/anixart-mod/autopatcher.git
```
Требования:
- Python 3.6+
- Java 8+
- zipalign
- apksigner
- pngquant
Все остальные инструменты и зависимости будут автоматически установлены при запуске `main.py`.
2. Создайте keystore с помощью `keytool` (требуется только один раз):
```bash
keytool -genkey -v -keystore keystore.jks -alias [имя_пользователя] -keyalg RSA -keysize 2048 -validity 10000
```
2. Измените настройки мода в файле `patches/config.json`
3. Поместите оригинальный apk файла anixart в папку `original`
4. Запустите `main.py` и выберите файл apk
## ПОКА ЕЩЁ В РАЗРАБОТКЕ И ПОЭТОМУ НЕ В СКРИПТЕ
1. Перейдите в папку `anixart/dist` и запустите `zipalign`:
```bash
zipalign -p 4 anixart.apk anixart-aligned.apk
```
2. Запустите `apksigner` для подписи apk файла:
```bash
apksigner sign --ks /путь/до/keystore.jks --out anixart-modded.apk anixart-aligned.apk
```
3. Установите приложение на ваше устройство.
## Лицензия:
Этот проект лицензирован под лицензией MIT. См. [LICENSE](./LICENSE) для получения подробной информации.
### Вклад в проект:
- Seele - Все оригинальные патчи основаны на модификации приложения от Seele [[GitHub](https://github.com/seeleme) | [Telegram](https://t.me/seele_off)]
- Kentai Radiquum - Разработка неофициального сайта и помощь с изучением API [[GitHub](https://github.com/Radiquum) | [Telegram](https://t.me/radiquum)]
- ReCode Liner - Помощь в модификации приложения [[Telegram](https://t.me/recodius)]
+14 -11
View File
@@ -7,22 +7,24 @@ import importlib
import subprocess import subprocess
from tqdm import tqdm from tqdm import tqdm
def init() -> dict:
def init(apktool_jar_url: str, apktool_wrapper_url: str) -> dict:
for directory in ["original", "modified", "patches", "tools", "decompiled"]: for directory in ["original", "modified", "patches", "tools", "decompiled"]:
if not os.path.exists(directory): if not os.path.exists(directory):
os.makedirs(directory) os.makedirs(directory)
with open("./patches/config.json", "r") as config_file:
conf = json.load(config_file)
if not os.path.exists("./tools/apktool.jar"): if not os.path.exists("./tools/apktool.jar"):
try: try:
print("Скачивание Apktool...") print("Скачивание Apktool...")
jar_response = requests.get(apktool_jar_url, stream=True) jar_response = requests.get(conf["tools"]["apktool_jar_url"], stream=True)
jar_path = "tools/apktool.jar" jar_path = "tools/apktool.jar"
with open(jar_path, "wb") as f: with open(jar_path, "wb") as f:
for chunk in jar_response.iter_content(chunk_size=8192): for chunk in jar_response.iter_content(chunk_size=8192):
f.write(chunk) f.write(chunk)
wrapper_response = requests.get(apktool_wrapper_url) wrapper_response = requests.get(conf["tools"]["apktool_wrapper_url"])
wrapper_path = "tools/apktool" wrapper_path = "tools/apktool"
with open(wrapper_path, "w") as f: with open(wrapper_path, "w") as f:
f.write(wrapper_response.text) f.write(wrapper_response.text)
@@ -47,9 +49,7 @@ def init(apktool_jar_url: str, apktool_wrapper_url: str) -> dict:
print("Java не установлена. Установите Java 8 или более позднюю версию.") print("Java не установлена. Установите Java 8 или более позднюю версию.")
exit(1) exit(1)
with open("./patches/config.json", "r") as config_file: return conf
return json.load(config_file)
def select_apk() -> str: def select_apk() -> str:
apks = [] apks = []
@@ -106,6 +106,10 @@ class Patch:
self.name = name self.name = name
self.package = pkg self.package = pkg
self.applied = False self.applied = False
try:
self.priority = pkg.priority
except AttributeError:
self.priority = 0
def apply(self, conf: dict) -> bool: def apply(self, conf: dict) -> bool:
try: try:
@@ -117,10 +121,7 @@ class Patch:
return False return False
conf = init( conf = init()
apktool_jar_url="https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.12.0.jar",
apktool_wrapper_url="https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/linux/apktool",
)
apk = select_apk() apk = select_apk()
patch = decompile_apk(apk) patch = decompile_apk(apk)
@@ -131,6 +132,8 @@ for filename in os.listdir("patches/"):
module = importlib.import_module(f"patches.{module_name}") module = importlib.import_module(f"patches.{module_name}")
patches.append(Patch(module_name, module)) patches.append(Patch(module_name, module))
patches.sort(key=lambda x: x.package.priority, reverse=True)
for patch in tqdm(patches, colour="green", desc="Применение патчей"): for patch in tqdm(patches, colour="green", desc="Применение патчей"):
tqdm.write(f"Применение патча: {patch.name}") tqdm.write(f"Применение патча: {patch.name}")
patch.apply(conf) patch.apply(conf)
+3 -1
View File
@@ -1,4 +1,6 @@
# Change package icon of apk """Change application icon"""
priority = 0
from tqdm import tqdm
import time import time
+6 -2
View File
@@ -1,4 +1,6 @@
# Change api server of apk """Change api server"""
priority = 0
from tqdm import tqdm
import json import json
import requests import requests
@@ -6,6 +8,8 @@ import requests
def apply(config: dict) -> bool: def apply(config: dict) -> bool:
response = requests.get(config['server']) response = requests.get(config['server'])
if response.status_code == 200: if response.status_code == 200:
print(json.loads(response.text)) for item in json.loads(response.text)["modding"]:
tqdm.write(item)
return True return True
tqdm.write(f"Failed to fetch data {response.status_code} {response.text}")
return False return False
+5 -3
View File
@@ -1,4 +1,6 @@
# Remove unnecessary files """Remove unnecessary files"""
priority = 0
from tqdm import tqdm
import os import os
import shutil import shutil
@@ -9,9 +11,9 @@ def apply(config: dict) -> bool:
if os.path.isfile(item_path): if os.path.isfile(item_path):
os.remove(item_path) os.remove(item_path)
print(f'Удалён файл: {item_path}') tqdm.write(f'Удалён файл: {item_path}')
elif os.path.isdir(item_path): elif os.path.isdir(item_path):
if item not in config["cleanup"]["keep_dirs"]: if item not in config["cleanup"]["keep_dirs"]:
shutil.rmtree(item_path) shutil.rmtree(item_path)
print(f'Удалена папка: {item_path}') tqdm.write(f'Удалена папка: {item_path}')
return True return True
+3 -2
View File
@@ -1,8 +1,9 @@
# Change application theme """Change application theme"""
priority = 0
from tqdm import tqdm
from lxml import etree from lxml import etree
def apply(config: dict) -> bool: def apply(config: dict) -> bool:
main_color = config["theme"]["colors"]["primary"] main_color = config["theme"]["colors"]["primary"]
splash_color = config["theme"]["colors"]["secondary"] splash_color = config["theme"]["colors"]["secondary"]
+27
View File
@@ -0,0 +1,27 @@
"""Compress PNGs"""
priority = 1
from tqdm import tqdm
import os
import subprocess
def compress_pngs(root_dir):
compressed_files = []
for dirpath, _, filenames in os.walk(root_dir):
for filename in filenames:
if filename.lower().endswith(".png"):
filepath = os.path.join(dirpath, filename)
tqdm.write(f"Сжимаю: {filepath}")
try:
subprocess.run(
["pngquant", "--force", "--ext", ".png", "--quality=65-90", filepath],
check=True, capture_output=True
)
compressed_files.append(filepath)
except subprocess.CalledProcessError as e:
tqdm.write(f"Ошибка при сжатии {filepath}: {e}")
return compressed_files
def apply(config: dict) -> bool:
files = compress_pngs("./decompiled")
return len(files) > 0 and any(files)
+4
View File
@@ -1,4 +1,8 @@
{ {
"tools": {
"apktool_jar_url": "https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.12.0.jar",
"apktool_wrapper_url": "https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/linux/apktool"
},
"new_package_name": "com.wowlikon.anixart", "new_package_name": "com.wowlikon.anixart",
"server": "https://anixarty.wowlikon.tech/modding", "server": "https://anixarty.wowlikon.tech/modding",
"theme": { "theme": {
+5 -4
View File
@@ -1,13 +1,15 @@
# Change package icon of apk """Insert new files"""
priority = 0
from tqdm import tqdm
import shutil import shutil
import os import os
def apply(config: dict) -> bool: def apply(config: dict) -> bool:
# Mod first launch window # Mod first launch window
shutil.copytree( shutil.copytree(
"./patches/resources/smali_classes4/", "./decompiled/smali_classes4/" "./patches/resources/smali_classes4/",
"./decompiled/smali_classes4/"
) )
# Mod assets # Mod assets
@@ -43,5 +45,4 @@ def apply(config: dict) -> bool:
"./decompiled/res/raw/sdkinternalca.cer", "./decompiled/res/raw/sdkinternalca.cer",
) )
os.remove("./decompiled/res/font/ytsans_medium.otf")
return True return True
+3 -3
View File
@@ -1,13 +1,13 @@
# Change package name of apk """Change package name of apk"""
priority = -1
from tqdm import tqdm
import os import os
def rename_dir(src, dst): def rename_dir(src, dst):
os.makedirs(os.path.dirname(dst), exist_ok=True) os.makedirs(os.path.dirname(dst), exist_ok=True)
os.rename(src, dst) os.rename(src, dst)
def apply(config: dict) -> bool: def apply(config: dict) -> bool:
assert config["new_package_name"] is not None, "new_package_name is not configured" assert config["new_package_name"] is not None, "new_package_name is not configured"
+3 -2
View File
@@ -1,8 +1,9 @@
# Change application theme """Add new settings"""
priority = 0
from tqdm import tqdm
from lxml import etree from lxml import etree
# Generate PreferenceCategory # Generate PreferenceCategory
def make_category(ns, name, items): def make_category(ns, name, items):
cat = etree.Element("PreferenceCategory", nsmap=ns) cat = etree.Element("PreferenceCategory", nsmap=ns)