sftool_lib/common/
read_flash.rs

1use crate::SifliToolTrait;
2use crate::common::ram_command::{Command, RamCommand};
3use crate::utils::Utils;
4use std::fs::File;
5use std::io::{Read, Seek, Write};
6use tempfile::tempfile;
7
8/// 通用的Flash读取文件结构
9#[derive(Debug)]
10pub struct ReadFlashFile {
11    pub file_path: String,
12    pub address: u32,
13    pub size: u32,
14}
15
16/// 通用的Flash读取操作实现
17pub struct FlashReader;
18
19impl FlashReader {
20    /// 解析读取文件信息 (filename@address:size格式)
21    pub fn parse_file_info(file_spec: &str) -> Result<ReadFlashFile, std::io::Error> {
22        let Some((file_path, addr_size)) = file_spec.split_once('@') else {
23            return Err(std::io::Error::new(
24                std::io::ErrorKind::InvalidInput,
25                format!(
26                    "Invalid format: {}. Expected: filename@address:size",
27                    file_spec
28                ),
29            ));
30        };
31        let Some((addr, size)) = addr_size.split_once(':') else {
32            return Err(std::io::Error::new(
33                std::io::ErrorKind::InvalidInput,
34                format!(
35                    "Invalid format: {}. Expected: filename@address:size",
36                    file_spec
37                ),
38            ));
39        };
40
41        let address = Utils::str_to_u32(addr)
42            .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
43        let size = Utils::str_to_u32(size)
44            .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
45
46        Ok(ReadFlashFile {
47            file_path: file_path.to_string(),
48            address,
49            size,
50        })
51    }
52
53    /// 从Flash读取数据的通用实现
54    pub fn read_flash_data<T>(
55        tool: &mut T,
56        address: u32,
57        size: u32,
58        output_path: &str,
59    ) -> Result<(), std::io::Error>
60    where
61        T: SifliToolTrait + RamCommand,
62    {
63        let progress = tool.progress();
64        let progress_bar =
65            progress.create_bar(size as u64, format!("Reading from 0x{:08X}...", address));
66
67        // 创建临时文件
68        let mut temp_file = tempfile()?;
69        let packet_size = 128 * 1024; // 128KB chunks
70        let mut remaining = size;
71        let mut current_address = address;
72
73        while remaining > 0 {
74            let chunk_size = std::cmp::min(remaining, packet_size);
75
76            // 发送读取命令
77            let _ = tool.command(Command::Read {
78                address: current_address,
79                len: chunk_size,
80            });
81
82            // 读取数据
83            let mut buffer = vec![0u8; chunk_size as usize];
84            let mut total_read = 0;
85            let start_time = std::time::SystemTime::now();
86
87            while total_read < chunk_size {
88                let remaining_in_chunk = chunk_size - total_read;
89                let mut chunk_buffer = vec![0u8; remaining_in_chunk as usize];
90
91                match tool.port().read_exact(&mut chunk_buffer) {
92                    Ok(_) => {
93                        buffer[total_read as usize..(total_read + remaining_in_chunk) as usize]
94                            .copy_from_slice(&chunk_buffer);
95                        total_read += remaining_in_chunk;
96                    }
97                    Err(_) => {
98                        // 超时检查
99                        if start_time.elapsed().unwrap().as_millis() > 10000 {
100                            return Err(std::io::Error::new(
101                                std::io::ErrorKind::TimedOut,
102                                "Read timeout",
103                            ));
104                        }
105                        continue;
106                    }
107                }
108            }
109
110            // 写入临时文件
111            temp_file.write_all(&buffer)?;
112
113            remaining -= chunk_size;
114            current_address += chunk_size;
115
116            progress_bar.inc(chunk_size as u64);
117        }
118
119        progress_bar.finish_with_message("Read complete");
120
121        // 将临时文件内容写入目标文件
122        temp_file.seek(std::io::SeekFrom::Start(0))?;
123        let mut output_file = File::create(output_path)?;
124        std::io::copy(&mut temp_file, &mut output_file)?;
125
126        Ok(())
127    }
128}