Also make verification and uninstallation case-insensitive if necessary

This also only enables case-insensitive file tasks if installing a
Windows game
This commit is contained in:
Mathis Dröge 2025-02-11 20:58:21 +01:00
parent f08dbc4a62
commit 48b37d47cf
No known key found for this signature in database
GPG key ID: 3071D4EFBB298F5F
5 changed files with 33 additions and 12 deletions

View file

@ -1268,7 +1268,8 @@ class LegendaryCLI:
logger.info(f'Verifying "{igame.title}" version "{manifest.meta.build_version}"')
repair_file = []
for result, path, result_hash, bytes_read in validate_files(igame.install_path, file_list):
for result, path, result_hash, bytes_read in validate_files(igame.install_path, file_list,
case_insensitive=igame.platform.startswith('Win')):
processed += bytes_read
percentage = (processed / total_size) * 100.0
num += 1

View file

@ -1498,7 +1498,7 @@ class LegendaryCore:
dlm = DLManager(install_path, base_url, resume_file=resume_file, status_q=status_q,
max_shared_memory=max_shm * 1024 * 1024, max_workers=max_workers,
dl_timeout=dl_timeout, bind_ip=bind_ip)
dl_timeout=dl_timeout, bind_ip=bind_ip, case_insensitive=platform.startswith('Win'))
anlres = dlm.run_analysis(manifest=new_manifest, old_manifest=old_manifest,
patch=not disable_patching, resume=not force,
file_prefix_filter=file_prefix_filter,
@ -1697,7 +1697,8 @@ class LegendaryCore:
fm.filename for fm in manifest.file_manifest_list.elements if
not fm.install_tags or any(t in installed_game.install_tags for t in fm.install_tags)
]
if not delete_filelist(installed_game.install_path, filelist, delete_root_directory):
if not delete_filelist(installed_game.install_path, filelist, delete_root_directory,
case_insensitive=installed_game.platform.startswith('Win')):
self.log.error(f'Deleting "{installed_game.install_path}" failed, please remove manually.')
except Exception as e:
self.log.error(f'Deleting failed with {e!r}, please remove {installed_game.install_path} manually.')
@ -1717,7 +1718,10 @@ class LegendaryCore:
and os.path.exists(os.path.join(installed_game.install_path, fm.filename))
]
if not delete_filelist(installed_game.install_path, filelist):
if not delete_filelist(
installed_game.install_path, filelist,
case_insensitive=installed_game.platform.startswith('Win')
):
self.log.warning(f'Deleting some deselected files failed, please check/remove manually.')
def prereq_installed(self, app_name):

View file

@ -22,7 +22,7 @@ from legendary.models.manifest import ManifestComparison, Manifest
class DLManager(Process):
def __init__(self, download_dir, base_url, cache_dir=None, status_q=None,
max_workers=0, update_interval=1.0, dl_timeout=10, resume_file=None,
max_shared_memory=1024 * 1024 * 1024, bind_ip=None):
max_shared_memory=1024 * 1024 * 1024, bind_ip=None, case_insensitive=True):
super().__init__(name='DLManager')
self.log = logging.getLogger('DLM')
self.proc_debug = False
@ -42,6 +42,7 @@ class DLManager(Process):
self.max_workers = max_workers or min(cpu_count() * 2, 16)
self.dl_timeout = dl_timeout
self.bind_ips = [] if not bind_ip else bind_ip.split(',')
self.case_insensitive = case_insensitive
# Analysis stuff
self.analysis = None
@ -672,7 +673,8 @@ class DLManager(Process):
self.log.info('Starting file writing worker...')
writer_p = FileWorker(self.writer_queue, self.writer_result_q, self.dl_dir,
self.shared_memory.name, self.cache_dir, self.logging_queue)
self.shared_memory.name, self.cache_dir, self.logging_queue,
self.case_insensitive)
self.children.append(writer_p)
writer_p.start()

View file

@ -148,7 +148,8 @@ class DLWorker(Process):
class FileWorker(Process):
def __init__(self, queue, out_queue, base_path, shm, cache_path=None, logging_queue=None):
def __init__(self, queue, out_queue, base_path, shm, cache_path=None, logging_queue=None,
case_insensitive: bool = True):
super().__init__(name='FileWorker')
self.q = queue
self.o_q = out_queue
@ -157,6 +158,7 @@ class FileWorker(Process):
self.shm = SharedMemory(name=shm)
self.log_level = logging.getLogger().level
self.logging_queue = logging_queue
self.case_insensitive = case_insensitive
def run(self):
# we have to fix up the logger before we can start
@ -189,7 +191,9 @@ class FileWorker(Process):
# make directories if required
path, filename = os.path.split(j.filename)
file_dir = case_insensitive_file_search(os.path.join(self.base_path, path))
file_dir = os.path.join(self.base_path, path)
if self.case_insensitive:
file_dir = case_insensitive_file_search(file_dir)
if not os.path.exists(file_dir):
os.makedirs(file_dir)

View file

@ -13,6 +13,7 @@ from typing import List, Iterator
from filelock import FileLock
from legendary.lfs.wine_helpers import case_insensitive_file_search
from legendary.models.game import VerifyResult
logger = logging.getLogger('LFS Utils')
@ -34,7 +35,7 @@ def delete_folder(path: str, recursive=True) -> bool:
def delete_filelist(path: str, filenames: List[str],
delete_root_directory: bool = False,
silent: bool = False) -> bool:
silent: bool = False, case_insensitive: bool = True) -> bool:
dirs = set()
no_error = True
@ -45,7 +46,10 @@ def delete_filelist(path: str, filenames: List[str],
dirs.add(_dir)
try:
os.remove(os.path.join(path, _dir, _fn))
full_path = os.path.join(path, _dir, _fn)
if case_insensitive:
full_path = case_insensitive_file_search(full_path)
os.remove(full_path)
except Exception as e:
if not silent:
logger.error(f'Failed deleting file {filename} with {e!r}')
@ -61,7 +65,10 @@ def delete_filelist(path: str, filenames: List[str],
# remove all directories
for _dir in sorted(dirs, key=len, reverse=True):
try:
os.rmdir(os.path.join(path, _dir))
dir_path = os.path.join(path, _dir)
if case_insensitive:
dir_path = case_insensitive_file_search(dir_path)
os.rmdir(dir_path)
except FileNotFoundError:
# directory has already been deleted, ignore that
continue
@ -81,7 +88,7 @@ def delete_filelist(path: str, filenames: List[str],
def validate_files(base_path: str, filelist: List[tuple], hash_type='sha1',
large_file_threshold=1024 * 1024 * 512) -> Iterator[tuple]:
large_file_threshold=1024 * 1024 * 512, case_insensitive: bool = True) -> Iterator[tuple]:
"""
Validates the files in filelist in path against the provided hashes
@ -89,6 +96,7 @@ def validate_files(base_path: str, filelist: List[tuple], hash_type='sha1',
:param filelist: list of tuples in format (path, hash [hex])
:param hash_type: (optional) type of hash, default is sha1
:param large_file_threshold: (optional) threshold for large files, default is 512 MiB
:param case_insensitive: (optional) whether to search for files case insensitively
:return: yields tuples in format (VerifyResult, path, hash [hex], bytes read)
"""
@ -100,6 +108,8 @@ def validate_files(base_path: str, filelist: List[tuple], hash_type='sha1',
for file_path, file_hash in filelist:
full_path = os.path.join(base_path, file_path)
if case_insensitive:
full_path = case_insensitive_file_search(full_path)
# logger.debug(f'Checking "{file_path}"...')
if not os.path.exists(full_path):