Beispiel #1
0
def load_m64(filename: str) -> Tuple[TasMetadata, Dict[Variable, object]]:
    with open(filename, 'rb') as f:
        f.seek(0x10)
        rerecords = struct.unpack('>H', f.read(2))[0]

        f.seek(0xE4)
        crc = f.read(4)
        game_version = dict_inverse(CRC_CODES).get(crc) or 'us'
        # TODO: Fall back to country code?

        f.seek(0x222)
        authors = f.read(222).partition(b'\x00')[0].decode('utf-8')

        f.seek(0x300)
        description = f.read(256).partition(b'\x00')[0].decode('utf-8')

        metadata = TasMetadata(
            game_version,
            os.path.split(filename)[1],
            authors,
            description,
            rerecords,
        )
        edits: Dict[Variable, object] = {}

        f.seek(0x400)
        frame = 0
        while True:
            try:
                buttons = struct.unpack('>H', f.read(2))[0]
                stick_x = struct.unpack('=b', f.read(1))[0]
                stick_y = struct.unpack('=b', f.read(1))[0]
            except struct.error:
                break

            for variable, flag in INPUT_BUTTON_FLAGS.items():
                if buttons & flag:
                    edits[variable.with_frame(frame)] = True
            if stick_x != 0:
                edits[Variable('input-stick-x').with_frame(frame)] = stick_x
            if stick_y != 0:
                edits[Variable('input-stick-y').with_frame(frame)] = stick_y

            prev_buttons = buttons
            prev_stick_x = stick_x
            prev_stick_y = stick_y
            frame += 1

        return (metadata, edits)
Beispiel #2
0
def get_mario_pos(model: Model) -> Vec3f:
    return (
        dcast(
            float,
            model.get(
                Variable('mario-pos-x').with_frame(model.selected_frame))),
        dcast(
            float,
            model.get(
                Variable('mario-pos-y').with_frame(model.selected_frame))),
        dcast(
            float,
            model.get(
                Variable('mario-pos-z').with_frame(model.selected_frame))),
    )
 def render_button(button: str) -> None:
     self.render_variable(
         tab,
         Variable('input-button-' + button).with_frame(
             self.model.selected_frame),
         10,
         25,
     )
Beispiel #4
0
    def render_headers(self) -> None:
        header_labels = [
            self.displayer.column_header(column.variable)
            for column in self.columns
        ]
        header_lines = max((len(label.split('\n')) for label in header_labels),
                           default=1)

        ig.columns(len(self.columns) + 1)
        if len(self.columns) > 0:
            ig.set_column_width(-1, self.frame_column_width)
        ig.text('')
        ig.next_column()

        for index, column in enumerate(self.columns):
            initial_cursor_pos = ig.get_cursor_pos()
            ig.selectable(
                '##fs-col-' + str(id(self)) + '-' + str(id(column)),
                height=header_lines * ig.get_text_line_height(),
            )

            # TODO: Width adjusting
            ig.set_column_width(-1, column.width)

            if ig.begin_drag_drop_source():
                ig.text(header_labels[index])
                ig.set_drag_drop_payload('fs-col', str(index).encode('utf-8'))
                ig.end_drag_drop_source()

            if ig.begin_drag_drop_target():
                payload = ig.accept_drag_drop_payload('fs-col')
                if payload is not None:
                    source = int(payload.decode('utf-8'))
                    self._move_column(source, index)

                payload = ig.accept_drag_drop_payload('ve-var')
                if payload is not None:
                    self._insert_variable(index, Variable.from_bytes(payload))

                ig.end_drag_drop_target()

            if ig.is_item_hovered() and ig.is_mouse_clicked(2):
                self._remove_column(index)

            if ig.begin_popup_context_item('##fs-colctx-' + str(id(self)) +
                                           '-' + str(id(column))):
                if ig.selectable('Close')[0]:
                    self._remove_column(index)
                ig.end_popup_context_item()

            ig.set_cursor_pos(initial_cursor_pos)
            ig.text(header_labels[index])

            ig.next_column()
        ig.separator()
        ig.columns(1)
