def export( self, style: packageLoader.Style, selected_objects: dict, should_refresh=False, ) -> Tuple[bool, bool]: """Generate the editoritems.txt and vbsp_config. - If no backup is present, the original editoritems is backed up. - For each object type, run its .export() function with the given - item. - Styles are a special case. """ LOGGER.info('-' * 20) LOGGER.info('Exporting Items and Style for "{}"!', self.name) LOGGER.info('Style = {}', style.id) for obj, selected in selected_objects.items(): # Skip lists and dicts etc - too long if selected is None or isinstance(selected, str): LOGGER.info('{} = {}', obj, selected) # VBSP, VRAD, editoritems export_screen.set_length('BACK', len(FILES_TO_BACKUP)) # files in compiler/ try: num_compiler_files = len(os.listdir('../compiler')) except FileNotFoundError: num_compiler_files = 0 if self.steamID == utils.STEAM_IDS['APERTURE TAG']: # Coop paint gun instance num_compiler_files += 1 if num_compiler_files == 0: LOGGER.warning('No compiler files!') export_screen.skip_stage('COMP') else: export_screen.set_length('COMP', num_compiler_files) LOGGER.info('Should refresh: {}', should_refresh) if should_refresh: # Check to ensure the cache needs to be copied over.. should_refresh = self.cache_invalid() if should_refresh: LOGGER.info("Cache invalid - copying..") else: LOGGER.info("Skipped copying cache!") # The items, plus editoritems, vbsp_config and the instance list. export_screen.set_length('EXP', len(packageLoader.OBJ_TYPES) + 3) # Do this before setting music and resources, # those can take time to compute. export_screen.show() export_screen.grab_set_global() # Stop interaction with other windows if should_refresh: # Count the files. export_screen.set_length( 'RES', sum(1 for file in res_system.walk_folder_repeat()), ) else: export_screen.skip_stage('RES') export_screen.skip_stage('MUS') # Make the folders we need to copy files to, if desired. os.makedirs(self.abs_path('bin/bee2/'), exist_ok=True) # Start off with the style's data. editoritems, vbsp_config = style.export() export_screen.step('EXP') vpk_success = True # Export each object type. for obj_name, obj_data in packageLoader.OBJ_TYPES.items(): if obj_name == 'Style': continue # Done above already LOGGER.info('Exporting "{}"', obj_name) selected = selected_objects.get(obj_name, None) try: obj_data.cls.export(packageLoader.ExportData( game=self, selected=selected, editoritems=editoritems, vbsp_conf=vbsp_config, selected_style=style, )) except packageLoader.NoVPKExport: # Raised by StyleVPK to indicate it failed to copy. vpk_success = False export_screen.step('EXP') vbsp_config.set_key( ('Options', 'BEE2_loc'), os.path.dirname(os.getcwd()) # Go up one dir to our actual location ) vbsp_config.set_key( ('Options', 'Game_ID'), self.steamID, ) # If there are multiple of these blocks, merge them together. # They will end up in this order. vbsp_config.merge_children( 'Textures', 'Fizzler', 'Options', 'StyleVars', 'Conditions', 'Voice', 'PackTriggers', ) for name, file, ext in FILES_TO_BACKUP: item_path = self.abs_path(file + ext) backup_path = self.abs_path(file + '_original' + ext) if os.path.isfile(item_path) and not os.path.isfile(backup_path): LOGGER.info('Backing up original {}!', name) shutil.copy(item_path, backup_path) export_screen.step('BACK') # Backup puzzles, if desired backup.auto_backup(selected_game, export_screen) # This is the connection "heart" and "error" models. # These have to come last, so we need to special case it. editoritems += style.editor.find_key("Renderables", []).copy() # Special-case: implement the UnlockDefault stlylevar here, # so all items are modified. if selected_objects['StyleVar']['UnlockDefault']: LOGGER.info('Unlocking Items!') for item in editoritems.find_all('Item'): # If the Unlock Default Items stylevar is enabled, we # want to force the corridors and obs room to be # deletable and copyable # Also add DESIRES_UP, so they place in the correct orientation if item['type', ''] in _UNLOCK_ITEMS: editor_section = item.find_key("Editor", []) editor_section['deletable'] = '1' editor_section['copyable'] = '1' editor_section['DesiredFacing'] = 'DESIRES_UP' LOGGER.info('Editing Gameinfo!') self.edit_gameinfo(True) LOGGER.info('Writing instance list!') with open(self.abs_path('bin/bee2/instances.cfg'), 'w', encoding='utf8') as inst_file: for line in self.build_instance_data(editoritems): inst_file.write(line) export_screen.step('EXP') # AtomicWriter writes to a temporary file, then renames in one step. # This ensures editoritems won't be half-written. LOGGER.info('Writing Editoritems!') with srctools.AtomicWriter(self.abs_path( 'portal2_dlc2/scripts/editoritems.txt')) as editor_file: for line in editoritems.export(): editor_file.write(line) export_screen.step('EXP') LOGGER.info('Writing VBSP Config!') os.makedirs(self.abs_path('bin/bee2/'), exist_ok=True) with open(self.abs_path('bin/bee2/vbsp_config.cfg'), 'w', encoding='utf8') as vbsp_file: for line in vbsp_config.export(): vbsp_file.write(line) export_screen.step('EXP') if num_compiler_files > 0: LOGGER.info('Copying Custom Compiler!') for file in os.listdir('../compiler'): src_path = os.path.join('../compiler', file) if not os.path.isfile(src_path): continue dest = self.abs_path('bin/' + file) LOGGER.info('\t* compiler/{0} -> bin/{0}', file) try: if os.path.isfile(dest): # First try and give ourselves write-permission, # if it's set read-only. utils.unset_readonly(dest) shutil.copy( src_path, self.abs_path('bin/') ) except PermissionError: # We might not have permissions, if the compiler is currently # running. export_screen.grab_release() export_screen.reset() messagebox.showerror( title=_('BEE2 - Export Failed!'), message=_('Copying compiler file {file} failed.' 'Ensure the {game} is not running.').format( file=file, game=self.name, ), master=TK_ROOT, ) return False, vpk_success export_screen.step('COMP') if should_refresh: LOGGER.info('Copying Resources!') self.refresh_cache() self.copy_mod_music() if self.steamID == utils.STEAM_IDS['APERTURE TAG']: os.makedirs(self.abs_path('sdk_content/maps/instances/bee2/'), exist_ok=True) with open(self.abs_path('sdk_content/maps/instances/bee2/tag_coop_gun.vmf'), 'w') as f: TAG_COOP_INST_VMF.export(f) export_screen.grab_release() export_screen.reset() # Hide loading screen, we're done return True, vpk_success
def export( self, style, all_items, music, skybox, voice, style_vars, elevator, pack_list, editor_sounds, should_refresh=False, ): """Generate the editoritems.txt and vbsp_config. - If no backup is present, the original editoritems is backed up. - We unlock the mandatory items if specified. - """ LOGGER.info('-' * 20) LOGGER.info('Exporting Items and Style for "{}"!', self.name) LOGGER.info('Style = {}', style) LOGGER.info('Music = {}', music) LOGGER.info('Voice = {}', voice) LOGGER.info('Skybox = {}', skybox) LOGGER.info('Elevator = {}', elevator) LOGGER.info('Style Vars:') LOGGER.info(' {') for key, val in style_vars.items(): LOGGER.info(' {} = {!s}', key, val) LOGGER.info(' }') LOGGER.info('{} Pack Lists!', len(pack_list)) LOGGER.info('{} Editor Sounds!', len(editor_sounds)) LOGGER.info('-' * 20) # VBSP, VRAD, editoritems export_screen.set_length('BACK', len(FILES_TO_BACKUP)) export_screen.set_length( 'CONF', # VBSP_conf, Editoritems, instances, gameinfo, pack_lists, # editor_sounds, template VMF 7 + # Don't add the voicelines to the progress bar if not selected (0 if voice is None else len(VOICE_PATHS)), ) # files in compiler/ export_screen.set_length('COMP', len(os.listdir('../compiler'))) if should_refresh: export_screen.set_length('RES', extract_packages.res_count) else: export_screen.skip_stage('RES') export_screen.show() export_screen.grab_set_global() # Stop interaction with other windows vbsp_config = style.config.copy() # Editoritems.txt is composed of a "ItemData" block, holding "Item" and # "Renderables" sections. editoritems = Property("ItemData", list(style.editor.find_all('Item'))) for item in sorted(all_items): item_block, editor_parts, config_part = all_items[item].export() editoritems += item_block editoritems += editor_parts vbsp_config += config_part if voice is not None: vbsp_config += voice.config if skybox is not None: vbsp_config.set_key( ('Textures', 'Special', 'Sky'), skybox.material, ) vbsp_config += skybox.config if style.has_video: if elevator is None: # Use a randomised video vbsp_config.set_key( ('Elevator', 'type'), 'RAND', ) elif elevator.id == 'VALVE_BLUESCREEN': # This video gets a special script and handling vbsp_config.set_key( ('Elevator', 'type'), 'BSOD', ) else: # Use the particular selected video vbsp_config.set_key( ('Elevator', 'type'), 'FORCE', ) vbsp_config.set_key( ('Elevator', 'horiz'), elevator.horiz_video, ) vbsp_config.set_key( ('Elevator', 'vert'), elevator.vert_video, ) else: # No elevator video for this style vbsp_config.set_key( ('Elevator', 'type'), 'NONE', ) if music is not None: if music.sound is not None: vbsp_config.set_key( ('Options', 'music_SoundScript'), music.sound, ) if music.inst is not None: vbsp_config.set_key( ('Options', 'music_instance'), music.inst, ) if music.packfiles: vbsp_config.set_key( ('PackTriggers', 'Forced'), [ Property('File', file) for file in music.packfiles ], ) vbsp_config.set_key(('Options', 'music_ID'), music.id) vbsp_config += music.config if voice is not None: vbsp_config.set_key( ('Options', 'voice_pack'), voice.id, ) vbsp_config.set_key( ('Options', 'voice_char'), ','.join(voice.chars) ) if voice.cave_skin is not None: vbsp_config.set_key( ('Options', 'cave_port_skin'), voice.cave_skin, ) vbsp_config.set_key( ('Options', 'BEE2_loc'), os.path.dirname(os.getcwd()) # Go up one dir to our actual location ) vbsp_config.set_key( ('Options', 'Game_ID'), self.steamID, ) vbsp_config.ensure_exists('StyleVars') vbsp_config['StyleVars'] += [ Property(key, utils.bool_as_int(val)) for key, val in style_vars.items() ] pack_block = Property('PackList', []) # A list of materials which will casue a specific packlist to be used. pack_triggers = Property('PackTriggers', []) for key, pack in pack_list.items(): pack_block.append(Property( key, [ Property('File', file) for file in pack.files ] )) for trigger_mat in pack.trigger_mats: pack_triggers.append( Property('Material', [ Property('Texture', trigger_mat), Property('PackList', pack.id), ]) ) if pack_triggers.value: vbsp_config.append(pack_triggers) # If there are multiple of these blocks, merge them together # They will end up in this order. vbsp_config.merge_children( 'Textures', 'Fizzler', 'Options', 'StyleVars', 'Conditions', 'Voice', 'PackTriggers', ) for name, file, ext in FILES_TO_BACKUP: item_path = self.abs_path(file + ext) backup_path = self.abs_path(file + '_original' + ext) if os.path.isfile(item_path) and not os.path.isfile(backup_path): LOGGER.info('Backing up original {}!', name) shutil.copy(item_path, backup_path) export_screen.step('BACK') # Backup puzzles, if desired backup.auto_backup(selected_game, export_screen) # This is the connections "heart" icon and "error" icon editoritems += style.editor.find_key("Renderables", []) if style_vars.get('UnlockDefault', False): LOGGER.info('Unlocking Items!') for item in editoritems.find_all('Item'): # If the Unlock Default Items stylevar is enabled, we # want to force the corridors and obs room to be # deletable and copyable # Also add DESIRES_UP, so they place in the correct orientation if item['type', ''] in _UNLOCK_ITEMS: editor_section = item.find_key("Editor", []) editor_section['deletable'] = '1' editor_section['copyable'] = '1' editor_section['DesiredFacing'] = 'DESIRES_UP' LOGGER.info('Editing Gameinfo!') self.edit_gameinfo(True) export_screen.step('CONF') LOGGER.info('Writing Editoritems!') os.makedirs(self.abs_path('portal2_dlc2/scripts/'), exist_ok=True) with open(self.abs_path( 'portal2_dlc2/scripts/editoritems.txt'), 'w') as editor_file: for line in editoritems.export(): editor_file.write(line) export_screen.step('CONF') LOGGER.info('Writing VBSP Config!') os.makedirs(self.abs_path('bin/bee2/'), exist_ok=True) with open(self.abs_path('bin/bee2/vbsp_config.cfg'), 'w') as vbsp_file: for line in vbsp_config.export(): vbsp_file.write(line) export_screen.step('CONF') LOGGER.info('Writing instance list!') with open(self.abs_path('bin/bee2/instances.cfg'), 'w') as inst_file: for line in self.build_instance_data(editoritems): inst_file.write(line) export_screen.step('CONF') LOGGER.info('Writing packing list!') with open(self.abs_path('bin/bee2/pack_list.cfg'), 'w') as pack_file: for line in pack_block.export(): pack_file.write(line) export_screen.step('CONF') LOGGER.info('Editing game_sounds!') self.add_editor_sounds(editor_sounds.values()) export_screen.step('CONF') LOGGER.info('Exporting {} templates!', len(packageLoader.data['BrushTemplate']) ) with open(self.abs_path('bin/bee2/templates.vmf'), 'w') as temp_file: packageLoader.TEMPLATE_FILE.export(temp_file) export_screen.step('CONF') if voice is not None: for prefix, dest, pretty in VOICE_PATHS: path = os.path.join( os.getcwd(), '..', 'config', 'voice', prefix + voice.id + '.cfg', ) LOGGER.info(path) if os.path.isfile(path): shutil.copy( path, self.abs_path('bin/bee2/{}voice.cfg'.format(dest)) ) LOGGER.info('Written "{}voice.cfg"', dest) else: LOGGER.info('No {} voice config!', pretty) export_screen.step('CONF') LOGGER.info('Copying Custom Compiler!') for file in os.listdir('../compiler'): LOGGER.info('\t* compiler/{0} -> bin/{0}', file) try: shutil.copy( os.path.join('../compiler', file), self.abs_path('bin/') ) except PermissionError: # We might not have permissions, if the compiler is currently # running. export_screen.grab_release() export_screen.reset() messagebox.showerror( title='BEE2 - Export Failed!', message='Copying compiler file {file} failed.' 'Ensure the {game} is not running.'.format( file=file, game=self.name, ), master=TK_ROOT, ) return False export_screen.step('COMP') if should_refresh: LOGGER.info('Copying Resources!') self.refresh_cache() export_screen.grab_release() export_screen.reset() # Hide loading screen, we're done return True
def export( self, style: packageLoader.Style, selected_objects: dict, should_refresh=False, ): """Generate the editoritems.txt and vbsp_config. - If no backup is present, the original editoritems is backed up. - For each object type, run its .export() function with the given - item. - Styles are a special case. """ LOGGER.info('-' * 20) LOGGER.info('Exporting Items and Style for "{}"!', self.name) LOGGER.info('Style = {}', style.id) for obj, selected in selected_objects.items(): # Skip lists and dicts etc - too long if selected is None or isinstance(selected, str): LOGGER.info('{} = {}', obj, selected) # VBSP, VRAD, editoritems export_screen.set_length('BACK', len(FILES_TO_BACKUP)) # files in compiler/ export_screen.set_length('COMP', len(os.listdir('../compiler'))) if should_refresh: export_screen.set_length('RES', extract_packages.res_count) else: export_screen.skip_stage('RES') # The items, plus editoritems, vbsp_config and the instance list. export_screen.set_length('EXP', len(packageLoader.OBJ_TYPES) + 3) export_screen.show() export_screen.grab_set_global() # Stop interaction with other windows # Make the folders we need to copy files to, if desired. os.makedirs(self.abs_path('bin/bee2/'), exist_ok=True) # Start off with the style's data. editoritems, vbsp_config = style.export() export_screen.step('EXP') # Export each object type. for obj_name, obj_data in packageLoader.OBJ_TYPES.items(): if obj_name == 'Style': continue # Done above already LOGGER.info('Exporting "{}"', obj_name) selected = selected_objects.get(obj_name, None) LOGGER.debug('Name: {}, selected: {}', obj_name, selected) obj_data.cls.export(packageLoader.ExportData( game=self, selected=selected, editoritems=editoritems, vbsp_conf=vbsp_config, selected_style=style, )) export_screen.step('EXP') vbsp_config.set_key( ('Options', 'BEE2_loc'), os.path.dirname(os.getcwd()) # Go up one dir to our actual location ) vbsp_config.set_key( ('Options', 'Game_ID'), self.steamID, ) # If there are multiple of these blocks, merge them together. # They will end up in this order. vbsp_config.merge_children( 'Textures', 'Fizzler', 'Options', 'StyleVars', 'Conditions', 'Voice', 'PackTriggers', ) for name, file, ext in FILES_TO_BACKUP: item_path = self.abs_path(file + ext) backup_path = self.abs_path(file + '_original' + ext) if os.path.isfile(item_path) and not os.path.isfile(backup_path): LOGGER.info('Backing up original {}!', name) shutil.copy(item_path, backup_path) export_screen.step('BACK') # Backup puzzles, if desired backup.auto_backup(selected_game, export_screen) # This is the connection "heart" and "error" models. # These have to come last, so we need to special case it. editoritems += style.editor.find_key("Renderables", []) # Special-case: implement the UnlockDefault stlylevar here, # so all items are modified. if selected_objects['StyleVar']['UnlockDefault']: LOGGER.info('Unlocking Items!') for item in editoritems.find_all('Item'): # If the Unlock Default Items stylevar is enabled, we # want to force the corridors and obs room to be # deletable and copyable # Also add DESIRES_UP, so they place in the correct orientation if item['type', ''] in _UNLOCK_ITEMS: editor_section = item.find_key("Editor", []) editor_section['deletable'] = '1' editor_section['copyable'] = '1' editor_section['DesiredFacing'] = 'DESIRES_UP' LOGGER.info('Editing Gameinfo!') self.edit_gameinfo(True) LOGGER.info('Writing instance list!') with open(self.abs_path('bin/bee2/instances.cfg'), 'w') as inst_file: for line in self.build_instance_data(editoritems): inst_file.write(line) export_screen.step('EXP') LOGGER.info('Writing Editoritems!') os.makedirs(self.abs_path('portal2_dlc2/scripts/'), exist_ok=True) with open(self.abs_path( 'portal2_dlc2/scripts/editoritems.txt'), 'w') as editor_file: for line in editoritems.export(): editor_file.write(line) export_screen.step('EXP') LOGGER.info('Writing VBSP Config!') os.makedirs(self.abs_path('bin/bee2/'), exist_ok=True) with open(self.abs_path('bin/bee2/vbsp_config.cfg'), 'w') as vbsp_file: for line in vbsp_config.export(): vbsp_file.write(line) export_screen.step('EXP') LOGGER.info('Copying Custom Compiler!') for file in os.listdir('../compiler'): src_path = os.path.join('../compiler', file) if not os.path.isfile(src_path): continue LOGGER.info('\t* compiler/{0} -> bin/{0}', file) try: shutil.copy( src_path, self.abs_path('bin/') ) except PermissionError: # We might not have permissions, if the compiler is currently # running. export_screen.grab_release() export_screen.reset() messagebox.showerror( title='BEE2 - Export Failed!', message='Copying compiler file {file} failed.' 'Ensure the {game} is not running.'.format( file=file, game=self.name, ), master=TK_ROOT, ) return False export_screen.step('COMP') if should_refresh: LOGGER.info('Copying Resources!') self.refresh_cache() export_screen.grab_release() export_screen.reset() # Hide loading screen, we're done return True
def export(self, style: packageLoader.Style, selected_objects: dict, should_refresh=False): """Generate the editoritems.txt and vbsp_config. - If no backup is present, the original editoritems is backed up. - For each object type, run its .export() function with the given - item. - Styles are a special case. """ LOGGER.info("-" * 20) LOGGER.info('Exporting Items and Style for "{}"!', self.name) LOGGER.info("Style = {}", style.id) for obj, selected in selected_objects.items(): # Skip lists and dicts etc - too long if selected is None or isinstance(selected, str): LOGGER.info("{} = {}", obj, selected) # VBSP, VRAD, editoritems export_screen.set_length("BACK", len(FILES_TO_BACKUP)) # files in compiler/ try: num_compiler_files = len(os.listdir("../compiler")) except FileNotFoundError: num_compiler_files = 0 if self.steamID == utils.STEAM_IDS["APERTURE TAG"]: # Coop paint gun instance num_compiler_files += 1 if num_compiler_files == 0: LOGGER.warning("No compiler files!") export_screen.skip_stage("COMP") else: export_screen.set_length("COMP", num_compiler_files) LOGGER.info("Should refresh: {}", should_refresh) if should_refresh: # Check to ensure the cache needs to be copied over.. should_refresh = not self.cache_valid() if should_refresh: export_screen.set_length("RES", extract_packages.res_count) else: export_screen.skip_stage("RES") export_screen.skip_stage("MUS") # The items, plus editoritems, vbsp_config and the instance list. export_screen.set_length("EXP", len(packageLoader.OBJ_TYPES) + 3) export_screen.show() export_screen.grab_set_global() # Stop interaction with other windows # Make the folders we need to copy files to, if desired. os.makedirs(self.abs_path("bin/bee2/"), exist_ok=True) # Start off with the style's data. editoritems, vbsp_config = style.export() export_screen.step("EXP") # Export each object type. for obj_name, obj_data in packageLoader.OBJ_TYPES.items(): if obj_name == "Style": continue # Done above already LOGGER.info('Exporting "{}"', obj_name) selected = selected_objects.get(obj_name, None) obj_data.cls.export( packageLoader.ExportData( game=self, selected=selected, editoritems=editoritems, vbsp_conf=vbsp_config, selected_style=style ) ) export_screen.step("EXP") vbsp_config.set_key( ("Options", "BEE2_loc"), os.path.dirname(os.getcwd()) # Go up one dir to our actual location ) vbsp_config.set_key(("Options", "Game_ID"), self.steamID) # If there are multiple of these blocks, merge them together. # They will end up in this order. vbsp_config.merge_children("Textures", "Fizzler", "Options", "StyleVars", "Conditions", "Voice", "PackTriggers") for name, file, ext in FILES_TO_BACKUP: item_path = self.abs_path(file + ext) backup_path = self.abs_path(file + "_original" + ext) if os.path.isfile(item_path) and not os.path.isfile(backup_path): LOGGER.info("Backing up original {}!", name) shutil.copy(item_path, backup_path) export_screen.step("BACK") # Backup puzzles, if desired backup.auto_backup(selected_game, export_screen) # This is the connection "heart" and "error" models. # These have to come last, so we need to special case it. editoritems += style.editor.find_key("Renderables", []).copy() # Special-case: implement the UnlockDefault stlylevar here, # so all items are modified. if selected_objects["StyleVar"]["UnlockDefault"]: LOGGER.info("Unlocking Items!") for item in editoritems.find_all("Item"): # If the Unlock Default Items stylevar is enabled, we # want to force the corridors and obs room to be # deletable and copyable # Also add DESIRES_UP, so they place in the correct orientation if item["type", ""] in _UNLOCK_ITEMS: editor_section = item.find_key("Editor", []) editor_section["deletable"] = "1" editor_section["copyable"] = "1" editor_section["DesiredFacing"] = "DESIRES_UP" LOGGER.info("Editing Gameinfo!") self.edit_gameinfo(True) LOGGER.info("Writing instance list!") with open(self.abs_path("bin/bee2/instances.cfg"), "w") as inst_file: for line in self.build_instance_data(editoritems): inst_file.write(line) export_screen.step("EXP") # AtomicWriter writes to a temporary file, then renames in one step. # This ensures editoritems won't be half-written. LOGGER.info("Writing Editoritems!") with srctools.AtomicWriter(self.abs_path("portal2_dlc2/scripts/editoritems.txt")) as editor_file: for line in editoritems.export(): editor_file.write(line) export_screen.step("EXP") LOGGER.info("Writing VBSP Config!") os.makedirs(self.abs_path("bin/bee2/"), exist_ok=True) with open(self.abs_path("bin/bee2/vbsp_config.cfg"), "w") as vbsp_file: for line in vbsp_config.export(): vbsp_file.write(line) export_screen.step("EXP") if num_compiler_files > 0: LOGGER.info("Copying Custom Compiler!") for file in os.listdir("../compiler"): src_path = os.path.join("../compiler", file) if not os.path.isfile(src_path): continue dest = self.abs_path("bin/" + file) LOGGER.info("\t* compiler/{0} -> bin/{0}", file) try: if os.path.isfile(dest): # First try and give ourselves write-permission, # if it's set read-only. utils.unset_readonly(dest) shutil.copy(src_path, self.abs_path("bin/")) except PermissionError: # We might not have permissions, if the compiler is currently # running. export_screen.grab_release() export_screen.reset() messagebox.showerror( title=_("BEE2 - Export Failed!"), message=_("Copying compiler file {file} failed." "Ensure the {game} is not running.").format( file=file, game=self.name ), master=TK_ROOT, ) return False export_screen.step("COMP") if should_refresh: LOGGER.info("Copying Resources!") self.refresh_cache() self.copy_mod_music() if self.steamID == utils.STEAM_IDS["APERTURE TAG"]: with open(self.abs_path("sdk_content/maps/instances/bee2/tag_coop_gun.vmf"), "w") as f: TAG_COOP_INST_VMF.export(f) export_screen.grab_release() export_screen.reset() # Hide loading screen, we're done return True