diff --git a/legendary/cli.py b/legendary/cli.py index 9d8430d..561eec6 100644 --- a/legendary/cli.py +++ b/legendary/cli.py @@ -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 diff --git a/legendary/core.py b/legendary/core.py index 04344e3..680ecda 100644 --- a/legendary/core.py +++ b/legendary/core.py @@ -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): diff --git a/legendary/downloader/mp/manager.py b/legendary/downloader/mp/manager.py index 90ab37a..cc80783 100644 --- a/legendary/downloader/mp/manager.py +++ b/legendary/downloader/mp/manager.py @@ -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() diff --git a/legendary/downloader/mp/workers.py b/legendary/downloader/mp/workers.py index 1a13042..5d4f0e6 100644 --- a/legendary/downloader/mp/workers.py +++ b/legendary/downloader/mp/workers.py @@ -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) diff --git a/legendary/lfs/utils.py b/legendary/lfs/utils.py index 1b9662c..85c2793 100644 --- a/legendary/lfs/utils.py +++ b/legendary/lfs/utils.py @@ -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):