Beispiel #5
0
def save_m64(filename: str, metadata: TasMetadata, pipeline: Pipeline,
             length: int) -> None:
    with open(filename, 'wb') as f:
        # TODO: Remove blank frames at end
        f.write(b'\x4d\x36\x34\x1a')
        f.write(b'\x03\x00\x00\x00')
        f.write(b'\x00\x00\x00\x00')  # movie uid
        f.write(b'\xff\xff\xff\xff')

        f.write(struct.pack('<I', (metadata.rerecords or 0) & 0xFFFFFFFF))
        f.write(b'\x3c\x01\x00\x00')
        f.write(struct.pack('<I', length))
        f.write(b'\x02\x00\x00\x00')  # power-on

        f.write(b'\x01\x00\x00\x00')
        f.write(bytes(160))
        f.write(bytes_to_buffer(b'SUPER MARIO 64', 32))
        f.write(CRC_CODES[metadata.game_version])
        f.write(COUNTRY_CODES[metadata.game_version])
        f.write(bytes(57))

        f.write(bytes(64))
        f.write(bytes(64))
        f.write(bytes(64))
        f.write(bytes(64))

        f.write(bytes_to_buffer(metadata.authors.encode('utf-8'), 222))
        f.write(bytes_to_buffer(metadata.description.encode('utf-8'), 256))

        for frame in range(length):
            buttons = dcast(
                int,
                pipeline.read(Variable('input-buttons').with_frame(frame)))
            stick_x = dcast(
                int,
                pipeline.read(Variable('input-stick-x').with_frame(frame)))
            stick_y = dcast(
                int,
                pipeline.read(Variable('input-stick-y').with_frame(frame)))

            f.write(struct.pack(b'>H', buttons & 0xFFFF))
            f.write(struct.pack(b'=B', stick_x & 0xFF))
            f.write(struct.pack(b'=B', stick_y & 0xFF))
Beispiel #6
0
def render_labeled_variable(
    id: str,
    label: str,
    variable: Variable,
    value: T,
    formatter: VariableFormatter,
    is_edited: bool,
    label_width=80,
    value_width=80,
) -> Tuple[Maybe[T], bool]:
    ig.push_id(id)

    ig.selectable(label + '##label', width=label_width)

    if ig.begin_drag_drop_source():
        ig.text(label)
        ig.set_drag_drop_payload('ve-var', variable.to_bytes())
        ig.end_drag_drop_source()

    ig.same_line()

    cell_width = value_width
    cell_height = ig.get_text_line_height(
    ) + 2 * ig.get_style().frame_padding[1]

    cell_cursor_pos = ig.get_cursor_pos()
    cell_cursor_pos = (
        cell_cursor_pos[0] + ig.get_window_position()[0] - ig.get_scroll_x(),
        cell_cursor_pos[1] + ig.get_window_position()[1] - ig.get_scroll_y(),
    )

    changed_data, _, _ = render_variable_value(
        'value',
        value,
        formatter,
        (cell_width, cell_height),
    )

    clear_edit = is_edited and ig.is_item_hovered() and ig.is_mouse_down(2)

    if is_edited:
        dl = ig.get_window_draw_list()
        spacing = ig.get_style().item_spacing
        spacing = (spacing[0] / 2, spacing[1] / 2)
        dl.add_rect(
            cell_cursor_pos[0] - spacing[0],
            cell_cursor_pos[1] - spacing[1],
            cell_cursor_pos[0] + cell_width + spacing[0] - 1,
            cell_cursor_pos[1] + cell_height + spacing[1] - 1,
            ig.get_color_u32_rgba(0.8, 0.6, 0, 1),
        )

    ig.pop_id()
    return changed_data, clear_edit
