image_rider/disk_format/stx/
disk.rs1use log::{debug, error, info};
2
3use std::fs::File;
4use std::io::Write;
5use std::path::PathBuf;
6
7use nom::bytes::complete::{tag, take};
8use nom::number::complete::{le_u16, le_u8};
9use nom::IResult;
10
11use std::fmt::{Display, Formatter, Result};
12
13use crate::config::Config;
14use crate::disk_format::image::DiskImageSaver;
15use crate::disk_format::stx::track::{stx_tracks_parser, STXTrack};
16use crate::disk_format::stx::SanityCheck;
17
18#[derive(Debug)]
20pub struct STXDisk<'a> {
21 pub stx_disk_header: STXDiskHeader<'a>,
23
24 pub stx_tracks: Vec<STXTrack<'a>>,
26}
27
28impl Display for STXDisk<'_> {
30 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
31 write!(f, "header: {}", self.stx_disk_header)
32 }
33}
34
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
49pub struct STXDiskGuess<'a> {
50 pub data: &'a [u8],
52}
53
54impl DiskImageSaver for STXDisk<'_> {
55 fn save_disk_image(
59 &self,
60 _config: &Config,
61 _selected_filename: Option<&str>,
62 filename: &str,
63 ) -> std::result::Result<(), crate::error::Error> {
64 let disk_image_data: Vec<u8> = self
66 .stx_tracks
67 .iter()
68 .filter(|s| s.sector_data.is_some())
69 .flat_map(|s| s.sector_data.as_ref().unwrap().iter())
70 .flat_map(|bytes| (*bytes).iter())
71 .copied()
72 .collect();
73 info!("Found image data, writing data");
74 let filename = PathBuf::from(filename);
75 let file_result = File::create(filename);
76 match file_result {
77 Ok(mut file) => {
78 let _res = file.write_all(&disk_image_data);
79 }
80 Err(e) => error!("Error opening file: {}", e),
81 }
82 Ok(())
83 }
84}
85
86#[derive(Debug)]
89pub struct STXDiskHeader<'a> {
90 pub disk_id: &'a [u8],
92 pub version: u16,
95 pub tool_used: u16,
97 pub reserved_area_1: &'a [u8],
99 pub track_count: u8,
101 pub new_format: u8,
103 pub reserved_area_2: &'a [u8],
105}
106
107impl SanityCheck for STXDiskHeader<'_> {
112 fn check(&self) -> bool {
113 if self.track_count > 164 {
114 debug!("Disk track count is greater than 164: {}", self.track_count);
115 false
116 } else {
117 true
118 }
119 }
120}
121
122impl Display for STXDiskHeader<'_> {
124 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
125 write!(
126 f,
127 "version: {}, tool_used: {}, reserved_area_1: {:?}, track_count: {}, ",
128 self.version, self.tool_used, self.reserved_area_1, self.track_count
129 )?;
130 write!(
131 f,
132 "new_format: {}, reserved_area_2: {:?}",
133 self.new_format, self.reserved_area_2
134 )
135 }
136}
137
138pub fn stx_disk_parser(i: &[u8]) -> IResult<&[u8], STXDisk<'_>> {
141 let (i, stx_disk_header) = stx_disk_header_parser(i)?;
142
143 if !stx_disk_header.check() {
144 error!("Invalid data");
145 panic!("Invalid data");
146 }
147
148 info!("Disk header: {}", stx_disk_header);
149
150 let (i, tracks) = stx_tracks_parser(stx_disk_header.track_count as usize)(i)?;
151
152 let stx_disk = STXDisk {
153 stx_disk_header,
154 stx_tracks: tracks,
155 };
156
157 Ok((i, stx_disk))
158}
159
160pub fn stx_disk_header_parser(i: &[u8]) -> IResult<&[u8], STXDiskHeader<'_>> {
163 let (i, disk_id) = tag("RSY\0")(i)?;
166
167 let (i, version) = le_u16(i)?;
169
170 let (i, tool_used) = le_u16(i)?;
172
173 let (i, reserved_area_1) = take(2_usize)(i)?;
175
176 let (i, track_count) = le_u8(i)?;
177 let (i, new_format) = le_u8(i)?;
178
179 let (i, reserved_area_2) = take(4_usize)(i)?;
181
182 let stx_disk_header = STXDiskHeader {
183 disk_id,
184 version,
185 tool_used,
186 reserved_area_1,
187 track_count,
188 new_format,
189 reserved_area_2,
190 };
191
192 Ok((i, stx_disk_header))
193}
194
195#[cfg(test)]
196mod tests {
197 use super::stx_disk_header_parser;
198
199 #[test]
201 fn stx_disk_valid_header_parser_works() {
202 let stx_disk_header: [u8; 16] = [
205 0x52, 0x53, 0x59, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x52, 0x02, 0x00, 0x00,
206 0x00, 0x00,
207 ];
208
209 let stx_disk_header_parser_result = stx_disk_header_parser(&stx_disk_header);
210
211 match stx_disk_header_parser_result {
212 Ok((_, res)) => {
213 assert_eq!(res.disk_id, [0x52, 0x53, 0x59, 0x00]);
214 assert_eq!(res.version, 0x03);
215 assert_eq!(res.tool_used, 0x01);
216 assert_eq!(res.reserved_area_1, [0x00, 0x00]);
217 assert_eq!(res.track_count, 0x52);
218 assert_eq!(res.new_format, 0x02);
219 assert_eq!(res.reserved_area_2, [0x00, 0x00, 0x00, 0x00]);
220 }
221 Err(e) => panic!("Parsing failed on the STX disk header: {}", e),
222 }
223 }
224
225 #[test]
227 #[should_panic(
228 expected = "Parsing failed on the STX disk header: Parsing Error: Error { input: [82, 83, 96, 0, 3, 0, 1, 0, 0, 0, 82, 2, 0, 0, 0, 0], code: Tag }"
229 )]
230 fn stx_disk_invalid_header_parser_fails() {
231 let stx_disk_header: [u8; 16] = [
235 0x52, 0x53, 0x60, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x52, 0x02, 0x00, 0x00,
236 0x00, 0x00,
237 ];
238
239 let stx_disk_header_parser_result = stx_disk_header_parser(&stx_disk_header);
240
241 match stx_disk_header_parser_result {
242 Ok((_, _)) => panic!("Should fail parsing"),
243 Err(e) => panic!("Parsing failed on the STX disk header: {}", e),
244 }
245 }
246}