Exemple #1
0
 def update_hex_viewer_actions(self):
     self.ui.actionUSA.setDisabled(get_rom(RomVariant.USA) is None)
     self.ui.actionDEMO.setDisabled(get_rom(RomVariant.DEMO) is None)
     self.ui.actionJP.setDisabled(get_rom(RomVariant.JP) is None)
     self.ui.actionEU.setDisabled(get_rom(RomVariant.EU) is None)
     self.ui.actionDEMO_JP.setDisabled(get_rom(RomVariant.DEMO_JP) is None)
     self.ui.actionCUSTOM.setDisabled(get_rom(RomVariant.CUSTOM) is None)
     self.ui.actionCUSTOM_EU.setDisabled(
         get_rom(RomVariant.CUSTOM_EU) is None)
     self.ui.actionCUSTOM_JP.setDisabled(
         get_rom(RomVariant.CUSTOM_JP) is None)
     self.ui.actionCUSTOM_DEMO_USA.setDisabled(
         get_rom(RomVariant.CUSTOM_DEMO_USA) is None)
     self.ui.actionCUSTOM_DEMO_JP.setDisabled(
         get_rom(RomVariant.CUSTOM_DEMO_JP) is None)
    def add_hex_editor_dock(self, rom_variant: RomVariant,
                            object_name: str) -> HexViewerController:
        '''
        Internally used to add a new or existing hex editor
        '''

        rom = get_rom(rom_variant)
        if rom is None:
            QMessageBox.critical(self.parent, 'Load ROM',
                                 f'Unable to load rom {rom_variant}')
            return None

        dockWidget = HexViewerDock(self.parent, 'Hex Viewer ' + rom_variant)
        # Not only hide docks on close TODO still remove them here
        dockWidget.setAttribute(Qt.WA_DeleteOnClose)
        dockWidget.setObjectName(object_name)
        self.parent.addDockWidget(Qt.DockWidgetArea.TopDockWidgetArea,
                                  dockWidget)

        controller = HexViewerController(dockWidget, rom_variant, rom)
        self.hex_viewer_manager.register_controller(controller)

        dock = Dock(object_name, dockWidget, rom_variant, controller)
        self.docks[object_name] = dock
        dockWidget.destroyed.connect(lambda: self.remove_dock(object_name))
        return controller
    def invalidate(self) -> None:
        '''
        Invalidates all assumptions about the underlying data and requests a full repaint.
        Call this if the underlying rom changed.
        '''
        self.rom = get_rom(self.rom_variant)

        self.request_repaint()
Exemple #4
0
    def slot_extract_data(self, text: str) -> None:
        if self.symbols is None:
            # First need to load symbols
            self.symbols = get_symbol_database().get_symbols(RomVariant.CUSTOM)
            if self.symbols is None:
                self.server_worker.slot_extracted_data({
                    'status':
                    'error',
                    'text':
                    'No symbols for rom CUSTOM loaded'
                })
                return

        if self.data_extractor_plugin is None:
            self.data_extractor_plugin = get_plugin('data_extractor',
                                                    'DataExtractorPlugin')
            if self.data_extractor_plugin is None:
                self.server_worker.slot_extracted_data({
                    'status':
                    'error',
                    'text':
                    'Data Extractor plugin not loaded'
                })
                return

        if self.rom is None:
            self.rom = get_rom(RomVariant.CUSTOM)
            if self.rom is None:
                self.server_worker.slot_extracted_data({
                    'status':
                    'error',
                    'text':
                    'CUSTOM rom could not be loaded'
                })
                return

        try:
            result = self.data_extractor_plugin.instance.extract_data(
                text, self.symbols, self.rom)
            if result is not None:
                self.server_worker.slot_extracted_data({
                    'status': 'ok',
                    'text': result
                })
        except Exception as e:
            traceback.print_exc()
            self.server_worker.slot_extracted_data({
                'status': 'error',
                'text': str(e)
            })
Exemple #5
0
 def is_diffing(self, virtual_address: int) -> bool:
     # TODO cache this, optimize accesses of rom data
     data = None
     for variant in self.variants:
         local_address = self.constraint_manager.to_local(
             variant, virtual_address)
         if local_address == -1 or local_address > 0xffffff:
             # does count as a difference
             return True
         local_data = get_rom(variant).get_byte(local_address)
         if data is None:
             data = local_data
             continue
         if data != local_data:
             return True
     return False
