Compare commits

...

6 Commits

Author SHA1 Message Date
197dc767a8 feat: hdiffpatch 2023-06-20 23:02:26 +07:00
3d44bfe63a xdelta3: downloading binary perform in-memory 2023-06-20 19:14:25 +07:00
45a9953eb2 fix: make constants constants
ALL CAPS
2023-06-20 18:55:03 +07:00
a3a6cfdbe3 cli: Y/n instead of y/n 2023-06-20 18:51:21 +07:00
0d52b53172 xdelta3: delete archive file after extract 2023-06-20 18:51:02 +07:00
7ca33c62b2 cli: fix jadeite name
It's jadeite
2023-06-20 18:50:17 +07:00
9 changed files with 144 additions and 16 deletions

View File

@ -38,14 +38,14 @@ class HSR:
def __patch_jadeite(self):
try:
print("Installing patch...", end=" ")
jadelte_dir = self._patcher.patch_game(game=self._game)
jadeite_dir = self._patcher.patch_game(game=self._game)
except PatcherError as e:
print("FAILED")
print(f"Patching failed with following error: {e}")
return
print("OK")
exe_path = jadelte_dir.joinpath("jadeite.exe")
print("Jadelte executable is located at:", exe_path)
exe_path = jadeite_dir.joinpath("jadeite.exe")
print("jadeite executable is located at:", exe_path)
print(
"Installation succeeded, but note that you need to run the game using "
+ "Jadeite to use the patch."

View File

@ -6,8 +6,8 @@ def ask(question: str):
print(question + " [Y/n] Y")
return True
while True:
answer = input(question + " [y/n] ")
if answer.lower() in ["y", "yes"]:
answer = input(question + " [Y/n] ")
if answer.lower().strip() in ["y", "yes", ""]:
return True
# Pacman way, treat all other answers as no
else:

View File

@ -1,5 +1,4 @@
# Common
telemetry_hosts = [
TELEMETRY_HOSTS = [
# Global
"log-upload-os.hoyoverse.com",
"sg-public-data-api.hoyoverse.com",
@ -8,3 +7,4 @@ telemetry_hosts = [
"log-upload.mihoyo.com",
"public-data-api.mihoyo.com",
]
HDIFFPATCH_GIT_URL = "https://github.com/sisong/HDiffPatch"

View File

@ -1,5 +1,5 @@
latest_version = (1, 1, 0)
md5sums = {
LATEST_VERSION = (1, 1, 0)
MD5SUMS = {
"1.0.5": {
"cn": {
"StarRailBase.dll": "66c42871ce82456967d004ccb2d7cf77",
@ -12,5 +12,5 @@ md5sums = {
}
}
# Patches
astra_repo = "https://notabug.org/mkrsym1/astra"
jadeite_repo = "https://codeberg.org/mkrsym1/jadeite/"
ASTRA_REPO = "https://notabug.org/mkrsym1/astra"
JADEITE_REPO = "https://codeberg.org/mkrsym1/jadeite/"

View File

@ -11,7 +11,7 @@ from vollerei.exceptions.patcher import (
from vollerei.hsr.launcher.game import Game, GameChannel
from vollerei.utils import download_and_extract, Git, Xdelta3
from vollerei.paths import tools_data_path
from vollerei.hsr.constants import astra_repo, jadeite_repo
from vollerei.hsr.constants import ASTRA_REPO, JADEITE_REPO
class PatchType(Enum):
@ -49,10 +49,10 @@ class Patcher(PatcherABC):
self._patch_type = value
def _update_astra(self):
self._git.pull_or_clone(astra_repo, self._astra)
self._git.pull_or_clone(ASTRA_REPO, self._astra)
def _update_jadeite(self):
release_info = self._git.get_latest_release(jadeite_repo)
release_info = self._git.get_latest_release(JADEITE_REPO)
file = self._git.get_latest_release_dl(release_info)[0]
file_version = release_info["tag_name"][1:] # Remove "v" prefix
current_version = None

View File

@ -0,0 +1,104 @@
import platform
import subprocess
from zipfile import ZipFile
import requests
from io import BytesIO
from shutil import which
from vollerei.constants import HDIFFPATCH_GIT_URL
from vollerei.paths import tools_data_path
class HDiffPatch:
def __init__(self):
self._data = tools_data_path.joinpath("hdiffpatch")
self._data.mkdir(parents=True, exist_ok=True)
@staticmethod
def _get_platform_arch():
match platform.system():
case "Windows":
match platform.architecture()[0]:
case "32bit":
return "windows32"
case "64bit":
return "windows64"
case "Linux":
match platform.architecture()[0]:
case "32bit":
return "linux32"
case "64bit":
return "linux64"
case "Darwin":
return "macos"
# Rip BSD they need to use Linux compatibility layer to run this
# (or use Wine if they prefer that)
raise RuntimeError("Only Windows, Linux and macOS are supported by HDiffPatch")
def _get_hdiffpatch_exec(self, exec_name) -> str | None:
if which(exec_name):
return exec_name
if not self.data_path.exists():
return None
if not any(self.data_path.iterdir()):
return None
platform_arch_path = self.data_path.joinpath(self._get_platform_arch())
file = platform_arch_path.joinpath(exec_name)
if file.exists():
file.chmod(0o755)
return str(file)
def _hpatchz(self):
hpatchz_name = "hpatchz" + (".exe" if platform.system() == "Windows" else "")
return self._get_hdiffpatch_exec(hpatchz_name)
def patch_file(self, in_file, out_file, patch_file):
hpatchz = self.get_hpatchz_executable()
if not hpatchz:
raise RuntimeError("hpatchz executable not found")
subprocess.check_call([hpatchz, "-f", in_file, patch_file, out_file])
async def _get_latest_release_info(self) -> dict:
split = HDIFFPATCH_GIT_URL.split("/")
repo = split[-1]
owner = split[-2]
rsp = requests.get(
"https://api.github.com/repos/{}/{}/releases/latest".format(owner, repo),
params={"Headers": "Accept: application/vnd.github.v3+json"},
)
rsp.raise_for_status()
for asset in (await rsp.json())["assets"]:
if not asset["name"].endswith(".zip"):
continue
if "linux" in asset["name"]:
continue
if "windows" in asset["name"]:
continue
if "macos" in asset["name"]:
continue
if "android" in asset["name"]:
continue
return asset
async def get_latest_release_url(self):
asset = await self._get_latest_release_info()
return asset["browser_download_url"]
async def get_latest_release_name(self):
asset = await self._get_latest_release_info()
return asset["name"]
async def download(self):
"""
Download the latest release of HDiffPatch.
"""
url = await self.get_latest_release_url()
if not url:
raise RuntimeError("Unable to find latest release")
file = BytesIO()
with requests.get(url, stream=True) as r:
with open(file, "wb") as f:
for chunk in r.iter_content(chunk_size=32768):
f.write(chunk)
with ZipFile(file) as z:
z.extractall(self._data)

View File

@ -0,0 +1,22 @@
class HDiffPatchError(Exception):
"""Base class for HDiffPatch errors"""
pass
class HPatchZError(HDiffPatchError):
"""Raised when hpatchz fails"""
pass
class NotInstalledError(HPatchZError):
"""Raised when HDiffPatch is not installed"""
pass
class HPatchZPatchError(HPatchZError):
"""Raised when hpatchz patch fails"""
pass

View File

@ -2,6 +2,7 @@ import platform
import subprocess
import requests
from os import PathLike
from io import BytesIO
from zipfile import ZipFile
from shutil import which
from vollerei.paths import tools_cache_path
@ -60,11 +61,12 @@ class Xdelta3:
url = "https://github.com/jmacd/xdelta-gpl/releases/download/v3.1.0/xdelta3-3.1.0-i686.exe.zip"
case "i686":
url = "https://github.com/jmacd/xdelta-gpl/releases/download/v3.1.0/xdelta3-3.1.0-i686.exe.zip"
file = BytesIO()
with requests.get(url, stream=True) as r:
with open(self._xdelta3_path.joinpath("xdelta3.zip"), "wb") as f:
with open(file, "wb") as f:
for chunk in r.iter_content(chunk_size=32768):
f.write(chunk)
with ZipFile(self._xdelta3_path.joinpath("xdelta3.zip")) as z:
with ZipFile(file) as z:
z.extractall(self._xdelta3_path)
def patch_file(self, patch: PathLike, target: PathLike, output: PathLike):