1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use ratatui::text::{Line, Span};

use crate::{
    app::{settings::color_settings::ColorSettings, App},
    headers::Header,
};

use super::{instruction_tag::InstructionTag, section_tag::SectionTag};

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AssemblyLine {
    Instruction(InstructionTag),
    SectionTag(SectionTag),
}

impl AssemblyLine {
    pub fn file_address(&self) -> u64 {
        match self {
            AssemblyLine::Instruction(instruction) => instruction.file_address,
            AssemblyLine::SectionTag(section) => section.file_address,
        }
    }

    pub fn virtual_address(&self) -> u64 {
        match self {
            AssemblyLine::Instruction(instruction) => instruction.instruction.ip(),
            AssemblyLine::SectionTag(section) => section.virtual_address,
        }
    }

    pub fn len(&self) -> usize {
        match self {
            AssemblyLine::Instruction(instruction) => instruction.instruction.len(),
            AssemblyLine::SectionTag(section) => section.size,
        }
    }

    pub fn is_empty(&self) -> bool {
        match self {
            AssemblyLine::Instruction(instruction) => instruction.instruction.is_empty(),
            AssemblyLine::SectionTag(section) => section.size == 0,
        }
    }

    pub fn to_line(
        &self,
        color_settings: &ColorSettings,
        current_byte_index: usize,
        header: &Header,
        address_min_width: usize,
    ) -> Line {
        match self {
            AssemblyLine::Instruction(instruction) => {
                let selected = current_byte_index >= instruction.file_address as usize
                    && current_byte_index
                        < instruction.file_address as usize + instruction.instruction.len();
                App::instruction_to_line(
                    color_settings,
                    instruction,
                    selected,
                    header,
                    address_min_width,
                )
            }
            AssemblyLine::SectionTag(section) => {
                let selected = current_byte_index >= section.file_address as usize
                    && current_byte_index < section.file_address as usize + section.size;
                let mut line = Line::default();
                let address_style = if selected {
                    color_settings.assembly_selected
                } else {
                    color_settings.assembly_address
                };
                line.spans.push(Span::styled(
                    format!("{:>address_min_width$X}", section.file_address),
                    address_style,
                ));
                line.spans.push(Span::raw(" "));
                line.spans.push(Span::styled(
                    format!("[{} ({}B)]", section.name, section.size),
                    color_settings.assembly_section,
                ));
                line.spans.push(Span::styled(
                    format!(" @{:X}", section.virtual_address),
                    color_settings.assembly_virtual_address,
                ));
                line
            }
        }
    }

    pub fn is_same_instruction(&self, other: &AssemblyLine) -> bool {
        match (self, other) {
            (
                AssemblyLine::Instruction(instruction),
                AssemblyLine::Instruction(other_instruction),
            ) => {
                instruction.instruction.bytes == other_instruction.instruction.bytes
                    && instruction.instruction.virtual_address
                        == other_instruction.instruction.virtual_address
            }
            _ => false,
        }
    }
}