class RefineryActionsWindow(tk.Toplevel, BinillaWidget): app_root = None settings = None renamable = True propagatable = False accept_rename = None accept_settings = None tag_index_ref = None rename_string = None newtype_string = None recursive_rename = None original_name = "" def __init__(self, *args, **kwargs): title = kwargs.pop('title', None) self.renamable = kwargs.pop('renamable', self.renamable) self.settings = settings = kwargs.pop('settings', {}) self.tag_index_ref = kwargs.pop('tag_index_ref', self.tag_index_ref) # Zatarita, added propagatable kwarg for displaying checkbox self.propagatable = kwargs.pop('propagatable', self.propagatable) BinillaWidget.__init__(self, *args, **kwargs) tk.Toplevel.__init__(self, *args, **kwargs) try: self.iconbitmap(e_c.REFINERY_ICON_PATH) except Exception: if not e_c.IS_LNX: print("Could not load window icon.") self.bind('<Escape>', lambda e=None, s=self, *a, **kw: s.destroy()) if self.app_root is None and hasattr(self.master, 'app_root'): self.app_root = self.master.app_root self.accept_rename = settings.get('accept_rename', tk.IntVar(self)) self.accept_settings = settings.get('accept_settings', tk.IntVar(self)) self.rename_string = settings.get('rename_string', tk.StringVar(self)) self.newtype_string = settings.get('newtype_string', tk.StringVar(self)) self.extract_to_dir = settings.get('out_dir', tk.StringVar(self)) self.tagslist_path = settings.get('tagslist_path', tk.StringVar(self)) self.extract_mode = settings.get('extract_mode', tk.StringVar(self)) self.recursive_rename = tk.IntVar(self) self.resizable(1, 0) if title is None: title = self.rename_string.get() if not title: title = "Options" self.title(title) self.original_name = PureWindowsPath(self.rename_string.get()) if self.tag_index_ref is not None: # this ActionsWindow is displaying a single tag. the # original name will have an extension. remove it self.original_name = self.original_name.with_suffix("") self.rename_string.set(str(self.original_name)) self.newtype_string.set("") self.accept_rename.set(0) self.accept_settings.set(0) # frames self.rename_frame = tk.LabelFrame(self, text="Rename to") self.rename_frame_inner0 = tk.Frame(self.rename_frame) self.rename_frame_inner1 = tk.Frame(self.rename_frame) self.tags_list_frame = tk.LabelFrame( self, text="Tags list log(erase to disable logging)") self.extract_to_frame = tk.LabelFrame(self, text="Directory to extract to") self.settings_frame = tk.LabelFrame(self, text="Extract settings") self.button_frame = tk.Frame(self) self.accept_frame = tk.Frame(self.button_frame) self.cancel_frame = tk.Frame(self.button_frame) # rename self.rename_entry = tk.Entry(self.rename_frame_inner0, width=50, textvariable=self.rename_string) self.rename_button = tk.Button(self.rename_frame_inner0, text="Rename", command=self.rename, width=6) self.class_scroll_menu = ScrollMenu(self.rename_frame_inner1, menu_width=35) self.recursive_rename_cbtn = tk.Checkbutton( self.rename_frame_inner1, text="Recursive", variable=self.recursive_rename) if self.tag_index_ref: # populate the class_scroll_menu options opts = sorted([n for n in self.tag_index_ref.class_1.NAME_MAP]) self.class_scroll_menu.set_options(opts) try: self.class_scroll_menu.sel_index = opts.index( self.tag_index_ref.class_1.enum_name) except ValueError: pass # tags list self.tags_list_entry = tk.Entry(self.tags_list_frame, width=50, textvariable=self.tagslist_path) self.browse_tags_list_button = tk.Button(self.tags_list_frame, text="Browse", command=self.tags_list_browse) # extract to dir self.extract_to_entry = tk.Entry(self.extract_to_frame, width=50, textvariable=self.extract_to_dir) self.browse_extract_to_button = tk.Button( self.extract_to_frame, text="Browse", command=self.extract_to_browse) # settings self.recursive_cbtn = tk.Checkbutton(self.settings_frame, text="Recursive extraction", variable=settings.get( "recursive", tk.IntVar(self))) self.overwrite_cbtn = tk.Checkbutton( self.settings_frame, text="Overwrite tags(not recommended)", variable=settings.get("overwrite", tk.IntVar(self))) self.do_printout_cbtn = tk.Checkbutton( self.settings_frame, text="Print extracted tag names", variable=settings.get("do_printout", tk.IntVar(self))) # zatarita added do_propegate_settings to place the check box in frame, and link value to variable self.do_propegate_settings = tk.Checkbutton( self.settings_frame, text="Use these settings for each item", variable=settings.get("propagate_settings", tk.IntVar(self))) # accept/cancel self.accept_button = tk.Button(self.accept_frame, text="Add to queue", command=self.add_to_queue, width=14) self.cancel_button = tk.Button(self.cancel_frame, text="Cancel", command=self.destroy, width=14) self.show_meta_button = tk.Button(self, text="Display metadata", command=self.show_meta) # pack everything # frames if self.renamable: self.rename_frame.pack(padx=4, pady=2, expand=True, fill="x") self.rename_frame_inner0.pack(expand=True, fill="x") self.rename_frame_inner1.pack(expand=True, fill="x") self.tags_list_frame.pack(padx=4, pady=2, expand=True, fill="x") self.extract_to_frame.pack(padx=4, pady=2, expand=True, fill="x") self.settings_frame.pack(padx=4, pady=2, expand=True, fill="x") self.button_frame.pack(pady=2, expand=True, fill="x") self.accept_frame.pack(padx=4, side='left', fill='x', expand=True) self.cancel_frame.pack(padx=4, side='right', fill='x', expand=True) # rename self.rename_entry.pack(padx=4, side='left', fill='x', expand=True) self.rename_button.pack(padx=4, side='left', fill='x') if self.tag_index_ref: self.class_scroll_menu.pack(padx=4, side='left', fill='x') #self.recursive_rename_cbtn.pack(padx=4, side='left', fill='x') # extract to self.extract_to_entry.pack(padx=4, side='left', fill='x', expand=True) self.browse_extract_to_button.pack(padx=4, side='left', fill='x') # tags list self.tags_list_entry.pack(padx=4, side='left', fill='x', expand=True) self.browse_tags_list_button.pack(padx=4, side='left', fill='x') # settings # zatarita, added propagatable check box to top of settings frame. (if enabled) if self.propagatable: self.do_propegate_settings.pack(padx=4, anchor='w') self.recursive_cbtn.pack(padx=4, anchor='w') self.overwrite_cbtn.pack(padx=4, anchor='w') self.do_printout_cbtn.pack(padx=4, anchor='w') # accept/cancel self.accept_button.pack(side='right') self.cancel_button.pack(side='left') if self.tag_index_ref is not None: self.show_meta_button.pack(padx=4, pady=4, expand=True, fill='x') # make the window not show up on the start bar self.transient(self.master) self.wait_visibility() self.lift() self.grab_set() self.apply_style() try: self.update() self.app_root.place_window_relative(self) # I would use focus_set, but it doesn't seem to always work self.accept_button.focus_force() except AttributeError: pass def apply_style(self, seen=None): BinillaWidget.apply_style(self, seen) self.update() w, h = self.winfo_reqwidth(), self.winfo_reqheight() self.geometry("%sx%s" % (w, h)) self.minsize(width=w, height=h) def add_to_queue(self, e=None): self.accept_settings.set(1) self.destroy() def rename(self, e=None): if not self.renamable: return new_name = self.rename_string.get() new_name = str(PureWindowsPath(new_name)).lower().strip(".") if self.tag_index_ref is None and new_name: # directory of tags new_name += "\\" old_class = new_class = None try: old_class = self.tag_index_ref.class_1.enum_name except Exception: pass try: new_class = self.class_scroll_menu.get_option() except Exception: new_class = "" #new_name = os.path.splitext(new_name)[0] self.rename_string.set(new_name) str_len = len(new_name) if str_len > MAX_TAG_NAME_LEN: messagebox.showerror( "Max name length exceeded", ("The max length for a tag is limited to %s characters\n" + "Remove %s characters(excluding extension).") % (MAX_TAG_NAME_LEN, str_len - MAX_TAG_NAME_LEN), parent=self) return elif is_protected_tag(new_name): messagebox.showerror("Invalid name", "The entered string is not a valid filename.", parent=self) return elif not str_len and self.tag_index_ref is not None: messagebox.showerror("Invalid name", "The entered string cannot be empty.", parent=self) return self.accept_rename.set(1) # change the type if applicable if new_class and new_class != old_class: self.newtype_string.set(new_class) self.destroy() def tags_list_browse(self): try: init_dir = os.path.dirname(self.tagslist_path.get()) except Exception: init_dir = None dirpath = asksaveasfilename( initialdir=init_dir, parent=self, title="Select where to save the tag list log", filetypes=(("text log", "*.txt"), ("All", "*"))) if not dirpath: return self.tagslist_path.set(str(Path(dirpath))) def extract_to_browse(self): dirpath = askdirectory(initialdir=self.extract_to_dir.get(), parent=self, title="Select the directory to extract tags to") if not dirpath: return self.extract_to_dir.set(str(Path(dirpath))) def show_meta(self): index_ref = self.tag_index_ref if not index_ref: return try: halo_map = self.settings.get("halo_map") if halo_map is None: print("Could not get map.") return disable_safe_mode = getattr(self.app_root, "disable_safe_mode") disable_tag_cleaning = getattr(self.app_root, "disable_tag_cleaning") meta = halo_map.get_meta( index_ref.id & 0xFFff, True, allow_corrupt=self.settings["allow_corrupt"].get(), disable_safe_mode=disable_safe_mode, disable_tag_cleaning=disable_tag_cleaning) if meta is None: print("Could not get meta.") return meta_tag = meta_tag_def.build() meta_tag.data.tagdata = meta tag_path = index_ref.path meta_tag.filepath = tag_path if index_ref.class_1.enum_name not in BAD_CLASSES: ext = ".%s" % index_ref.class_1.enum_name else: ext = ".INVALID" w = MetaWindow(self.app_root, meta_tag, engine=halo_map.engine, tag_path=tag_path + ext) self.destroy() w.focus_set() except Exception: print(format_exc()) return
class HaloBitmapDisplayFrame(BitmapDisplayFrame): # these mappings have the 2nd and 3rd faces swapped on pc for some reason cubemap_cross_mapping = ( (-1, 1), ( 2, 4, 0, 5), (-1, 3), ) def __init__(self, master, bitmap_tag=None, *args, **kwargs): self.bitmap_tag = bitmap_tag textures = kwargs.get('textures', ()) BitmapDisplayFrame.__init__(self, master, *args, **kwargs) self.sequence_index = tk.IntVar(self) self.sprite_index = tk.IntVar(self) labels = [] labels.append(tk.Label(self.controls_frame0, text="Sequence index")) labels.append(tk.Label(self.controls_frame1, text="Sprite index")) for lbl in labels: lbl.config(width=15, anchor='w') self.sequence_menu = ScrollMenu(self.controls_frame0, menu_width=7, variable=self.sequence_index) self.sprite_menu = ScrollMenu(self.controls_frame1, menu_width=7, variable=self.sprite_index) padx = self.horizontal_padx pady = self.horizontal_pady for lbl in labels: lbl.pack(side='left', padx=(15, 0), pady=pady) self.sequence_menu.pack(side='left', padx=padx, pady=pady) self.sprite_menu.pack(side='left', padx=padx, pady=pady) self.write_trace(self.sequence_index, self.sequence_changed) self.write_trace(self.sprite_index, self.sprite_changed) self.change_textures(textures) self.apply_style() def sequence_changed(self, *args): tag = self.bitmap_tag if tag is None: self.sprite_menu.set_options(()) self.sequence_menu.set_options(("None", )) self.sprite_menu.sel_index = -1 self.sequence_menu.sel_index = 0 return sequence_i = self.sequence_index.get() - 1 options = () sequences = tag.data.tagdata.sequences.STEPTREE if sequence_i in range(len(sequences)): sequence = sequences[sequence_i] options = range(len(sequence.sprites.STEPTREE)) if not options: options = range(sequence.bitmap_count) self.sprite_menu.set_options(options) self.sprite_menu.sel_index = (self.sprite_menu.max_index >= 0) - 1 def sprite_changed(self, *args): self.image_canvas.delete(SPRITE_RECTANGLE_TAG) self.image_canvas.delete(SPRITE_CENTER_TAG) tag = self.bitmap_tag if tag is None: self.sprite_menu.set_options(()) self.sequence_menu.set_options(("None", )) self.sprite_menu.sel_index = -1 self.sequence_menu.sel_index = 0 return data = tag.data.tagdata sequences = data.sequences.STEPTREE bitmaps = data.bitmaps.STEPTREE sequence_i = self.sequence_index.get() - 1 if sequence_i not in range(len(sequences)): return sequence = sequences[sequence_i] sprite_i = self.sprite_index.get() sprites = sequence.sprites.STEPTREE bitmap_index = sequence.first_bitmap_index + sprite_i x0, y0, x1, y1 = 0, 0, 1, 1 rx, ry = 0.5, 0.5 if sprite_i in range(len(sprites)): sprite = sprites[sprite_i] if sprite.bitmap_index not in range(len(bitmaps)): return bitmap_index = sprite.bitmap_index x0, y0, x1, y1 = sprite.left_side, sprite.top_side,\ sprite.right_side, sprite.bottom_side rx = x0 + sprite.registration_point_x ry = y0 + sprite.registration_point_y elif bitmap_index not in range(len(bitmaps)): return if bitmap_index != self.bitmap_index.get(): self.bitmap_menu.sel_index = bitmap_index bitmap = bitmaps[bitmap_index] mip = self.mipmap_index.get() w, h = max(bitmap.width>>mip, 1), max(bitmap.height>>mip, 1) x0, y0 = int(round(x0 * w)), int(round(y0 * h)) x1, y1 = int(round(x1 * w)), int(round(y1 * h)) rx, ry = int(round(rx * w)), int(round(ry * h)) rx0, rx1 = rx - 1, rx + 1 ry0, ry1 = ry - 1, ry + 1 if x1 < x0: x0, x1 = x1, x0 if y1 < y0: y0, y1 = y1, y0 if ry1 < ry0: ry0, ry1 = ry1, ry0 x0 -= 1; y0 -= 1 rx0 -= 1; ry0 -= 1 self.image_canvas.create_rectangle( (x0, y0, x1, y1), fill=None, dash=(2, 1), tags=SPRITE_RECTANGLE_TAG, outline=self.bitmap_canvas_outline_color) self.image_canvas.create_rectangle( (rx0, ry0, rx1, ry1), fill=None, tags=SPRITE_CENTER_TAG, outline=self.bitmap_canvas_bg_color) def change_textures(self, textures): BitmapDisplayFrame.change_textures(self, textures) tag = self.bitmap_tag if tag is None: return data = tag.data.tagdata options = {i+1: str(i) for i in range(len(data.sequences.STEPTREE))} options[0] = "None" if not (hasattr(self, "sprite_menu") and hasattr(self, "sequence_menu")): return self.sequence_menu.set_options(options) self.sequence_menu.sel_index = (self.sequence_menu.max_index >= 0) - 1
class BitmapDisplayFrame(BinillaWidget, tk.Frame): app_root = None root_frame_id = None bitmap_index = None # index of the bitmap being displayed mipmap_index = None # the mip level to display of that bitmap channel_index = None # how to display the bitmap: # 0=RGB or L, 1=A, 2=ARGB or AL, 3=R, 4=G, 5=B depth_index = None # since 3d bitmaps must be viewed in 2d slices, # this is the depth of the slice to display. cube_display_index = None # mode to display the cubemap in: # 0 == horizontal, 1 == vertical, # 2 == linear strip prev_bitmap_index = None prev_mipmap_index = None prev_channel_index = None prev_depth_index = None prev_cube_display_index = None changing_settings = False curr_depth = 0 depth_canvas = None depth_canvas_id = None depth_canvas_image_id = None default_bitmap_mapping = ((0, ), ) cubemap_cross_mapping = ( (-1, 2), (1, 4, 0, 5), (-1, 3), ) cubemap_strip_mapping = ((0, 1, 2, 3, 4, 5), ) _image_handlers = None image_canvas_ids = () # does NOT include the depth_canvas_id textures = () # List of textures ready to be loaded into arbytmap. # Structure is as follows: [ (tex_block0, tex_info0), # (tex_block1, tex_info1), # (tex_block2, tex_info2), ... ] temp_root = os.path.join(tempfile.gettempdir(), "arbytmaps") temp_dir = '' def __init__(self, master, *args, **kwargs): BinillaWidget.__init__(self) self.temp_root = kwargs.pop('temp_root', self.temp_root) textures = kwargs.pop('textures', ()) app_root = kwargs.pop('app_root', ()) self.image_canvas_ids = [] self.textures = [] self._image_handlers = {} temp_name = str(int(random.random() * (1 << 32))) self.temp_dir = os.path.join(self.temp_root, temp_name) kwargs.update(relief='flat', bd=self.frame_depth, bg=self.default_bg_color) tk.Frame.__init__(self, master, *args, **kwargs) self.bitmap_index = tk.IntVar(self) self.mipmap_index = tk.IntVar(self) self.depth_index = tk.IntVar(self) self.channel_index = tk.IntVar(self) self.cube_display_index = tk.IntVar(self) self.root_canvas = tk.Canvas(self, highlightthickness=0) self.root_frame = tk.Frame(self.root_canvas, highlightthickness=0) # create the root_canvas and the root_frame within the canvas self.controls_frame0 = tk.Frame(self.root_frame, highlightthickness=0) self.controls_frame1 = tk.Frame(self.root_frame, highlightthickness=0) self.controls_frame2 = tk.Frame(self.root_frame, highlightthickness=0) self.image_root_frame = tk.Frame(self.root_frame, highlightthickness=0) self.image_canvas = tk.Canvas(self.image_root_frame, highlightthickness=0, bg=self.bitmap_canvas_bg_color) self.depth_canvas = tk.Canvas(self.image_canvas, highlightthickness=0, bg=self.bitmap_canvas_bg_color) self.bitmap_menu = ScrollMenu(self.controls_frame0, menu_width=7, variable=self.bitmap_index, can_scroll=True) self.mipmap_menu = ScrollMenu(self.controls_frame1, menu_width=7, variable=self.mipmap_index, can_scroll=True) self.depth_menu = ScrollMenu(self.controls_frame2, menu_width=7, variable=self.depth_index, can_scroll=True) self.channel_menu = ScrollMenu(self.controls_frame0, menu_width=9, variable=self.channel_index, can_scroll=True) self.cube_display_menu = ScrollMenu(self.controls_frame1, menu_width=9, variable=self.cube_display_index, options=("cross", "linear"), can_scroll=True) self.save_button = ttk.Button(self.controls_frame2, width=11, text="Browse", command=self.save_as) self.depth_menu.default_text = self.mipmap_menu.default_text =\ self.bitmap_menu.default_text =\ self.channel_menu.default_text =\ self.cube_display_menu.default_text = "" labels = [] labels.append(tk.Label(self.controls_frame0, text="Bitmap index")) labels.append(tk.Label(self.controls_frame1, text="Mipmap level")) labels.append(tk.Label(self.controls_frame2, text="Depth level")) labels.append(tk.Label(self.controls_frame0, text="Channels")) labels.append(tk.Label(self.controls_frame1, text="Cubemap display")) labels.append(tk.Label(self.controls_frame2, text="Save to file")) for lbl in labels: lbl.config(width=15, anchor='w', bg=self.default_bg_color, fg=self.text_normal_color, disabledforeground=self.text_disabled_color) self.hsb = tk.Scrollbar(self, orient="horizontal", command=self.root_canvas.xview) self.vsb = tk.Scrollbar(self, orient="vertical", command=self.root_canvas.yview) self.root_canvas.config(xscrollcommand=self.hsb.set, xscrollincrement=1, yscrollcommand=self.vsb.set, yscrollincrement=1) for w in [ self.root_frame, self.root_canvas, self.image_canvas, self.controls_frame0, self.controls_frame1, self.controls_frame2 ] + labels: if e_c.IS_LNX: w.bind('<Shift-4>', self.mousewheel_scroll_x) w.bind('<Shift-5>', self.mousewheel_scroll_x) w.bind('<4>', self.mousewheel_scroll_y) w.bind('<5>', self.mousewheel_scroll_y) else: w.bind('<Shift-MouseWheel>', self.mousewheel_scroll_x) w.bind('<MouseWheel>', self.mousewheel_scroll_y) # pack everything # pack in this order so scrollbars aren't shrunk self.root_frame_id = self.root_canvas.create_window( (0, 0), anchor="nw", window=self.root_frame) self.hsb.pack(side='bottom', fill='x', anchor='nw') self.vsb.pack(side='right', fill='y', anchor='nw') self.root_canvas.pack(fill='both', anchor='nw', expand=True) self.controls_frame0.pack(side='top', fill='x', anchor='nw') self.controls_frame1.pack(side='top', fill='x', anchor='nw') self.controls_frame2.pack(side='top', fill='x', anchor='nw') self.image_root_frame.pack(fill='both', anchor='nw', expand=True) self.image_canvas.pack(fill='both', side='right', anchor='nw', expand=True) padx = self.horizontal_padx pady = self.horizontal_pady for lbl in labels[:3]: lbl.pack(side='left', padx=(25, 0), pady=pady) self.bitmap_menu.pack(side='left', padx=padx, pady=pady) self.mipmap_menu.pack(side='left', padx=padx, pady=pady) self.depth_menu.pack(side='left', padx=padx, pady=pady) for lbl in labels[3:]: lbl.pack(side='left', padx=(15, 0), pady=pady) self.save_button.pack(side='left', padx=padx, pady=pady) self.channel_menu.pack(side='left', padx=padx, pady=pady) self.cube_display_menu.pack(side='left', padx=padx, pady=pady) self.change_textures(textures) self.write_trace(self.bitmap_index, self.settings_changed) self.write_trace(self.mipmap_index, self.settings_changed) self.write_trace(self.depth_index, self.settings_changed) self.write_trace(self.cube_display_index, self.settings_changed) self.write_trace(self.channel_index, self.settings_changed) self.apply_style() def destroy(self): try: self.clear_canvas() self.clear_depth_canvas() except Exception: pass try: del self.textures[:] except Exception: pass try: del self._image_handlers[:] except Exception: pass self.image_canvas_ids = self._image_handlers = None self.textures = None tk.Frame.destroy(self) self.delete_all_traces() self.delete_all_widget_refs() gc.collect() @property def active_image_handler(self): b = self.bitmap_index.get() if b not in range(len(self.textures)) or not import_arbytmap(): return None elif b not in self._image_handlers: # make a new PhotoImageHandler if one doesnt exist already self._image_handlers[b] = PhotoImageHandler( self.textures[b][0], self.textures[b][1], self.temp_dir) return self._image_handlers[b] @property def should_update(self): return (self.prev_bitmap_index != self.bitmap_index.get() or self.prev_mipmap_index != self.mipmap_index.get() or self.prev_channel_index != self.channel_index.get()) def mousewheel_scroll_x(self, e): # prevent scrolling if the root_canvas.bbox width >= canvas width bbox = self.root_canvas.bbox(tk.ALL) if not bbox or (self.root_canvas.winfo_width() >= bbox[2] - bbox[0]): return delta = getattr(self.app_root, "scroll_increment_x", 20) self.root_canvas.xview_scroll(int(get_mouse_delta(e) * delta), "units") def mousewheel_scroll_y(self, e): # prevent scrolling if the root_canvas.bbox height >= canvas height bbox = self.root_canvas.bbox(tk.ALL) if not bbox or (self.root_canvas.winfo_height() >= bbox[3] - bbox[1]): return delta = getattr(self.app_root, "scroll_increment_y", 20) self.root_canvas.yview_scroll(int(get_mouse_delta(e) * delta), "units") def update_scroll_regions(self): if not self.image_canvas_ids and not self.depth_canvas_id: return rf = self.root_frame region = self.image_canvas.bbox(tk.ALL) # bbox region isn't actually x, y, w, h, but if the # origin is at (0,0) we can treat it like that if region is None: x, y, w, h = (0, 0, 0, 0) else: x, y, w, h = region self.image_canvas.config(scrollregion=(x, y, w, h)) rf.update_idletasks() max_w = w total_h = h for widget in self.root_frame.children.values(): if widget is not self.image_root_frame: max_w = max(widget.winfo_reqwidth(), max_w) total_h += widget.winfo_reqheight() self.root_canvas.itemconfigure(self.root_frame_id, width=max_w, height=total_h) self.root_canvas.config(scrollregion=(0, 0, max_w, total_h)) def save_as(self, e=None, initial_dir=None): handler = self.active_image_handler if handler is None: return None fp = asksaveasfilename(initialdir=initial_dir, defaultextension='.dds', title="Save bitmap as...", parent=self, filetypes=(("DirectDraw surface", "*.dds"), ('Portable network graphics', '*.png'), ('Truevision graphics adapter', '*.tga'), ('Raw pixel data', '*.bin'))) fp, ext = os.path.splitext(fp) if not fp: return elif not ext: ext = ".dds" mip_levels = "all" if ext.lower() == ".dds" else self.mipmap_index.get( ) handler.arby.save_to_file(output_path=fp, ext=ext, overwrite=True, mip_levels=mip_levels, bitmap_indexes="all", keep_alpha=handler.channels.get("A"), swizzle_mode=False, channel_mapping=handler.channel_mapping, target_big_endian=False, tile_mode=False) def clear_depth_canvas(self): self.depth_canvas.delete(tk.ALL) self.prev_depth_index = None self.prev_cube_display_index = None self.depth_canvas_image_id = None def clear_canvas(self): self.image_canvas.delete(tk.ALL) self.clear_depth_canvas() self.depth_canvas_id = None self.image_canvas_ids = [] def hide_depth_canvas(self): if self.depth_canvas_id is not None: self.image_canvas.delete(self.depth_canvas_id) self.depth_canvas_id = None def show_depth_canvas(self): self.depth_canvas_id = self.image_canvas.create_window( (0, 0), anchor="nw", window=self.depth_canvas) def change_textures(self, textures): assert hasattr(textures, '__iter__') for tex in textures: assert len(tex) == 2 assert isinstance(tex[1], dict) if self._image_handlers: del self._image_handlers if self.textures: del self.textures[:] self.textures = list(textures) self._image_handlers = {} self.bitmap_index.set(0) self.mipmap_index.set(0) self.channel_index.set(0) self.depth_index.set(0) self.cube_display_index.set(0) self.bitmap_menu.set_options(range(len(textures))) self.prev_bitmap_index = None self.prev_mipmap_index = None self.prev_channel_index = None self.prev_depth_index = None self.prev_cube_display_index = None self.update_bitmap(force=True) def get_images(self): image_handler = self.active_image_handler if not image_handler: return images = image_handler.get_images(mip_levels=self.mipmap_index.get()) return tuple(images[i] for i in sorted(images.keys())) def settings_changed(self, *args, force=False): handler = self.active_image_handler force = False if not handler: return elif self.changing_settings: return elif self.prev_bitmap_index != self.bitmap_index.get(): force = True elif self.prev_mipmap_index != self.mipmap_index.get(): force = True elif self.prev_channel_index != self.channel_index.get(): force = True elif self.prev_depth_index != self.depth_index.get(): pass elif self.prev_cube_display_index != self.cube_display_index.get(): force = True else: return self.changing_settings = True new_mip_options = range(handler.max_mipmap + 1) if self.mipmap_index.get() not in new_mip_options: self.mipmap_index.set(0) max_depth = handler.mip_depth(self.mipmap_index.get()) self.mipmap_menu.set_options(new_mip_options) self.depth_menu.set_options(range(max_depth)) if self.depth_menu.sel_index > max_depth - 1: self.depth_menu.sel_index = max_depth - 1 channel_count = handler.channel_count if channel_count <= 2: opts = ("Luminance", "Alpha", "AL") else: opts = ("RGB", "Alpha", "ARGB", "Red", "Green", "Blue") self.channel_menu.set_options(opts) try: handler.set_channel_mode(self.channel_index.get()) self.update_bitmap(force=force) self.changing_settings = False except Exception: self.changing_settings = False raise def update_bitmap(self, *args, force=False): handler = self.active_image_handler if handler is None: return None tex_type = handler.tex_type if tex_type == "2D": self._display_2d_bitmap(force) elif tex_type == "3D": self._display_3d_bitmap(force) elif tex_type == "CUBE": self._display_cubemap(force) self.prev_bitmap_index = self.bitmap_index.get() self.prev_mipmap_index = self.mipmap_index.get() self.prev_channel_index = self.channel_index.get() self.prev_depth_index = self.depth_index.get() self.prev_cube_display_index = self.cube_display_index.get() def _display_cubemap(self, force=False, bitmap_mapping=None): mapping_type = self.cube_display_index.get() if bitmap_mapping is None: if mapping_type == 0: bitmap_mapping = self.cubemap_cross_mapping else: bitmap_mapping = self.cubemap_strip_mapping self._display_2d_bitmap(force, bitmap_mapping) def _display_2d_bitmap(self, force=False, bitmap_mapping=None): images = self.get_images() if not images or not (self.should_update or force): return w = max(image.width() for image in images) h = max(image.height() for image in images) if bitmap_mapping is None: bitmap_mapping = self.default_bitmap_mapping self.clear_canvas() # place the bitmaps on the canvas y = 0 for line in bitmap_mapping: max_column_ct = max(0, len(line)) x = 0 for image_index in line: if image_index in range(len(images)): # place the cube face on the canvas self.image_canvas_ids.append( self.image_canvas.create_image( (x, y), anchor="nw", image=images[image_index], tags=("BITMAP", "2D_BITMAP"))) x += w y += h self.update_scroll_regions() def _display_3d_bitmap(self, force=False): if self.should_update or self.depth_canvas_image_id is None or force: self.clear_canvas() self.show_depth_canvas() handler = self.active_image_handler images = self.get_images() if not (images and handler): return m = self.mipmap_index.get() w, h = images[0].width(), handler.mip_height(m) self.curr_depth = handler.mip_depth(m) # place the bitmap on the canvas self.depth_canvas_image_id = self.depth_canvas.create_image( (0, 0), anchor="nw", image=images[0], tags=("BITMAP", "3D_BITMAP")) self.image_canvas.itemconfig(self.depth_canvas_id, width=w, height=h) self.depth_canvas.config(scrollregion="0 0 %s %s" % (w, h)) self.update_scroll_regions() self.depth_canvas.coords( self.depth_canvas_image_id, (0, -self.depth_index.get() * self.curr_depth))