예제 #1
0
    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)
예제 #2
0
    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)
예제 #3
0
    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)
예제 #4
0
    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}')
예제 #5
0
 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()
예제 #6
0
    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')
예제 #7
0
 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)
예제 #8
0
    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
예제 #9
0
    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()