Добавление патча сжатия png, обновление документации
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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)]
|
||||
@@ -7,22 +7,24 @@ import importlib
|
||||
import subprocess
|
||||
from tqdm import tqdm
|
||||
|
||||
|
||||
def init(apktool_jar_url: str, apktool_wrapper_url: str) -> dict:
|
||||
def init() -> dict:
|
||||
for directory in ["original", "modified", "patches", "tools", "decompiled"]:
|
||||
if not os.path.exists(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"):
|
||||
try:
|
||||
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"
|
||||
with open(jar_path, "wb") as f:
|
||||
for chunk in jar_response.iter_content(chunk_size=8192):
|
||||
f.write(chunk)
|
||||
|
||||
wrapper_response = requests.get(apktool_wrapper_url)
|
||||
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)
|
||||
@@ -47,9 +49,7 @@ def init(apktool_jar_url: str, apktool_wrapper_url: str) -> dict:
|
||||
print("Java не установлена. Установите Java 8 или более позднюю версию.")
|
||||
exit(1)
|
||||
|
||||
with open("./patches/config.json", "r") as config_file:
|
||||
return json.load(config_file)
|
||||
|
||||
return conf
|
||||
|
||||
def select_apk() -> str:
|
||||
apks = []
|
||||
@@ -106,6 +106,10 @@ class Patch:
|
||||
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:
|
||||
@@ -117,10 +121,7 @@ class Patch:
|
||||
return False
|
||||
|
||||
|
||||
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",
|
||||
)
|
||||
conf = init()
|
||||
apk = select_apk()
|
||||
patch = decompile_apk(apk)
|
||||
|
||||
@@ -131,6 +132,8 @@ for filename in os.listdir("patches/"):
|
||||
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)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# Change package icon of apk
|
||||
"""Change application icon"""
|
||||
priority = 0
|
||||
from tqdm import tqdm
|
||||
|
||||
import time
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# Change api server of apk
|
||||
"""Change api server"""
|
||||
priority = 0
|
||||
from tqdm import tqdm
|
||||
|
||||
import json
|
||||
import requests
|
||||
@@ -6,6 +8,8 @@ import requests
|
||||
def apply(config: dict) -> bool:
|
||||
response = requests.get(config['server'])
|
||||
if response.status_code == 200:
|
||||
print(json.loads(response.text))
|
||||
for item in json.loads(response.text)["modding"]:
|
||||
tqdm.write(item)
|
||||
return True
|
||||
tqdm.write(f"Failed to fetch data {response.status_code} {response.text}")
|
||||
return False
|
||||
|
||||
+5
-3
@@ -1,4 +1,6 @@
|
||||
# Remove unnecessary files
|
||||
"""Remove unnecessary files"""
|
||||
priority = 0
|
||||
from tqdm import tqdm
|
||||
|
||||
import os
|
||||
import shutil
|
||||
@@ -9,9 +11,9 @@ def apply(config: dict) -> bool:
|
||||
|
||||
if os.path.isfile(item_path):
|
||||
os.remove(item_path)
|
||||
print(f'Удалён файл: {item_path}')
|
||||
tqdm.write(f'Удалён файл: {item_path}')
|
||||
elif os.path.isdir(item_path):
|
||||
if item not in config["cleanup"]["keep_dirs"]:
|
||||
shutil.rmtree(item_path)
|
||||
print(f'Удалена папка: {item_path}')
|
||||
tqdm.write(f'Удалена папка: {item_path}')
|
||||
return True
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# Change application theme
|
||||
"""Change application theme"""
|
||||
priority = 0
|
||||
from tqdm import tqdm
|
||||
|
||||
from lxml import etree
|
||||
|
||||
|
||||
def apply(config: dict) -> bool:
|
||||
main_color = config["theme"]["colors"]["primary"]
|
||||
splash_color = config["theme"]["colors"]["secondary"]
|
||||
|
||||
@@ -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)
|
||||
@@ -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",
|
||||
"server": "https://anixarty.wowlikon.tech/modding",
|
||||
"theme": {
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
# Change package icon of apk
|
||||
"""Insert new files"""
|
||||
priority = 0
|
||||
from tqdm import tqdm
|
||||
|
||||
import shutil
|
||||
import os
|
||||
|
||||
|
||||
def apply(config: dict) -> bool:
|
||||
# Mod first launch window
|
||||
shutil.copytree(
|
||||
"./patches/resources/smali_classes4/", "./decompiled/smali_classes4/"
|
||||
"./patches/resources/smali_classes4/",
|
||||
"./decompiled/smali_classes4/"
|
||||
)
|
||||
|
||||
# Mod assets
|
||||
@@ -43,5 +45,4 @@ def apply(config: dict) -> bool:
|
||||
"./decompiled/res/raw/sdkinternalca.cer",
|
||||
)
|
||||
|
||||
os.remove("./decompiled/res/font/ytsans_medium.otf")
|
||||
return True
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Change package name of apk
|
||||
"""Change package name of apk"""
|
||||
priority = -1
|
||||
from tqdm import tqdm
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def rename_dir(src, dst):
|
||||
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
||||
os.rename(src, dst)
|
||||
|
||||
|
||||
def apply(config: dict) -> bool:
|
||||
assert config["new_package_name"] is not None, "new_package_name is not configured"
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# Change application theme
|
||||
"""Add new settings"""
|
||||
priority = 0
|
||||
from tqdm import tqdm
|
||||
|
||||
from lxml import etree
|
||||
|
||||
|
||||
# Generate PreferenceCategory
|
||||
def make_category(ns, name, items):
|
||||
cat = etree.Element("PreferenceCategory", nsmap=ns)
|
||||
|
||||
Reference in New Issue
Block a user