feat: verify_game_file, rename verify_game to verify_game_files

You can now use verify_game_file to verify a game file from a pkg_version (either parsed pkg_version or a path to pkg_version is fine), and verify_game_files to verify all game files from a pkg_version
This commit is contained in:
tretrauit 2022-09-20 17:43:11 +07:00
parent f61e4a39cf
commit 208b34c9df
Signed by: tretrauit
GPG Key ID: CDDE1C97EE305DAF
2 changed files with 82 additions and 21 deletions

View File

@ -6,7 +6,7 @@ import json
import hashlib
import logging
from os import PathLike
from pathlib import Path
from pathlib import Path, PurePath
from worthless import constants
from worthless.launcher import Launcher
@ -24,6 +24,28 @@ from worthless.game.launcherconfig import LauncherConfig
_logger = logging.getLogger("worthless.Game")
def _calculate_md5(file: str | bytes | PathLike):
file = Path(file)
if not file.exists():
return ""
with file.open("rb") as f:
file_hash = hashlib.md5()
while chunk := f.read(8196):
file_hash.update(chunk)
return file_hash.hexdigest()
def _parse_pkg_version(pkg_version: str | bytes | PathLike):
contents = Path(pkg_version).read_text()
pkg_version = []
for content in contents.split("\r\n"):
if not content.strip():
continue
info = json.loads(content)
pkg_version.append(info)
return pkg_version
def voicepack_lang_translate(lang: str, base_language="game") -> str | None:
"""
Translates the voicepack language to the language code used by the game (and reverse)
@ -58,7 +80,9 @@ def voicepack_lang_translate(lang: str, base_language="game") -> str | None:
return
def get_voicepack_archive_language(archive: str | bytes | PathLike) -> VoicepackArchiveLanguage:
def get_voicepack_archive_language(
archive: str | bytes | PathLike,
) -> VoicepackArchiveLanguage:
"""
Gets voicepack archive language.
@ -100,6 +124,27 @@ def get_voicepack_archive_type(archive: str | bytes | PathLike) -> VoicepackArch
return VoicepackArchiveType.FULL
def verify_game_file(
file: str | bytes | PathLike,
pkg_version: list[dict] | str | bytes | PathLike,
ignore_mismatch=True,
):
file = Path(file)
if not isinstance(pkg_version, list):
pkg_version = _parse_pkg_version(pkg_version=pkg_version)
for info in pkg_version:
if PurePath(info["remoteName"]).name != file.name:
continue
file_md5 = _calculate_md5(file)
if file_md5 == info["md5"]:
return None
if ignore_mismatch:
return file, info["md5"], file_md5
raise ValueError(
f"MD5 does not match for {file}, expected md5: {info['md5']}, actual md5: {file_md5}"
)
class Game:
"""
Manages game & voicepacks installation.
@ -191,18 +236,8 @@ class Game:
async def _verify_from_pkg_version(self, pkg_version: Path, ignore_mismatch=False):
contents = pkg_version.read_text()
def calculate_md5(file_to_calculate):
file_to_calculate = Path(file_to_calculate)
if not file_to_calculate.exists():
return ""
with file_to_calculate.open("rb") as f:
file_hash = hashlib.md5()
while chunk := f.read(8196):
file_hash.update(chunk)
return file_hash.hexdigest()
def verify_file(file_to_verify, md5):
file_md5 = calculate_md5(file_to_verify)
file_md5 = _calculate_md5(file_to_verify)
if file_md5 == md5:
return None
if ignore_mismatch:
@ -270,14 +305,18 @@ class Game:
return voicepack_lang_translate(lang=lang, base_language=base_language)
@staticmethod
def get_voicepack_archive_language(archive: str | bytes | PathLike) -> VoicepackArchiveLanguage:
def get_voicepack_archive_language(
archive: str | bytes | PathLike,
) -> VoicepackArchiveLanguage:
"""
This function is an alias to worthless.game.get_voicepack_archive_language
"""
return get_voicepack_archive_language(archive=archive)
@staticmethod
def get_voicepack_archive_type(archive: str | bytes | PathLike) -> VoicepackArchiveType:
def get_voicepack_archive_type(
archive: str | bytes | PathLike,
) -> VoicepackArchiveType:
"""
This function is an alias to worthless.game.get_voicepack_archive_type
"""
@ -307,7 +346,9 @@ class Game:
"""
return self._game_dir.joinpath(self.get_game_data_name())
def get_archive_game_version(self, game_archive: str | bytes | PathLike) -> str | None:
def get_archive_game_version(
self, game_archive: str | bytes | PathLike
) -> str | None:
"""
Gets the game version in the archive
@ -493,7 +534,10 @@ class Game:
self._extract_game_file(archive)
async def install_game(
self, game_archive: str | bytes | PathLike, force_reinstall: bool = False, callback=None
self,
game_archive: str | bytes | PathLike,
force_reinstall: bool = False,
callback=None,
):
"""Installs the game to the current directory
@ -547,7 +591,24 @@ class Game:
if v.version == from_version:
return v
async def verify_game(self, pkg_version: str | bytes | PathLike = None, ignore_mismatch=None):
def verify_game_file(
self,
file: str | bytes | PathLike,
pkg_version: list[dict] | str | bytes | PathLike = None,
ignore_mismatch=True,
):
"""
This function is an alias to worthless.game.get_voicepack_archive_type
"""
if pkg_version is None:
pkg_version = self._game_dir.joinpath("pkg_version")
return verify_game_file(
file=file, pkg_version=pkg_version, ignore_mismatch=ignore_mismatch
)
async def verify_game_files(
self, pkg_version: str | bytes | PathLike = None, ignore_mismatch=True
):
"""
Verifies the current game installation
@ -561,8 +622,6 @@ class Game:
"""
if pkg_version is None:
pkg_version = self._game_dir.joinpath("pkg_version")
if ignore_mismatch is None:
ignore_mismatch = True
return await self._verify_from_pkg_version(pkg_version, ignore_mismatch)
def delete_game(self):

View File

@ -85,7 +85,9 @@ class Helper(Game):
game = await self._game._get_game(pre_download=pre_download)
if self._game.version == game.latest.version:
raise ValueError("Game is already up to date.")
diff_archive = await self._game.get_game_diff_archive(from_version, pre_download)
diff_archive = await self._game.get_game_diff_archive(
from_version, pre_download
)
if diff_archive is None:
raise ValueError(
"Game diff archive is not available for this version, please reinstall."