def slot_remove_pointer(self, pointer: Pointer) -> None: pointer_addresses = { pointer.rom_variant: pointer.points_to - ROM_OFFSET } virtual_address = self.constraint_manager.to_virtual(pointer.rom_variant, pointer.address) print(f'remove {pointer}') remove_pointers = [pointer] for rom_variant in self.linked_variants: if rom_variant == pointer.rom_variant: continue local_address = self.constraint_manager.to_local(rom_variant, virtual_address) pointers = get_pointer_database().get_pointers(rom_variant).get_pointers_at(local_address) if len(pointers) != 1: continue pointer_addresses[rom_variant] = pointers[0].points_to - ROM_OFFSET print(f'remove {pointers[0]}') remove_pointers.append(pointers[0]) # Find the corresponding constraints to delete remove_constraints = [] constraints = get_constraint_database().get_constraints() for constraint in constraints: if constraint.romA in pointer_addresses and constraint.addressA == pointer_addresses[constraint.romA]: if constraint.romB in pointer_addresses and constraint.addressB == pointer_addresses[constraint.romB]: remove_constraints.append(constraint) print(f'Remove {constraint}') if QMessageBox.question(self.parent(), 'Remove Pointer', f'Remove {len(remove_pointers)} pointers and {len(remove_constraints)} constraints?') == QMessageBox.Yes: get_pointer_database().remove_pointers(remove_pointers) get_constraint_database().remove_constraints(remove_constraints)
def add_new_constraint(self, constraint: Constraint) -> None: # Check that constraint is valid constraint_manager = ConstraintManager({RomVariant.USA, RomVariant.DEMO, RomVariant.EU, RomVariant.JP, RomVariant.DEMO_JP, RomVariant.CUSTOM, RomVariant.CUSTOM_EU, RomVariant.CUSTOM_JP, RomVariant.CUSTOM_DEMO_USA, RomVariant.CUSTOM_DEMO_JP}) constraint_manager.add_all_constraints( get_constraint_database().get_constraints()) try: constraint_manager.add_constraint(constraint) constraint_manager.rebuild_relations() except InvalidConstraintError as e: QMessageBox.critical(self.parent(), 'Add constraint', 'Invalid Constraint') return get_constraint_database().add_constraint(constraint)
def __init__(self, parent) -> None: super().__init__(parent=parent) self.controllers: List[HexViewerController] = [] self.linked_controllers: List[HexViewerController] = [] self.linked_variants: List[RomVariant] = [] self.contextmenu_handlers = [] if settings.is_using_constraints(): self.constraint_manager = ConstraintManager({}) get_constraint_database().constraints_changed.connect(self.update_constraints) else: self.constraint_manager = NoConstraintManager() self.linked_diff_calculator = LinkedDiffCalculator( self.constraint_manager, self.linked_variants)
def mark_only_in_one(self, controller: HexViewerController, virtual_address: int, length: int) -> None: rom_variant = controller.rom_variant # TODO show dialog for inputs certainty = 1 author = settings.get_username() note = 'Only in ' + rom_variant enabled = True # Get the end of the section only in this variant + 1 local_address = self.constraint_manager.to_local( rom_variant, virtual_address + length) new_constraints = [] for variant in self.linked_variants: if variant != rom_variant: # Link it to the start of the selection in all other variants la = self.constraint_manager.to_local(variant, virtual_address) constraint = Constraint( rom_variant, local_address, variant, la, certainty, author, note, enabled) new_constraints.append(constraint) # Check whether the new constraint is invalid constraint_manager = ConstraintManager({RomVariant.USA, RomVariant.DEMO, RomVariant.EU, RomVariant.JP, RomVariant.DEMO_JP, RomVariant.CUSTOM, RomVariant.CUSTOM_EU, RomVariant.CUSTOM_JP, RomVariant.CUSTOM_DEMO_USA, RomVariant.CUSTOM_DEMO_JP}) constraint_manager.add_all_constraints( get_constraint_database().get_constraints()) try: constraint_manager.add_all_constraints(new_constraints) except InvalidConstraintError as e: raise e constraint_database = get_constraint_database() constraint_database.add_constraints(new_constraints) print(f'mark only in one {rom_variant} {virtual_address} {length}')
def update_constraints(self): if settings.is_using_constraints(): print('update constraints') print(self.linked_variants) self.constraint_manager.reset() if len(self.linked_variants) > 1: print('Add constraints') try: self.constraint_manager.add_all_constraints( get_constraint_database().get_constraints()) except InvalidConstraintError as e: print(e) QMessageBox.critical(self.parent(), 'Constraint Error', 'The current constraints are not valid.') for controller in self.linked_controllers: controller.request_repaint() controller.setup_scroll_bar()
def __init__(self, dock: HexViewerDock, rom_variant: RomVariant, rom: Rom) -> None: super().__init__(parent=dock) self.dock = dock self.area = dock.ui.hexArea self.status_bar = dock.ui.labelStatusBar self.scroll_bar = dock.ui.scrollBar self.rom_variant = rom_variant self.rom = rom self.address_resolver = TrivialAddressResolver() self.diff_calculator = NoDiffCalculator() # State TODO put into different class? self.is_linked = False self.start_offset = 0 self.cursor = 0 self.selected_bytes = 1 self.display_byte_cache = {} # TODO invalidate this cache if a constraint is added # Settings # TODO move elsewhere self.diff_color = QColor(158, 80, 88) # QColor(244, 108, 117) self.pointer_color = QColor(68, 69, 34) self.default_annotation_color = QColor(50, 180, 50) self.default_selection_size = settings.get_default_selection_size() self.highlight_8_bytes = settings.is_highlight_8_bytes() self.contextmenu_handlers = [] self.setup_scroll_bar() self.scroll_bar.valueChanged.connect(self.slot_scroll_bar_changed) # Connect to all necessary UI signals self.dock.ui.pushButtonGoto.clicked.connect(self.slot_show_goto_dialog) self.dock.ui.pushButtonLink.clicked.connect(self.slot_toggle_linked) # self.dock.ui.scrollBar.valueChanged.connect(self.on_scroll_bar_changed) self.area.signal_resized.connect(self.slot_on_resize) self.area.signal_scroll_wheel_changed.connect( self.slot_scroll_wheel_changed) self.area.signal_cursor_changed.connect( self.slot_update_cursor_from_offset) self.area.signal_selection_updated.connect( self.slot_update_selection_from_offset) self.area.signal_key_cursor_pressed.connect( self.slot_key_cursor_pressed) self.area.signal_key_selection_pressed.connect( self.slot_key_selection_pressed) self.area.signal_context_menu_shown.connect( self.slot_shot_context_menu) self.area.signal_show_tooltip_at_offset.connect( self.slot_show_tooltip_at_offset) self.area.signal_go_to_pointer_at_offset.connect( self.slot_go_to_pointer_at) # Keyboard shortcuts QShortcut(QKeySequence(Qt.Key_G), self.dock, self.slot_show_goto_dialog, context=Qt.WidgetWithChildrenShortcut) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_C), self.dock, self.copy_selected_bytes, context=Qt.WidgetWithChildrenShortcut) QShortcut(QKeySequence(Qt.CTRL + Qt.Key_A), self.dock, self.mark_as_all_pointer, context=Qt.WidgetWithChildrenShortcut) QShortcut(QKeySequence(Qt.Key_4), self.dock, lambda:self.update_selected_bytes(4), context=Qt.WidgetWithChildrenShortcut) QShortcut(QKeySequence(Qt.Key_8), self.dock, lambda:self.update_selected_bytes(8), context=Qt.WidgetWithChildrenShortcut) QShortcut(QKeySequence(Qt.Key_F3), self.dock, self.slot_jump_to_next_diff, context=Qt.WidgetWithChildrenShortcut) QShortcut(QKeySequence(Qt.Key_Delete), self.dock, self.slot_delete_current_pointer, context=Qt.WidgetWithChildrenShortcut) # TODO tmp QShortcut(QKeySequence(Qt.Key_Tab), self.dock, lambda:(self.update_cursor(self.cursor+5), self.update_selected_bytes(4)), context=Qt.WidgetWithChildrenShortcut) # Go to next midi command or whatever QShortcut(QKeySequence(Qt.Key_W), self.dock, lambda:(self.update_cursor(self.cursor+12), self.update_selected_bytes(4)), context=Qt.WidgetWithChildrenShortcut) self.pointers: PointerList = None self.annotations: AnnotationList = None self.constraints: ConstraintList = None self.symbols: SymbolList = None if settings.is_using_constraints(): self.update_pointers() get_pointer_database().pointers_changed.connect(self.slot_update_pointers) self.update_annotations() get_annotation_database().annotations_changed.connect(self.slot_update_annotations) if settings.is_using_constraints(): self.update_constraints() get_constraint_database().constraints_changed.connect(self.slot_update_constraints) self.update_symbols() get_symbol_database().symbols_changed.connect(self.slot_update_symbols) self.update_hex_area() self.status_bar.setText('loaded')
def update_constraints(self): if settings.is_using_constraints(): constraint_database = get_constraint_database() constraints = constraint_database.get_constraints() self.constraints = ConstraintList(constraints, self.rom_variant)
def add_pointers_and_constraints(self, pointer: Pointer) -> bool: """ Add a pointer that is the same for all variants and the resulting constraints. Returns true if the constraint changes the relations between the files. """ # Found new_pointers = [pointer] new_constraints = [] virtual_address = self.constraint_manager.to_virtual( pointer.rom_variant, pointer.address) for variant in self.linked_variants: if variant != pointer.rom_variant: address = self.constraint_manager.to_local( variant, virtual_address) points_to = get_rom(variant).get_pointer(address) # Add a corresponding pointer for this variant new_pointers.append(Pointer( variant, address, points_to, pointer.certainty, pointer.author, pointer.note)) # Add a constraint for the places that these two pointers are pointing to, as the pointers should be the same # TODO check that it's actually a pointer into rom note = f'Pointer at {pointer.rom_variant} {hex(pointer.address)}' if pointer.note.strip() != '': note += '\n' + pointer.note # TODO test that adding the added constraints are not invalid # TODO It might be that a constraint that is added with the new_constraints invalidates some other newly added # constraint which then would need to be enabled. Need to test for all new_constraints whether they are actually still valid after adding them to the constraint manager? enabled = self.constraint_manager.to_virtual( pointer.rom_variant, pointer.points_to-ROM_OFFSET) != self.constraint_manager.to_virtual(variant, points_to-ROM_OFFSET) print(f'Add constraint {enabled}') new_constraints.append(Constraint(pointer.rom_variant, pointer.points_to-ROM_OFFSET, variant, points_to-ROM_OFFSET, pointer.certainty, pointer.author, note, enabled)) # Show dialog if one constraint was new one_enabled = False for constraint in new_constraints: if constraint.enabled: one_enabled = True break if one_enabled: # TODO we cannot be sure yet that the one enabled constraint does not interfere with the disabled constraint, # so just enable all constraints again (and disable them later via the constraint cleaner plugin) for constraint in new_constraints: constraint.enabled = True # Check whether the new constraint is invalid constraint_manager = ConstraintManager({RomVariant.USA, RomVariant.DEMO, RomVariant.EU, RomVariant.JP, RomVariant.DEMO_JP, RomVariant.CUSTOM, RomVariant.CUSTOM_EU, RomVariant.CUSTOM_JP, RomVariant.CUSTOM_DEMO_USA, RomVariant.CUSTOM_DEMO_JP}) constraint_manager.add_all_constraints( get_constraint_database().get_constraints()) try: constraint_manager.add_all_constraints(new_constraints) except InvalidConstraintError as e: raise e print('Adding to database') pointer_database = get_pointer_database() pointer_database.add_pointers(new_pointers) constraint_database = get_constraint_database() constraint_database.add_constraints(new_constraints) return one_enabled
def process(self) -> None: print('Start processing') progress = 0 # Test using a constraint manager with all variations manager = ConstraintManager({ RomVariant.USA, RomVariant.JP, RomVariant.EU, RomVariant.DEMO, RomVariant.DEMO_JP }) constraint_database = get_constraint_database() constraints = constraint_database.get_constraints() i = 0 count = len(constraints) for constraint in constraints: if not constraint.enabled: i = i + 1 continue # test if constraint is redundant va_a = manager.to_virtual(constraint.romA, constraint.addressA) va_b = manager.to_virtual(constraint.romB, constraint.addressB) if va_a == va_b: print(f'Disable {constraint}') constraint.enabled = False else: #print(f'Keep {constraint}') manager.add_constraint(constraint) try: manager.rebuild_relations() except InvalidConstraintError as e: print(e) print(constraint) self.signal_fail.emit() return i = i + 1 new_progress = (i * 50) // count if new_progress != progress: progress = new_progress self.signal_progress.emit(new_progress) self.signal_progress.emit(50) i = 0 # Test that there are no disabled constraints that are still needed for constraint in constraints: if constraint.enabled: i = i + 1 continue # test if constraint is redundant va_a = manager.to_virtual(constraint.romA, constraint.addressA) va_b = manager.to_virtual(constraint.romB, constraint.addressB) if va_a != va_b: print(f'Need to reenable {constraint}') constraint.enabled = True manager.add_constraint(constraint) try: manager.rebuild_relations() except InvalidConstraintError as e: print(e) print(constraint) self.signal_fail.emit() return i = i + 1 new_progress = (i * 50) // count + 50 if new_progress != progress: progress = new_progress self.signal_progress.emit(new_progress) constraint_database._write_constraints( ) # TODO add a public method to update changed constraints in the database? constraint_database.constraints_changed.emit() self.signal_done.emit()