118 lines
3.6 KiB
Python
118 lines
3.6 KiB
Python
from pathlib import Path
|
|
from zipfile import Path as ZipPath
|
|
from zipfile import ZipFile, is_zipfile
|
|
from leek.utils import GameFinder
|
|
|
|
|
|
class ModInstaller:
|
|
__archive_path: Path
|
|
__archive_format: str
|
|
__is_flat: bool
|
|
__is_installed: bool
|
|
|
|
def __init__(self, mod_path: Path) -> None:
|
|
self.__archive_path = mod_path
|
|
self.__is_installed = False
|
|
if is_zipfile(mod_path):
|
|
self.__archive_format = "zip"
|
|
archive: ZipFile = ZipFile(mod_path)
|
|
path: ZipPath = ZipPath(archive)
|
|
|
|
# Make sure archive contains a mod
|
|
things_in_root: list[ZipPath] = list(path.iterdir())
|
|
config_toml: ZipPath
|
|
match len(things_in_root):
|
|
case 0:
|
|
raise EmptyArchiveError
|
|
case 1:
|
|
config_toml = things_in_root[0] / "config.toml"
|
|
if config_toml.exists() and config_toml.is_file():
|
|
# Mod can be extracted as is
|
|
self.__is_flat = False
|
|
self.__check_if_already_installed()
|
|
return
|
|
case _:
|
|
config_toml = path / "config.toml"
|
|
if config_toml.exists() and config_toml.is_file():
|
|
# Mod needs to be extracted in a folder
|
|
self.__is_flat = True
|
|
self.__check_if_already_installed()
|
|
return
|
|
|
|
# If we ever get here there's either no mod on the archive, or we failed to find it
|
|
raise NoModExceptionError(str(mod_path))
|
|
|
|
else:
|
|
raise UnsupportedArchiveTypeError(str(mod_path))
|
|
|
|
@property
|
|
def is_installed(self) -> bool:
|
|
return self.__is_installed
|
|
|
|
def install(self) -> None:
|
|
"""
|
|
Install the mod to Project Diva's mod folder
|
|
"""
|
|
if self.__is_installed:
|
|
return
|
|
|
|
game_path: Path = GameFinder.find()
|
|
if self.__is_flat:
|
|
mod_folder_name: str = self.__get_mod_folder_name()
|
|
ZipFile(self.__archive_path).extractall(
|
|
path=str(game_path / "mods" / mod_folder_name)
|
|
)
|
|
else:
|
|
ZipFile(self.__archive_path).extractall(path=str(game_path / "mods"))
|
|
|
|
self.__is_installed = True
|
|
|
|
def __get_mod_folder_name(self) -> str:
|
|
mod_folder_name: str
|
|
if self.__is_flat:
|
|
mod_folder_name = self.__archive_path.stem
|
|
else:
|
|
path: ZipPath = ZipPath(ZipFile(self.__archive_path))
|
|
things_in_root: list[ZipPath] = list(path.iterdir())
|
|
mod_folder_name = things_in_root[0].stem
|
|
|
|
return mod_folder_name
|
|
|
|
def __check_if_already_installed(self) -> None:
|
|
installed_mod_path: Path = (
|
|
GameFinder.find() / "mods" / self.__get_mod_folder_name()
|
|
)
|
|
if installed_mod_path.exists():
|
|
self.__is_installed = True
|
|
|
|
|
|
class UnsupportedArchiveTypeError(Exception):
|
|
"""
|
|
Rased when ModInstaller runs into an archive that it can't extract
|
|
"""
|
|
|
|
__message: str
|
|
|
|
def __init__(self, archive_path: str) -> None:
|
|
self.__message = f"Don't know how to unpack archive at {archive_path}"
|
|
|
|
def __str__(self) -> str:
|
|
return f"{self.__message}"
|
|
|
|
|
|
class EmptyArchiveError(Exception):
|
|
pass
|
|
|
|
|
|
class NoModExceptionError(Exception):
|
|
"""
|
|
Raised if the archive does not have a mod inside
|
|
"""
|
|
|
|
__message: str
|
|
|
|
def __init__(self, archive_path: str) -> None:
|
|
self.__message = f"Archive at {archive_path} does not have a mod"
|
|
|
|
def __str__(self) -> str:
|
|
return f"{self.__message}"
|