def filterAlreadyLinked(arr): trueind = np.where( array([True] + [ not hardlink.samefile(os.sep.join(arr[0, :2]), os.sep.join(x[:2])) for x in arr[1:] ])) return arr[trueind]
def dupe2link4PFarrayOfSame(arrayOfSomePF, onlyInfo=False): acc1 = array(arrayOfSomePF) forig = os.sep.join(acc1[0, :2]) print "\norig:\t", forig for x in acc1[1:]: fsame = os.sep.join(x[:2]) print fsame, if hardlink.samefile(forig, fsame): print "already hardlink" continue else: print if not onlyInfo: os.remove(fsame) try: hardlink.create(forig, fsame) except: try: print "can't create hardlink. try create symlink" makesymlinkViaCmd(forig, fsame) except: print "can't create symlink. sorry. try just copy" try: shutil.copyfile(forig, fsame) except: print "can't copy. please do something manualy!" raw_input() print "created hardlink" else: print "need to created hardlink"
def samefile(path1, path2): if not os.path.exists(path1) or not os.path.exists(path2): return False if System.is_unix(): return os.path.samefile(path1, path2) return winlink.samefile(path1, path2)
def test(self): link = 'hardlink' contents = 'bar' create(self.FOO, link) self.assertTrue(samefile(self.FOO, link)) with open(link, 'r') as fd: self.assertEqual(fd.read(), self.FOO_CONTENTS) with open(self.FOO, 'w') as fd: fd.write(contents) with open(link, 'r') as fd: self.assertEqual(fd.read(), contents)
async def main(loop): download_timer = Timer() extract_timer = Timer() mod_converge_timer = Timer() apply_converge_timer = Timer() purge_timer = Timer() preproc_timer = Timer() postproc_timer = Timer() load_order_timer = Timer() enable_plugins_timer = Timer() mod_list = [ # Install outside of Data/ FastExit(), FourGBPatch(), OBSE(), ENB(), ENBoost(), MoreHeap(), # OBSE Plugins OneTweak(), # OBSETester(), MenuQue(), ConScribe(), Pluggy(), NifSE(), # Performance Streamline(), OSR(), # Necessary Tweaks ATakeAllAlsoBooks(), DarnifiedUI(), DarnifiedUIConfigAddon(), HarvestFlora(), HarvestContainers(), # Textures QTP3R(), GraphicImprovementProject(), ZiraHorseCompilationModpack(), RingRetexture(), KafeisArmoredCirclets(), KoldornsSewerTextures2(), KoldornsCaveTextures2(), MEAT(), BomretTexturePackForShiveringIslesWithUSIP(), # Gameplay RealisticLeveling(), HUDStatusBars(), UV3(), # Install Last INITweaks(), ArchiveInvalidationInvalidated(), ] converged_paths = {} path_mod_owner = {} # which mod owns which path for path in recurse_files(Config.VANILLA_DIR): converged_paths[str(path).lower()] = Config.VANILLA_DIR / path log.info('downloading') with download_timer: for mod in mod_list: async with aiohttp.ClientSession(loop=loop) as session: await mod.download(session) if False: # stop after download? log.info('stopping after download') return log.info('extracting') with extract_timer: for mod in mod_list: await mod.extract() if False: # stop after extract? log.info('stopping after extract') return log.info('pre-processing') with preproc_timer: for mod in mod_list: await mod.preprocess() log.info('calulcating convergance for each mod') with mod_converge_timer: for mod in mod_list: log.info(f'converging {mod.mod_name}') for source_path, dest_path in mod.modify(): if not isinstance(dest_path, Path): raise Exception(f'{dest_path} is not a Path!') elif dest_path.is_absolute(): raise Exception(f'{dest_path} is not absolute') if not isinstance(source_path, Path): raise Exception(f'{source_path} is not a Path!') elif not source_path.is_absolute(): raise Exception(f'{source_path} is not absolute') elif not source_path.exists(): raise Exception(f'{source_path} does not exist, bad modify code') elif not source_path.is_file(): raise Exception(f'{source_path} is not a regular file.') converged_paths[str(dest_path).lower()] = source_path path_mod_owner[str(dest_path).lower()] = mod.mod_name log.info('applying convergance') with apply_converge_timer: for dest_path, source_path in converged_paths.items(): dest_path = Config.game.root_dir / dest_path if not dest_path.exists() or not samefile(str(dest_path), str(source_path)): if dest_path.exists(): dest_path.unlink() # FIXME move to purged dir? dest_path.parent.mkdir(exist_ok=True, parents=True) try: create_hardlink(str(source_path), str(dest_path)) except FileNotFoundError: raise Exception(f'failed to hard link {source_path} to {dest_path} {source_path} (or {dest_path.parent}) not found') log.info('purging') with purge_timer: try: with Config.mod_ownership_path.open('rb') as f: old_path_mod_owner = json.load(f) except FileNotFoundError: old_path_mod_owner = {} purged_root = Config.PURGED_DIR / datetime.now().isoformat().replace(':', '') for path in recurse_files(Config.game.root_dir): if ( str(path).lower() not in converged_paths and not path.suffix.lower() in {'.ini', '.cfg', '.json', '.log'} and #TODO don't purge xml files unless they're menu files not path.parts[0].lower() in {'obmm', 'mopy'} ): if str(path).lower() in old_path_mod_owner: (Config.game.root_dir / path).unlink() else: purged_path = purged_root / path purged_path.parent.mkdir(exist_ok=True, parents=True) (Config.game.root_dir / path).rename(purged_path) log.info('purging empty directories') for d in recurse_dirs(Config.game.root_dir): if dir_is_empty(d): d.rmdir() log.info('postprocessing') with postproc_timer: for mod in mod_list: await mod.postprocess() log.info('Done Applying Changes') log.info('modifying load order') with load_order_timer: boss_uninstall_string = get_regkey('HKLM', r'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\BOSS', 'UninstallString') boss_install_location = Path(shlex.split(boss_uninstall_string)[0]).parent boss_exe_path = boss_install_location / 'boss.exe' proc = await asyncio.create_subprocess_exec( str(boss_exe_path), '-s', '-g', Config.game.BOSS_NAME, cwd=str(boss_install_location), stderr=sys.stderr, stdout=sys.stdout, ) await proc.wait() log.info('enabling all .esp and .esm files') with enable_plugins_timer: PLUGINS_HEADER = textwrap.dedent(''' # This file is used to tell Oblivion which data files to load. # WRITE YOUR OWN PYTHON SCRIPT TO MODIFY THIS FILE (lol) # Please do not modify this file by hand. ''').strip() with atomic_save(str(Config.game.app_data_path / 'plugins.txt')) as f: with io.TextIOWrapper(f, 'ascii') as ef: ef.write(PLUGINS_HEADER) ef.write('\n') for esm in Config.game.root_dir.glob('Data/*.esm'): ef.write(esm.name) ef.write('\n') for esp in Config.game.root_dir.glob('Data/*.esp'): ef.write(esp.name) ef.write('\n') log.info('saving which mod owns which file') with atomic_save(str(Config.mod_ownership_path)) as f: with io.TextIOWrapper(f, 'ascii') as ef: json.dump(path_mod_owner, ef) log.info(f'download_timer = {download_timer}') log.info(f'extract_timer = {extract_timer}') log.info(f'mod_converge_timer = {mod_converge_timer}') log.info(f'apply_converge_timer = {apply_converge_timer}') log.info(f'purge_timer = {purge_timer}') log.info(f'preproc_timer = {preproc_timer}') log.info(f'postproc_timer = {postproc_timer}') log.info(f'load_order_timer = {load_order_timer}') log.info(f'enable_plugins_timer = {enable_plugins_timer}')