added a 2nd progress bar

This commit is contained in:
Faraphel 2022-08-15 14:14:53 +02:00
parent 917f491ad1
commit c0915ae13e
10 changed files with 118 additions and 100 deletions

View file

@ -13,6 +13,7 @@ from source.gui import better_gui_error, mystuff, mod_settings
from source.mkw.Game import Game from source.mkw.Game import Game
from source.mkw.ModConfig import ModConfig from source.mkw.ModConfig import ModConfig
from source.option import Option from source.option import Option
from source.progress import Progress
from source.translation import translate as _ from source.translation import translate as _
from source import plugins from source import plugins
from source import * from source import *
@ -95,18 +96,23 @@ class Window(tkinter.Tk):
for child in self.winfo_children(): for child in self.winfo_children():
getattr(child, "set_state", lambda *_: "pass")(state) getattr(child, "set_state", lambda *_: "pass")(state)
def progress_function(self, func_gen: Generator) -> None: def progress_function(self, func_gen: Generator[Progress, None, None]) -> None:
""" """
Run a generator function that yield status for the progress bar Run a generator function that yield status for the progress bar
:return: :return:
""" """
# get the generator data yield by the generator function # get the generator data yield by the generator function
for step_data in func_gen: for progress in func_gen:
if "description" in step_data: self.progress_bar.set_description(step_data["description"]) if progress.title is not None: self.progress_bar.set_title(progress.title)
if "maximum" in step_data: self.progress_bar.set_maximum(step_data["maximum"]) if progress.part is not None: self.progress_bar.part(progress.part)
if "step" in step_data: self.progress_bar.step(step_data["step"]) if progress.set_part is not None: self.progress_bar.set_part(progress.set_part)
if "value" in step_data: self.progress_bar.set_value(step_data["value"]) if progress.max_part is not None: self.progress_bar.set_max_part(progress.max_part)
if "determinate" in step_data: self.progress_bar.set_determinate(step_data["determinate"])
if progress.description is not None: self.progress_bar.set_description(progress.description)
if progress.step is not None: self.progress_bar.step(progress.step)
if progress.set_step is not None: self.progress_bar.set_step(progress.set_step)
if progress.max_step is not None: self.progress_bar.set_max_step(progress.max_step)
if progress.determinate is not None: self.progress_bar.set_determinate(progress.determinate)
def get_mod_config(self) -> ModConfig: def get_mod_config(self) -> ModConfig:
""" """
@ -450,11 +456,17 @@ class ProgressBar(ttk.LabelFrame):
# make the element fill the whole frame # make the element fill the whole frame
self.columnconfigure(1, weight=1) self.columnconfigure(1, weight=1)
self.progress_bar = ttk.Progressbar(self, orient="horizontal") self.progress_bar_part = ttk.Progressbar(self, orient="horizontal")
self.progress_bar.grid(row=1, column=1, sticky="nsew") self.progress_bar_part.grid(row=1, column=1, sticky="nsew")
self.title = ttk.Label(self, text="", anchor="center", font=("TkDefaultFont", 10), wraplength=350)
self.title.grid(row=2, column=1, sticky="nsew")
self.progress_bar_step = ttk.Progressbar(self, orient="horizontal")
self.progress_bar_step.grid(row=3, column=1, sticky="nsew")
self.description = ttk.Label(self, text="", anchor="center", font=("TkDefaultFont", 10), wraplength=350) self.description = ttk.Label(self, text="", anchor="center", font=("TkDefaultFont", 10), wraplength=350)
self.description.grid(row=2, column=1, sticky="nsew") self.description.grid(row=4, column=1, sticky="nsew")
def set_state(self, state: InstallerState) -> None: def set_state(self, state: InstallerState) -> None:
""" """
@ -466,37 +478,15 @@ class ProgressBar(ttk.LabelFrame):
case InstallerState.IDLE: self.grid_remove() case InstallerState.IDLE: self.grid_remove()
case InstallerState.INSTALLING: self.grid() case InstallerState.INSTALLING: self.grid()
def set_description(self, desc: str) -> None: def set_title(self, title: str): self.title.config(text=title)
""" def set_max_part(self, maximum: int): self.progress_bar_part.configure(maximum=maximum)
Set the progress bar description def set_part(self, value: int): self.progress_bar_part.configure(value=value)
:param desc: description def part(self, value: int = 1): self.progress_bar_part.step(value)
:return:
"""
self.description.config(text=desc)
def set_maximum(self, maximum: int) -> None: def set_description(self, desc: str) -> None: self.description.config(text=desc)
""" def set_max_step(self, maximum: int) -> None: self.progress_bar_step.configure(maximum=maximum)
Set the progress bar maximum value def set_step(self, value: int) -> None: self.progress_bar_step.configure(value=value)
:param maximum: the maximum value def step(self, value: int = 1) -> None: self.progress_bar_step.step(value)
:return:
"""
self.progress_bar.configure(maximum=maximum)
def set_value(self, value: int) -> None:
"""
Set the progress bar value
:param value: the value
:return:
"""
self.progress_bar.configure(value=value)
def step(self, value: int = 1) -> None:
"""
Set the progress bar by the value
:param value: the step
:return:
"""
self.progress_bar.step(value)
def set_determinate(self, value: bool) -> None: def set_determinate(self, value: bool) -> None:
""" """