Exemple #6
0
    def slot_script_addr(self, addr: int) -> None:
        print('ADDR: ', addr)

        if addr == 0:
            self.ui.labelCode.setText('No script executed in current context.')
            return
        rom = get_rom(RomVariant.CUSTOM)

        # receive the current instruction pointer
        #instruction_pointer = 0x8009b70
        #instruction_pointer = 0x8009d42
        instruction_pointer = addr
        symbols = get_symbol_database().get_symbols(
            RomVariant.CUSTOM)  # Symbols for our custom USA rom
        symbol = symbols.get_symbol_at(instruction_pointer - ROM_OFFSET)
        script_name = symbol.name
        script_offset = instruction_pointer - ROM_OFFSET - symbol.address

        # Find file containing the script
        # TODO or statically find all script files?
        script_file = None
        for root, dirs, files in os.walk(
                os.path.join(settings.get_repo_location(), 'data', 'scripts')):
            if script_name + '.inc' in files:
                script_file = os.path.join(root, script_name + '.inc')
                break
            # TODO search the file contents for the script
            for file in files:
                path = os.path.join(root, file)
                with open(path, 'r') as f:
                    if 'SCRIPT_START ' + script_name in f.read():
                        script_file = path

        if script_file is None:
            self.ui.labelCode.setText(
                f'ERROR: Count not find script file containing {script_name}')
            return

        self.ui.labelScriptName.setText(script_file)

        script_lines = []
        with open(script_file, 'r') as file:
            script_lines = file.read().split('\n')

        # print(script_lines)
        # TODO for testing ifdefs: script_0800B200
        # print('test')
        # print(symbol)
        # print(script_offset)

        # TODO only disassemble the number of bytes, the actual instructions are not interesting as they are read from the source file.
        (_, instructions) = disassemble_script(
            rom.get_bytes(symbol.address, symbol.address + symbol.length),
            symbol.address)

        output = ''
        current_instruction = 0
        in_correct_script = False

        ifdef_stack = [True]

        for line in script_lines:
            stripped = line.strip()
            if stripped.startswith('SCRIPT_START'):
                in_correct_script = stripped == 'SCRIPT_START ' + script_name
                output += f'{line}\n'
                continue
            if not in_correct_script or stripped.startswith(
                    '@') or stripped.endswith(':'):
                output += f'{line}\n'
                continue

            if '.ifdef' in stripped:
                if not ifdef_stack[-1]:
                    ifdef_stack.append(False)
                    output += f'{line}\n'
                    continue
                # TODO check variant
                is_usa = stripped.split(' ')[1] == 'USA'
                ifdef_stack.append(is_usa)
                output += f'{line}\n'
                continue
            if '.ifndef' in stripped:
                if not ifdef_stack[-1]:
                    ifdef_stack.append(False)
                    output += f'{line}\n'
                    continue
                is_usa = stripped.split(' ')[1] == 'USA'
                ifdef_stack.append(not is_usa)
                output += f'{line}\n'
                continue
            if '.else' in stripped:
                if ifdef_stack[-2]:
                    # If the outermost ifdef is not true, this else does not change the validiness of this ifdef
                    ifdef_stack[-1] = not ifdef_stack[-1]
                output += f'{line}\n'
                continue
            if '.endif' in stripped:
                ifdef_stack.pop()
                output += f'{line}\n'
                continue

            if not ifdef_stack[-1]:
                # Not defined for this variant
                output += f'{line}\n'
                continue

            if current_instruction >= len(instructions):
                # TODO maybe even not print additional lines?
                output += f'{line}\n'
                continue
            addr = instructions[current_instruction].addr
            prefix = ''
            if addr == script_offset:
                prefix = '>'
            output += f'{addr:03d}| {prefix}{line}\t\n'
            current_instruction += 1
            if stripped.startswith('SCRIPT_END'):
                break
        self.ui.labelCode.setText(output)
Exemple #7
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
Exemple #8
0
    def process(self) -> None:
        try:
            print('start')
            # Load shifted rom
            rom_original = get_rom(RomVariant.USA)
            print('Load shifted')
            rom_path = path.join(settings.get_repo_location(), 'tmc.gba')
            print(rom_path)
            if not path.isfile(rom_path):
                self.signal_fail.emit(f'Shifted rom expected at {rom_path}')
                return
            rom_shifted = Rom(rom_path)
            print('Shifted rom loaded')

            pointerlist = get_pointer_database().get_pointers(RomVariant.USA)

            END_OF_USED_DATA = 0xde7da4

            errors = []
            locations = []

            shift_location = 0x108
            shift_length = 0x10000

            take_long_time = True

            progress = 0
            for i in range(END_OF_USED_DATA):
                orig = rom_original.get_byte(i)
                shifted_i = i
                if i >= shift_location:
                    #print('SHIFT')
                    shifted_i += shift_length
                #print(i, shifted_i)
                shifted = rom_shifted.get_byte(shifted_i)

                if orig != shifted:
                    pointers = pointerlist.get_pointers_at(i)

                    # Test if pointer
                    if len(pointers) > 0:
                        assert shifted == orig + 1
                        # TODO parse the full pointer
                        continue

                    print(f'{hex(i-2)}\t{orig}\t{shifted}')
                    errors.append((i, orig, shifted))
                    locations.append(i)
                    #self.signal_fail.emit(f'Failed at {hex(i)}: {orig} {shifted}')
                    #break
                else:

                    if take_long_time:
                        if rom_original.get_byte(i + 1) != 0x8:
                            # Certainly not a pointer here
                            continue
                        pointers = pointerlist.get_pointers_at(i)
                        if len(pointers) > 0:
                            if pointers[0].address == i - 2:
                                errors.append((i, orig, shifted))
                                locations.append(i)
                                print(f'missing shift at {hex(i-2)}')

                    #if len(pointers) > 0:
                    # TODO test that pointer was shifted
                    #pass

                new_progress = i * 100 // END_OF_USED_DATA
                if new_progress != progress:
                    progress = new_progress
                    self.signal_progress.emit(new_progress)

            if len(errors) == 0:
                self.signal_done.emit()
            else:
                self.signal_locations.emit(locations)
                self.signal_fail.emit(f'{len(errors)} errors found.')
        except Exception as e:
            print(e)
            self.signal_fail.emit('Caught exception')