Beispiel #7
0
    def _insert_variable(self, index: int, variable: Variable) -> None:
        variable = variable.without_frame()

        if self.columns != self.next_columns:
            log.error('Multiple frame sheet column mods on same frame')
            return

        object_slot = variable.object
        column = FrameSheetColumn(variable)
        if column not in self.columns:
            self.next_columns.insert(index, column)
    def render_stick_control(self, id: str, tab: TabId) -> None:
        stick_x_var = Variable('input-stick-x').with_frame(
            self.model.selected_frame)
        stick_y_var = Variable('input-stick-y').with_frame(
            self.model.selected_frame)

        self.render_variable(tab, stick_x_var, 60, 50)
        self.render_variable(tab, stick_y_var, 60, 50)

        stick_x = dcast(int, self.model.get(stick_x_var))
        stick_y = dcast(int, self.model.get(stick_y_var))

        n_x = 2 * ((stick_x + 128) / 255) - 1
        n_y = 2 * ((stick_y + 128) / 255) - 1
        new_n = ui.render_joystick_control(id, n_x, n_y)

        if new_n is not None:
            new_stick_x = int(0.5 * (new_n[0] + 1) * 255 - 128)
            new_stick_y = int(0.5 * (new_n[1] + 1) * 255 - 128)

            self.model.set(stick_x_var, new_stick_x)
            self.model.set(stick_y_var, new_stick_y)
    def render_intended_stick_control(self, id: str) -> None:
        up_options = ['3d view', 'mario yaw', 'stick y', 'world x']
        up_option = use_state('up-option', 0)

        ig.text('up =')
        ig.same_line()
        ig.push_item_width(100)
        _, up_option.value = ig.combo('##up-option', up_option.value,
                                      up_options)
        ig.pop_item_width()
        ig.dummy(1, 10)

        stick_x_var = Variable('input-stick-x').with_frame(
            self.model.selected_frame)
        stick_y_var = Variable('input-stick-y').with_frame(
            self.model.selected_frame)

        face_yaw = dcast(
            int,
            self.model.get(
                Variable('mario-face-yaw').with_frame(
                    self.model.selected_frame)))
        camera_yaw = dcast(
            int,
            self.model.get(
                Variable('camera-yaw').with_frame(self.model.selected_frame))
            or 0)
        squish_timer = dcast(
            int,
            self.model.get(self.model.selected_frame,
                           'gMarioState->squishTimer'))
        active_face_yaw = face_yaw

        events = self.model.pipeline.frame_log(self.model.selected_frame + 1)

        active_face_yaw_action = None
        for event in events:
            if event['type'] == 'FLT_EXECUTE_ACTION':
                action_name = self.model.action_names[event['action']]
                active_face_yaw = event['faceAngle'][1]
                active_face_yaw_action = action_name
                if action_name == 'idle':
                    break

        up_angle = {
            'mario yaw': active_face_yaw,
            'stick y': camera_yaw + 0x8000,
            'world x': 0x4000,
            '3d view': self.model.rotational_camera_yaw,
        }[up_options[up_option.value]]
        self.model.input_up_yaw = up_angle

        raw_stick_x = dcast(int, self.model.get(stick_x_var))
        raw_stick_y = dcast(int, self.model.get(stick_y_var))

        adjusted = stick_raw_to_adjusted(raw_stick_x, raw_stick_y)
        intended = stick_adjusted_to_intended(
            adjusted,
            face_yaw,
            camera_yaw,
            squish_timer != 0,
        )

        def render_value(label: str, value: object,
                         formatter: VariableFormatter) -> Optional[Any]:
            label_width = 60
            value_size = (
                60 if label == 'dyaw' else 80,
                ig.get_text_line_height() +
                2 * ig.get_style().frame_padding[1],
            )
            ig.push_item_width(label_width)
            ig.selectable(label, width=label_width)
            ig.pop_item_width()
            ig.same_line()
            new_value, _, _ = ui.render_variable_value('value-' + label, value,
                                                       formatter, value_size)
            return None if new_value is None else new_value.value

        target_yaw: Optional[int] = None
        target_dyaw: Optional[int] = None
        target_mag: Optional[float] = None

        target_mag = render_value('int mag', intended.mag, FloatFormatter())
        target_yaw = render_value('int yaw', intended.yaw,
                                  DecimalIntFormatter())
        dyaw = intended.yaw - active_face_yaw
        target_dyaw = render_value('dyaw', dyaw, DecimalIntFormatter())

        ig.same_line()
        if ig.button('?'):
            ig.open_popup('active-yaw-expl')
        if ig.begin_popup('active-yaw-expl'):
            ig.text(f'{intended.yaw} - {active_face_yaw} = {dyaw}')
            ig.text(f'intended yaw = {intended.yaw}')
            if active_face_yaw == face_yaw:
                ig.text(f'face yaw = {face_yaw}')
            if active_face_yaw != face_yaw:
                ig.text(
                    f'face yaw = {active_face_yaw} at start of {active_face_yaw_action} action'
                )
                ig.text(f'(face yaw = {face_yaw} at start of frame)')
            ig.end_popup()

        if dyaw not in range(0, 16):
            if ig.button('dyaw = 0'):
                target_dyaw = 0

        if target_yaw is not None or target_dyaw is not None or target_mag is not None:
            relative_to = 0 if target_yaw is not None else active_face_yaw
            if target_dyaw is not None:
                target_yaw = active_face_yaw + target_dyaw
            if target_yaw is None:
                target_yaw = intended.yaw
            if target_mag is None:
                target_mag = intended.mag

            new_raw_stick_x, new_raw_stick_y = intended_to_raw(
                face_yaw, camera_yaw, squish_timer, target_yaw, target_mag,
                relative_to)

            self.model.set(stick_x_var, new_raw_stick_x)
            self.model.set(stick_y_var, new_raw_stick_y)

        n_a = intended.yaw - up_angle
        n_x = intended.mag / 32 * math.sin(-n_a * math.pi / 0x8000)
        n_y = intended.mag / 32 * math.cos(n_a * math.pi / 0x8000)

        ig.set_cursor_pos((ig.get_cursor_pos().x + 155, 0))
        new_n = ui.render_joystick_control(id, n_x, n_y, 'circle')

        if new_n is not None:
            new_n_a = int(math.atan2(-new_n[0], new_n[1]) * 0x8000 / math.pi)
            new_intended_yaw = up_angle + new_n_a
            new_intended_mag = 32 * math.sqrt(new_n[0]**2 + new_n[1]**2)

            new_raw_stick_x, new_raw_stick_y = intended_to_raw(
                face_yaw,
                camera_yaw,
                squish_timer,
                new_intended_yaw,
                new_intended_mag,
                relative_to=0)

            self.model.set(stick_x_var, new_raw_stick_x)
            self.model.set(stick_y_var, new_raw_stick_y)
Beispiel #10
0
from wafel_core import Variable

INPUT_BUTTON_FLAGS = {
    Variable('input-button-a'): 0x8000,
    Variable('input-button-b'): 0x4000,
    Variable('input-button-z'): 0x2000,
    Variable('input-button-s'): 0x1000,
    Variable('input-button-l'): 0x0020,
    Variable('input-button-r'): 0x0010,
    Variable('input-button-cu'): 0x0008,
    Variable('input-button-cl'): 0x0002,
    Variable('input-button-cr'): 0x0001,
    Variable('input-button-cd'): 0x0004,
    Variable('input-button-du'): 0x0800,
    Variable('input-button-dl'): 0x0200,
    Variable('input-button-dr'): 0x0100,
    Variable('input-button-dd'): 0x0400,
}