cargo_v5/commands/
dir.rs

1use chrono::{TimeZone, Utc};
2use std::io::{self, Write};
3use std::time::Duration;
4use vex_v5_serial::packets::factory::{
5    FactoryEnablePacket, FactoryEnablePayload, FactoryEnableReplyPacket,
6};
7
8use vex_v5_serial::packets::file::ExtensionType;
9use vex_v5_serial::timestamp::J2000_EPOCH;
10use vex_v5_serial::{
11    connection::{serial::SerialConnection, Connection},
12    packets::file::{
13        FileVendor, GetDirectoryEntryPacket, GetDirectoryEntryPayload,
14        GetDirectoryEntryReplyPacket, GetDirectoryFileCountPacket, GetDirectoryFileCountPayload,
15        GetDirectoryFileCountReplyPacket,
16    },
17};
18
19use humansize::{format_size, BINARY};
20use tabwriter::TabWriter;
21
22use crate::errors::CliError;
23
24fn vendor_prefix(vid: FileVendor) -> &'static str {
25    match vid {
26        FileVendor::User => "user/",
27        FileVendor::Sys => "sys_/",
28        FileVendor::Dev1 => "rmsh/",
29        FileVendor::Dev2 => "pros/",
30        FileVendor::Dev3 => "mwrk/",
31        FileVendor::Dev4 => "deva/",
32        FileVendor::Dev5 => "devb/",
33        FileVendor::Dev6 => "devc/",
34        FileVendor::VexVm => "vxvm/",
35        FileVendor::Vex => "vex_/",
36        FileVendor::Undefined => "test/",
37    }
38}
39
40pub async fn dir(connection: &mut SerialConnection) -> Result<(), CliError> {
41    let mut tw = TabWriter::new(io::stdout());
42
43    const USEFUL_VIDS: [FileVendor; 11] = [
44        FileVendor::User,
45        FileVendor::Sys,
46        FileVendor::Dev1,
47        FileVendor::Dev2,
48        FileVendor::Dev3,
49        FileVendor::Dev4,
50        FileVendor::Dev5,
51        FileVendor::Dev6,
52        FileVendor::VexVm,
53        FileVendor::Vex,
54        FileVendor::Undefined,
55    ];
56
57    connection
58        .packet_handshake::<FactoryEnableReplyPacket>(
59            Duration::from_millis(500),
60            1,
61            FactoryEnablePacket::new(FactoryEnablePayload::new()),
62        )
63        .await
64        .unwrap();
65
66    write!(
67        &mut tw,
68        "\x1B[1mName\tSize\tLoad Address\tVendor\tType\tTimestamp\tVersion\tCRC32\n\x1B[0m"
69    )
70    .unwrap();
71    for vid in USEFUL_VIDS {
72        let file_count = connection
73            .packet_handshake::<GetDirectoryFileCountReplyPacket>(
74                Duration::from_millis(500),
75                1,
76                GetDirectoryFileCountPacket::new(GetDirectoryFileCountPayload {
77                    vendor: vid,
78                    option: 0,
79                }),
80            )
81            .await?;
82
83        for n in 0..file_count.payload {
84            if let Some(entry) = connection
85                .packet_handshake::<GetDirectoryEntryReplyPacket>(
86                    Duration::from_millis(500),
87                    1,
88                    GetDirectoryEntryPacket::new(GetDirectoryEntryPayload {
89                        file_index: n as u8,
90                        unknown: 0,
91                    }),
92                )
93                .await?
94                .payload
95            {
96                writeln!(
97                    &mut tw,
98                    "{}{}\t{}\t{}\t{:?}\t{}\t{}\t{}\t{}",
99                    vendor_prefix(vid),
100                    entry.file_name,
101                    format_size(entry.size, BINARY),
102                    if entry.load_address == u32::MAX {
103                        "-".to_string()
104                    } else {
105                        format!("{:#x}", entry.load_address)
106                    },
107                    vid,
108                    entry
109                        .metadata
110                        .as_ref()
111                        .map(|m| match m.extension_type {
112                            ExtensionType::Binary => "binary",
113                            ExtensionType::EncryptedBinary => "encrypted",
114                            ExtensionType::Vm => "vm",
115                        })
116                        .unwrap_or("system"),
117                    entry
118                        .metadata
119                        .as_ref()
120                        .map(|m| Utc
121                            .timestamp_millis_opt((J2000_EPOCH as i64 + m.timestamp as i64) * 1000)
122                            .unwrap()
123                            .format("%Y-%m-%d %H:%M:%S")
124                            .to_string())
125                        .unwrap_or("-".to_string()),
126                    entry
127                        .metadata
128                        .as_ref()
129                        .map(|m| format!(
130                            "{}.{}.{}.b{}",
131                            m.version.major, m.version.minor, m.version.build, m.version.beta
132                        ))
133                        .unwrap_or("-".to_string()),
134                    if entry.crc == u32::MAX {
135                        "-".to_string()
136                    } else {
137                        format!("{:#x}", entry.crc)
138                    },
139                )
140                .unwrap();
141            }
142        }
143    }
144
145    tw.flush().unwrap();
146
147    Ok(())
148}