View file

@ -5,6 +5,7 @@ from typing import Generator, IO, TYPE_CHECKING
from source.mkw.ModConfig import ModConfig from source.mkw.ModConfig import ModConfig
from source.mkw.Patch.Patch import Patch from source.mkw.Patch.Patch import Patch
from source.progress import Progress
from source.wt import szs, lec, wit from source.wt import szs, lec, wit
from source.wt.wstrt import StrPath from source.wt.wstrt import StrPath
@ -27,35 +28,35 @@ class ExtractedGame:
self.original_game = original_game self.original_game = original_game
self._special_file: dict[str, IO] = {} self._special_file: dict[str, IO] = {}
def extract_autoadd(self, destination_path: "Path | str") -> Generator[dict, None, None]: def extract_autoadd(self, destination_path: "Path | str") -> Generator[Progress, None, None]:
""" """
Extract all the autoadd files from the game to destination_path Extract all the autoadd files from the game to destination_path
:param destination_path: directory where the autoadd files will be extracted :param destination_path: directory where the autoadd files will be extracted
""" """
yield {"description": "Extracting autoadd files...", "determinate": False} yield Progress(description="Extracting autoadd files...", determinate=False)
szs.autoadd(self.path / "files/Race/Course/", destination_path) szs.autoadd(self.path / "files/Race/Course/", destination_path)
def extract_original_tracks(self, destination_path: "Path | str") -> Generator[dict, None, None]: def extract_original_tracks(self, destination_path: "Path | str") -> Generator[Progress, None, None]:
""" """
Move all the original tracks to the destination path Move all the original tracks to the destination path
:param destination_path: destination of the track :param destination_path: destination of the track
""" """
destination_path = Path(destination_path) destination_path = Path(destination_path)
destination_path.mkdir(parents=True, exist_ok=True) destination_path.mkdir(parents=True, exist_ok=True)
yield {"description": "Extracting original tracks...", "determinate": False} yield Progress(description="Extracting original tracks...", determinate=False)
for track_file in (self.path / "files/Race/Course/").glob("*.szs"): for track_file in (self.path / "files/Race/Course/").glob("*.szs"):
yield {"description": f"Extracting original tracks ({track_file.name})...", "determinate": False} yield Progress(description=f"Extracting original tracks ({track_file.name})...", determinate=False)
if not (destination_path / track_file.name).exists(): track_file.rename(destination_path / track_file.name) if not (destination_path / track_file.name).exists(): track_file.rename(destination_path / track_file.name)
else: track_file.unlink() else: track_file.unlink()
def install_mystuff(self, mystuff_path: "Path | str") -> Generator[dict, None, None]: def install_mystuff(self, mystuff_path: "Path | str") -> Generator[Progress, None, None]:
""" """
Install mystuff directory. If any files of the game have the same name as a file at the root of the MyStuff Install mystuff directory. If any files of the game have the same name as a file at the root of the MyStuff
Patch, then it is copied. Patch, then it is copied.
:mystuff_path: path to the MyStuff directory :mystuff_path: path to the MyStuff directory
:return: :return:
""" """
yield {"description": f"Installing MyStuff '{mystuff_path}'...", "determinate": False} yield Progress(description=f"Installing MyStuff '{mystuff_path}'...", determinate=False)
mystuff_path = Path(mystuff_path) mystuff_path = Path(mystuff_path)
mystuff_rootfiles: dict[str, Path] = {} mystuff_rootfiles: dict[str, Path] = {}
@ -67,49 +68,49 @@ class ExtractedGame:
if (mystuff_file := mystuff_rootfiles.get(game_file.name)) is None: continue if (mystuff_file := mystuff_rootfiles.get(game_file.name)) is None: continue
shutil.copy(mystuff_file, game_file) shutil.copy(mystuff_file, game_file)
def install_multiple_mystuff(self, mystuff_paths: list["Path | str"]) -> Generator[dict, None, None]: def install_multiple_mystuff(self, mystuff_paths: list["Path | str"]) -> Generator[Progress, None, None]:
""" """
Install multiple mystuff patch Install multiple mystuff patch
:param mystuff_paths: paths to all the mystuff patch :param mystuff_paths: paths to all the mystuff patch
""" """
yield {"description": "Installing all the mystuff patchs"} yield Progress(description="Installing all the mystuff patchs")
for mystuff_path in mystuff_paths: for mystuff_path in mystuff_paths:
yield from self.install_mystuff(mystuff_path) yield from self.install_mystuff(mystuff_path)
def prepare_special_file(self, mod_config: ModConfig) -> Generator[dict, None, None]: def prepare_special_file(self, mod_config: ModConfig) -> Generator[Progress, None, None]:
""" """
Prepare special files for the patch Prepare special files for the patch
:return: the special files dict :return: the special files dict
""" """
yield {"description": "Preparing ct_icon special file...", "determinate": False} yield Progress(description="Preparing ct_icon special file...", determinate=False)
ct_icons = BytesIO() ct_icons = BytesIO()
mod_config.get_full_cticon().save(ct_icons, format="PNG") mod_config.get_full_cticon().save(ct_icons, format="PNG")
ct_icons.seek(0) ct_icons.seek(0)
self._special_file["ct_icons"] = ct_icons self._special_file["ct_icons"] = ct_icons
def prepare_dol(self) -> Generator[dict, None, None]: def prepare_dol(self) -> Generator[Progress, None, None]:
""" """
Prepare main.dol and StaticR.rel files (clean them and add lecode) Prepare main.dol and StaticR.rel files (clean them and add lecode)
""" """
yield {"description": "Preparing main.dol...", "determinate": False} yield Progress(description="Preparing main.dol...", determinate=False)
StrPath(self.path / "sys/main.dol").patch(clean_dol=True, add_lecode=True) StrPath(self.path / "sys/main.dol").patch(clean_dol=True, add_lecode=True)
def recreate_all_szs(self) -> Generator[dict, None, None]: def recreate_all_szs(self) -> Generator[Progress, None, None]:
""" """
Repack all the .d directory into .szs files. Repack all the .d directory into .szs files.
""" """
yield {"description": f"Repacking all szs", "determinate": False} yield Progress(description=f"Repacking all szs", determinate=False)
for extracted_szs in filter(lambda path: path.is_dir(), self.path.rglob("*.d")): for extracted_szs in filter(lambda path: path.is_dir(), self.path.rglob("*.d")):
# for every directory that end with a .d in the extracted game, recreate the szs # for every directory that end with a .d in the extracted game, recreate the szs
yield {"description": f"Repacking {extracted_szs} to szs", "determinate": False} yield Progress(description=f"Repacking {extracted_szs} to szs", determinate=False)
szs.create(extracted_szs, extracted_szs.with_suffix(".szs"), overwrite=True) szs.create(extracted_szs, extracted_szs.with_suffix(".szs"), overwrite=True)
shutil.rmtree(str(extracted_szs.resolve())) shutil.rmtree(str(extracted_szs.resolve()))
def patch_lecode(self, mod_config: ModConfig, cache_directory: Path | str, def patch_lecode(self, mod_config: ModConfig, cache_directory: Path | str,
cttracks_directory: Path | str, ogtracks_directory: Path | str) -> Generator[dict, None, None]: cttracks_directory: Path | str, ogtracks_directory: Path | str) -> Generator[Progress, None, None]:
""" """
install lecode on the mod install lecode on the mod
:param cttracks_directory: directory to the customs tracks :param cttracks_directory: directory to the customs tracks
@ -117,7 +118,7 @@ class ExtractedGame:
:param cache_directory: Path to the cache :param cache_directory: Path to the cache
:param mod_config: mod configuration :param mod_config: mod configuration
""" """
yield {"description": "Patching LECODE.bin"} yield Progress(description="Patching LECODE.bin")
cache_directory = Path(cache_directory) cache_directory = Path(cache_directory)
cttracks_directory = Path(cttracks_directory) cttracks_directory = Path(cttracks_directory)
ogtracks_directory = Path(ogtracks_directory) ogtracks_directory = Path(ogtracks_directory)
@ -139,42 +140,44 @@ class ExtractedGame:
copy_tracks_directories=[ogtracks_directory, cttracks_directory] copy_tracks_directories=[ogtracks_directory, cttracks_directory]
) )
def _install_all_patch(self, mod_config: ModConfig, patch_directory_name: str) -> Generator[dict, None, None]: def _install_all_patch(self, mod_config: ModConfig, patch_directory_name: str) -> Generator[Progress, None, None]:
""" """
for all directory that are in the root of the mod, and don't start with an underscore, for all directory that are in the root of the mod, and don't start with an underscore,
for all the subdirectory named by the patch_directory_name, apply the patch for all the subdirectory named by the patch_directory_name, apply the patch
:param mod_config: the mod to install :param mod_config: the mod to install
""" """
yield {} # yield an empty dict so that if nothing is yielded by the Patch, still is considered a generator # yield an empty dict so that if nothing is yielded by the Patch, still is considered a generator
yield Progress()
for part_directory in mod_config.get_mod_directory().glob("[!_]*"): for part_directory in mod_config.get_mod_directory().glob("[!_]*"):
for patch_directory in part_directory.glob(patch_directory_name): for patch_directory in part_directory.glob(patch_directory_name):
yield from Patch(patch_directory, mod_config, self._special_file).install(self) yield from Patch(patch_directory, mod_config, self._special_file).install(self)
def install_all_prepatch(self, mod_config: ModConfig) -> Generator[dict, None, None]: def install_all_prepatch(self, mod_config: ModConfig) -> Generator[Progress, None, None]:
""" """
Install all patchs of the mod_config into the game. Install all patchs of the mod_config into the game.
Used before the lecode patch is applied Used before the lecode patch is applied
:param mod_config: the mod to install :param mod_config: the mod to install
""" """
yield {"description": "Installing all Pre-Patch...", "determinate": False} yield Progress(description="Installing all Pre-Patch...", determinate=False)
yield from self._install_all_patch(mod_config, "_PREPATCH/") yield from self._install_all_patch(mod_config, "_PREPATCH/")
def install_all_patch(self, mod_config: ModConfig) -> Generator[dict, None, None]: def install_all_patch(self, mod_config: ModConfig) -> Generator[Progress, None, None]:
""" """
Install all patchs of the mod_config into the game. Install all patchs of the mod_config into the game.
Used after the lecode patch is applied Used after the lecode patch is applied
:param mod_config: the mod to install :param mod_config: the mod to install
""" """
yield {"description": "Installing all Patch...", "determinate": False} yield Progress(description="Installing all Patch...", determinate=False)
yield from self._install_all_patch(mod_config, "_PATCH/") yield from self._install_all_patch(mod_config, "_PATCH/")
def convert_to(self, output_type: wit.Extension) -> Generator[dict, None, wit.WITPath | None]: def convert_to(self, output_type: wit.Extension) -> Generator[Progress, None, wit.WITPath | None]:
""" """
Convert the extracted game to another format Convert the extracted game to another format
:param output_type: path to the destination of the game :param output_type: path to the destination of the game
:output_type: format of the destination game :output_type: format of the destination game
""" """
yield {"description": f"Converting game to {output_type}", "determinate": False} yield Progress(description=f"Converting game to {output_type}", determinate=False)
if output_type == wit.Extension.FST: return if output_type == wit.Extension.FST: return
destination_file = self.path.with_suffix(self.path.suffix + output_type.value) destination_file = self.path.with_suffix(self.path.suffix + output_type.value)
@ -190,7 +193,7 @@ class ExtractedGame:
destination_file=destination_file, destination_file=destination_file,
) )
yield {"description": "Deleting the extracted game...", "determinate": False} yield Progress(description="Deleting the extracted game...", determinate=False)
shutil.rmtree(self.path) shutil.rmtree(self.path)
return converted_game return converted_game

View file

@ -4,6 +4,7 @@ from typing import Generator
from source.mkw.ExtractedGame import ExtractedGame from source.mkw.ExtractedGame import ExtractedGame
from source.mkw.ModConfig import ModConfig from source.mkw.ModConfig import ModConfig
from source.option import Option from source.option import Option
from source.progress import Progress
from source.wt.wit import WITPath, Region, Extension from source.wt.wit import WITPath, Region, Extension
@ -37,7 +38,7 @@ class Game:
""" """
return not any(self.wit_path[f"./files/rel/lecode-{region.value}.bin"].exists() for region in Region) return not any(self.wit_path[f"./files/rel/lecode-{region.value}.bin"].exists() for region in Region)
def extract(self, dest: "Path | str") -> Generator[dict, None, Path]: def extract(self, dest: "Path | str") -> Generator[Progress, None, Path]:
""" """
Extract the game to the destination directory. If the game is a FST, just copy to the destination Extract the game to the destination directory. If the game is a FST, just copy to the destination
:param dest: destination directory :param dest: destination directory
@ -45,30 +46,25 @@ class Game:
gen = self.wit_path.progress_extract_all(dest) gen = self.wit_path.progress_extract_all(dest)
if self.wit_path.extension == Extension.FST: if self.wit_path.extension == Extension.FST:
for gen_data in gen: for _ in gen: yield Progress(description="Copying Game...", determinate=False)
yield {
"description": "Copying Game...",
"determinate": False
}
try: next(gen) try: next(gen)
except StopIteration as e: except StopIteration as e: return e.value
return e.value
else: else:
for gen_data in gen: for gen_data in gen:
yield { yield Progress(
"description": f'Extracting - {gen_data["percentage"]}% - (estimated time remaining: ' description=f'Extracting - {gen_data["percentage"]}% - (estimated time remaining: '
f'{gen_data["estimation"] if gen_data["estimation"] is not None else "-:--"})', f'{gen_data["estimation"] if gen_data["estimation"] is not None else "-:--"})',
"maximum": 100, max_step=100,
"value": gen_data["percentage"], set_step=gen_data["percentage"],
"determinate": True determinate=True
} )
try: next(gen) try: next(gen)
except StopIteration as e: except StopIteration as e:
return e.value return e.value
def edit(self, mod_config: ModConfig) -> Generator[dict, None, None]: def edit(self, mod_config: ModConfig) -> Generator[Progress, None, None]:
yield {"description": "Changing game metadata...", "determinate": False} yield Progress(description="Changing game metadata...", determinate=False)
self.wit_path.edit( self.wit_path.edit(
name=mod_config.name, name=mod_config.name,
game_id=self.wit_path.id[:4] + mod_config.variant game_id=self.wit_path.id[:4] + mod_config.variant
@ -96,7 +92,7 @@ class Game:
return extracted_game return extracted_game
def install_mod(self, dest: Path, mod_config: ModConfig, options: "Option", output_type: Extension def install_mod(self, dest: Path, mod_config: ModConfig, options: "Option", output_type: Extension
) -> Generator[dict, None, None]: ) -> Generator[Progress, None, None]:
""" """
Patch the game with the mod Patch the game with the mod
:dest: destination directory :dest: destination directory

View file

@ -13,6 +13,7 @@ from source.mkw.Track import CustomTrack, DefaultTrack, Arena
import json import json
from source.mkw.OriginalTrack import OriginalTrack from source.mkw.OriginalTrack import OriginalTrack
from source.progress import Progress
from source.safe_eval import safe_eval, multiple_safe_eval from source.safe_eval import safe_eval, multiple_safe_eval
from source.wt.szs import SZSPath from source.wt.szs import SZSPath
@ -404,7 +405,8 @@ class ModConfig:
return full_cticon return full_cticon
def normalize_all_tracks(self, autoadd_path: "Path | str", destination_path: "Path | str", def normalize_all_tracks(self, autoadd_path: "Path | str", destination_path: "Path | str",
original_tracks_path: "Path | str", thread_amount: int = 8) -> Generator[dict, None, None]: original_tracks_path: "Path | str",
thread_amount: int = 8) -> Generator[Progress, None, None]:
""" """
Convert all tracks of the mod to szs into the destination_path Convert all tracks of the mod to szs into the destination_path
:param original_tracks_path: path to the originals tracks (if a track is disabled for multiplayer) :param original_tracks_path: path to the originals tracks (if a track is disabled for multiplayer)
@ -412,21 +414,23 @@ class ModConfig:
:param autoadd_path: autoadd directory :param autoadd_path: autoadd directory
:param destination_path: destination where the files are converted :param destination_path: destination where the files are converted
""" """
yield {"description": "Normalizing track..."} yield Progress(description="Normalizing track...")
destination_path = Path(destination_path) destination_path = Path(destination_path)
original_tracks_path = Path(original_tracks_path) original_tracks_path = Path(original_tracks_path)
destination_path.mkdir(parents=True, exist_ok=True) destination_path.mkdir(parents=True, exist_ok=True)
normalize_threads: list[dict] = [] normalize_threads: list[dict] = []
def remove_finished_threads() -> Generator[dict, None, None]: def remove_finished_threads() -> Generator[Progress, None, None]:
""" """
Remove all the thread that stopped in a thread list Remove all the thread that stopped in a thread list
:return: the list without the stopped thread :return: the list without the stopped thread
""" """
nonlocal normalize_threads nonlocal normalize_threads
yield {"description": f"Normalizing tracks :\n" + "\n".join(thread['name'] for thread in normalize_threads)} yield Progress(
description=f"Normalizing tracks :\n" + "\n".join(thread['name'] for thread in normalize_threads)
)
normalize_threads = list(filter(lambda thread: thread["thread"].is_alive(), normalize_threads)) normalize_threads = list(filter(lambda thread: thread["thread"].is_alive(), normalize_threads))
track_directory = self.path.parent / "_TRACKS" track_directory = self.path.parent / "_TRACKS"

View file

@ -1,6 +1,8 @@
from pathlib import Path from pathlib import Path
from typing import Generator, IO, TYPE_CHECKING from typing import Generator, IO, TYPE_CHECKING
from source.progress import Progress
if TYPE_CHECKING: if TYPE_CHECKING:
from source.mkw.ModConfig import ModConfig from source.mkw.ModConfig import ModConfig
from source.mkw.ExtractedGame import ExtractedGame from source.mkw.ExtractedGame import ExtractedGame
@ -19,13 +21,13 @@ class Patch:
def __repr__(self) -> str: def __repr__(self) -> str:
return f"<{self.__class__.__name__} {self.path}>" return f"<{self.__class__.__name__} {self.path}>"
def install(self, extracted_game: "ExtractedGame") -> Generator[dict, None, None]: def install(self, extracted_game: "ExtractedGame") -> Generator[Progress, None, None]:
""" """
patch a game with this Patch patch a game with this Patch
:param extracted_game: the extracted game :param extracted_game: the extracted game
""" """
from source.mkw.Patch.PatchDirectory import PatchDirectory from source.mkw.Patch.PatchDirectory import PatchDirectory
yield {"description": f"Installing the patch", "determinate": False} yield Progress(description=f"Installing the patch", determinate=False)
# take all the files in the root directory, and patch them into the game. # take all the files in the root directory, and patch them into the game.
# Patch is not directly applied to the root to avoid custom configuration # Patch is not directly applied to the root to avoid custom configuration

View file

@ -3,6 +3,7 @@ from typing import Generator, TYPE_CHECKING
from source.mkw.Patch import PathOutsidePatch, InvalidPatchMode from source.mkw.Patch import PathOutsidePatch, InvalidPatchMode
from source.mkw.Patch.PatchObject import PatchObject from source.mkw.Patch.PatchObject import PatchObject
from source.progress import Progress
if TYPE_CHECKING: if TYPE_CHECKING:
from source.mkw.ExtractedGame import ExtractedGame from source.mkw.ExtractedGame import ExtractedGame
@ -22,11 +23,11 @@ class PatchDirectory(PatchObject):
if subpath.suffix == ".json": continue if subpath.suffix == ".json": continue
yield self.subfile_from_path(subpath) yield self.subfile_from_path(subpath)
def install(self, extracted_game: "ExtractedGame", game_subpath: Path) -> Generator[dict, None, None]: def install(self, extracted_game: "ExtractedGame", game_subpath: Path) -> Generator[Progress, None, None]:
""" """
patch a subdirectory of the game with the PatchDirectory patch a subdirectory of the game with the PatchDirectory
""" """
yield {"description": f"Patching {game_subpath}"} yield Progress(description=f"Patching {game_subpath}")
# check if the directory should be patched # check if the directory should be patched
if not self.is_enabled(extracted_game): return if not self.is_enabled(extracted_game): return

View file

@ -5,6 +5,7 @@ from typing import Generator, IO, TYPE_CHECKING
from source.mkw.Patch import PathOutsidePatch, InvalidPatchMode, InvalidSourceMode from source.mkw.Patch import PathOutsidePatch, InvalidPatchMode, InvalidSourceMode
from source.mkw.Patch.PatchOperation import AbstractPatchOperation from source.mkw.Patch.PatchOperation import AbstractPatchOperation
from source.mkw.Patch.PatchObject import PatchObject from source.mkw.Patch.PatchObject import PatchObject
from source.progress import Progress
from source.wt.szs import SZSPath from source.wt.szs import SZSPath
if TYPE_CHECKING: if TYPE_CHECKING:
@ -76,11 +77,11 @@ class PatchFile(PatchObject):
if not szs_path.exists() and szs_path.with_suffix(".szs").exists(): if not szs_path.exists() and szs_path.with_suffix(".szs").exists():
SZSPath(szs_path.with_suffix(".szs")).extract_all(szs_path) SZSPath(szs_path.with_suffix(".szs")).extract_all(szs_path)
def install(self, extracted_game: "ExtractedGame", game_subpath: Path) -> Generator[dict, None, None]: def install(self, extracted_game: "ExtractedGame", game_subpath: Path) -> Generator[Progress, None, None]:
""" """
patch a subfile of the game with the PatchFile patch a subfile of the game with the PatchFile
""" """
yield {"description": f"Patching {game_subpath}"} yield Progress(description=f"Patching {game_subpath}")
# check if the file should be patched # check if the file should be patched
if not self.is_enabled(extracted_game): return if not self.is_enabled(extracted_game): return
@ -118,7 +119,7 @@ class PatchFile(PatchObject):
if not game_subfile.relative_to(extracted_game.path): if not game_subfile.relative_to(extracted_game.path):
raise PathOutsidePatch(game_subfile, extracted_game.path) raise PathOutsidePatch(game_subfile, extracted_game.path)
yield {"description": f"Patching {game_subfile}"} yield Progress(description=f"Patching {game_subfile}")
# if the source is the game, then recalculate the content for every game subfile # if the source is the game, then recalculate the content for every game subfile
if self.configuration["source"] == "game": if self.configuration["source"] == "game":

View file

@ -3,6 +3,8 @@ from abc import abstractmethod, ABC
from pathlib import Path from pathlib import Path
from typing import Generator, TYPE_CHECKING from typing import Generator, TYPE_CHECKING
from source.progress import Progress
if TYPE_CHECKING: if TYPE_CHECKING:
from source.mkw.Patch import Patch from source.mkw.Patch import Patch
from source.mkw.ExtractedGame import ExtractedGame from source.mkw.ExtractedGame import ExtractedGame
@ -64,7 +66,7 @@ class PatchObject(ABC):
return obj(self.patch, str(path.relative_to(self.patch.path))) return obj(self.patch, str(path.relative_to(self.patch.path)))
@abstractmethod @abstractmethod
def install(self, extracted_game: "ExtractedGame", game_subpath: Path) -> Generator[dict, None, None]: def install(self, extracted_game: "ExtractedGame", game_subpath: Path) -> Generator[Progress, None, None]:
""" """
install the PatchObject into the game install the PatchObject into the game
yield the step of the process yield the step of the process

21
source/progress.py Normal file
View file

@ -0,0 +1,21 @@
from dataclasses import dataclass
@dataclass
class Progress:
"""
Represent the level of progression of the installer. Used for progress bar.
"""
# this represents the first progress bar, showing every big part in the process
title: str = None
part: int = None
set_part: int = None
max_part: int = None
# this represents the second progress bar, showing every step of the current part of the process
description: str = None
step: int = None
set_step: int = None
max_step: int = None
determinate: bool = None

View file

@ -148,9 +148,7 @@ class WITPath:
:return: the extracted file path :return: the extracted file path
""" """
if self.extension == Extension.FST: if self.extension == Extension.FST:
yield { yield {}
"determinate": False
}
shutil.copytree(self._get_fst_root(), dest) shutil.copytree(self._get_fst_root(), dest)
else: else: