1use crate::{Error, Result};
25use nom::{number::Endianness, IResult};
26use std::{
27 ffi::CString,
28 fmt::{self, Debug, Display, Formatter, Write},
29};
30use tracing::trace;
31
32const RCX_TAG: &str = "RCXI";
33const MAX_SECTIONS: usize = 10;
34const INDENT: &str = " ";
35const HEXDUMP_WRAP_BYTES: usize = 16;
36
37fn print_hex_with_marker_at(bin: &[u8], pos: usize) -> String {
38 let mut out = String::new();
39
40 write!(&mut out, " ").unwrap();
42 for n in 0..16 {
43 write!(&mut out, " {n:2x}").unwrap();
44 }
45 writeln!(&mut out).unwrap();
46
47 for (idx, chunk) in bin.chunks(HEXDUMP_WRAP_BYTES).enumerate() {
49 write!(&mut out, "0x{:02x}:", idx * HEXDUMP_WRAP_BYTES,).unwrap();
50 for byte in chunk {
51 write!(&mut out, " {byte:02x}").unwrap();
52 }
53 writeln!(&mut out).unwrap();
54 if (idx * HEXDUMP_WRAP_BYTES..(idx + 1) * HEXDUMP_WRAP_BYTES)
55 .contains(&pos)
56 {
57 out += " "; out.extend(std::iter::repeat(" ").take(pos % HEXDUMP_WRAP_BYTES));
60 writeln!(&mut out, "^<<").unwrap();
61 }
62 }
63 out
64}
65
66#[derive(Clone, Debug, PartialEq, Eq)]
67pub struct RcxBin {
68 pub signature: [u8; 4],
69 pub version: u16,
70 pub section_count: u16,
71 pub symbol_count: u16,
72 pub target_type: TargetType,
73 pub reserved: u8,
74 pub sections: Vec<Section>,
75 pub symbols: Vec<Symbol>,
76}
77
78impl RcxBin {
79 pub fn parse(bin: &[u8]) -> Result<Self> {
80 let (_i, bin) = parse(bin).map_err(|err| {
81 let input = match &err {
82 nom::Err::Error(err) => err.input,
83 nom::Err::Failure(err) => err.input,
84 nom::Err::Incomplete(needed) => {
85 return Error::Nom(format!(
86 "Incomplete input, needed {needed:?}",
87 ));
88 }
89 };
90 let pos = bin.len() - input.len();
91 println!("{}", print_hex_with_marker_at(bin, pos));
92 err.into()
93 })?;
94 bin.verify()?;
95 Ok(bin)
96 }
97
98 pub fn verify(&self) -> Result<()> {
99 fn repeated_idx(sections: &[Section]) -> bool {
100 let mut c = sections
101 .iter()
102 .map(|c| (c.ty as u8, c.number))
103 .collect::<Vec<_>>();
104 c.sort_unstable();
105 c.dedup();
106 c.len() != sections.len()
107 }
108
109 if self.section_count as usize != self.sections.len()
111 || self.sections.len() > MAX_SECTIONS
112 {
113 Err(Error::Parse("Invalid number of chunks"))
114 } else if repeated_idx(&self.sections) {
115 Err(Error::Parse("Nonunique chunk numbers"))
116 } else {
117 Ok(())
118 }
119 }
120}
121
122impl Display for RcxBin {
123 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
124 writeln!(
125 fmt,
126 "Signature: {}",
127 String::from_utf8_lossy(&self.signature),
128 )?;
129 writeln!(fmt, "Version: {:x}", self.version)?;
130 writeln!(
131 fmt,
132 "{} sections, {} symbols",
133 self.section_count, self.symbol_count,
134 )?;
135 writeln!(fmt, "Target: {}", self.target_type)?;
136 writeln!(fmt, "Sections:")?;
137 for section in &self.sections {
138 writeln!(fmt, "{section}")?;
139 }
140 writeln!(fmt, "Symbols:")?;
141 for symbol in &self.symbols {
142 writeln!(fmt, "{symbol}")?;
143 }
144 Ok(())
145 }
146}
147
148fn parse(bin: &[u8]) -> IResult<&[u8], RcxBin> {
149 trace!("Input len: {}", bin.len());
150 let read_u16 = nom::number::complete::u16(Endianness::Little);
151 let read_u8 = nom::number::complete::u8;
152
153 let (i, signature) = nom::bytes::complete::tag(RCX_TAG)(bin)?;
154 let (i, version) = read_u16(i)?;
155 let (i, section_count) = read_u16(i)?;
156 let (i, symbol_count) = read_u16(i)?;
157 let (i, target_type) = TargetType::parse(i)?;
158 let (i, reserved) = read_u8(i)?;
159
160 trace!("Parse {section_count} sections");
161 let (i, sections) =
162 nom::multi::count(parse_section, section_count.into())(i)?;
163 trace!("Parse {symbol_count} symbols");
164 let (i, symbols) = nom::multi::count(parse_symbol, symbol_count.into())(i)?;
165
166 IResult::Ok((
167 i,
168 RcxBin {
169 signature: signature.try_into().unwrap_or([0; 4]),
170 version,
171 section_count,
172 symbol_count,
173 target_type,
174 reserved,
175 sections,
176 symbols,
177 },
178 ))
179}
180
181#[derive(Copy, Clone, Debug, PartialEq, Eq)]
182#[repr(u8)]
183pub enum TargetType {
184 Rcx = 0,
186 CyberMaster,
188 Scout,
190 Rcx2,
192 Spybotics,
194 Swan,
196}
197
198impl TargetType {
199 pub fn parse(i: &[u8]) -> IResult<&[u8], Self> {
200 let (i, ty) = nom::number::complete::u8(i)?;
201 let ty = match ty {
202 0 => Self::Rcx,
203 1 => Self::CyberMaster,
204 2 => Self::Scout,
205 3 => Self::Rcx2,
206 4 => Self::Spybotics,
207 5 => Self::Swan,
208 _ => {
209 return Err(nom::Err::Failure(nom::error::Error {
210 input: i,
211 code: nom::error::ErrorKind::Verify,
212 }));
213 }
214 };
215 Ok((i, ty))
216 }
217}
218
219impl Display for TargetType {
220 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
221 Debug::fmt(self, fmt)
222 }
223}
224
225#[derive(Clone, Debug, PartialEq, Eq)]
226pub struct Section {
227 pub ty: SectionType,
228 pub number: u8,
229 pub length: u16,
230 pub data: Vec<u8>,
231}
232
233fn parse_section(i: &[u8]) -> IResult<&[u8], Section> {
234 let neg_offset = i.len();
235 let read_u16 = nom::number::complete::u16(Endianness::Little);
236 let read_u8 = nom::number::complete::u8;
237
238 let (i, ty) = SectionType::parse(i)?;
239 trace!("- section type {ty} - offset from back: {neg_offset}");
240 let (i, number) = read_u8(i)?;
241 trace!(" number {number}");
242 trace!(" length raw: {:02x}{:02x}", i[1], i[0]);
243 let (i, length) = read_u16(i)?;
244 trace!(" length {length}");
245 let (i, data) = nom::bytes::complete::take(length)(i)?;
246 trace!(" data len {}", data.len());
247 trace!(" data: {data:02x?}");
248
249 let (i, _pad) = nom::bytes::complete::take((4 - (length % 4)) & 3)(i)?;
251
252 Ok((
253 i,
254 Section {
255 ty,
256 number,
257 length,
258 data: data.to_vec(),
259 },
260 ))
261}
262
263impl Display for Section {
264 fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
265 writeln!(fmt, "{INDENT}{} - {} bytes", self.ty, self.length)?;
266 writeln!(fmt, "{INDENT}{INDENT}{}", hex::encode(&self.data))?;
267 Ok(())
268 }
269}
270
271#[derive(Copy, Clone, Debug, PartialEq, Eq)]
272#[repr(u8)]
273pub enum SectionType {
274 Task = 0,
275 Subroutine,
276 Sound,
277 Animation,
278 Count,
279}
280
281impl SectionType {
282 pub fn parse(i: &[u8]) -> IResult<&[u8], Self> {
283 let (i, ty) = nom::number::complete::u8(i)?;
284 let ty = match ty {
285 0 => Self::Task,
286 1 => Self::Subroutine,
287 2 => Self::Sound,
288 3 => Self::Animation,
289 4 => Self::Count,
290 _ => {
291 return Err(nom::Err::Failure(nom::error::Error {
292 input: i,
293 code: nom::error::ErrorKind::Verify,
294 }));
295 }
296 };
297 Ok((i, ty))
298 }
299}
300
301impl Display for SectionType {
302 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
303 Debug::fmt(self, fmt)
304 }
305}
306
307#[derive(Clone, Debug, PartialEq, Eq)]
308pub struct Symbol {
309 pub ty: SymbolType,
310 pub index: u8,
311 pub length: u16,
312 pub name: CString,
313}
314
315fn parse_symbol(i: &[u8]) -> IResult<&[u8], Symbol> {
316 let read_u16 = nom::number::complete::u16(Endianness::Little);
317 let read_u8 = nom::number::complete::u8;
318
319 let (i, ty) = SymbolType::parse(i)?;
320 trace!("Symbol type {ty}");
321 let (i, index) = read_u8(i)?;
322 let (i, length) = read_u16(i)?;
323 let (i, name) = nom::bytes::complete::take(length)(i)?;
324
325 Ok((
326 i,
327 Symbol {
328 ty,
329 index,
330 length,
331 name: CString::from_vec_with_nul(name.to_vec()).map_err(|_| {
332 nom::Err::Failure(nom::error::Error {
333 input: i,
334 code: nom::error::ErrorKind::Alpha,
335 })
336 })?,
337 },
338 ))
339}
340
341impl Display for Symbol {
342 fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
343 writeln!(
344 fmt,
345 "{INDENT}{} at {} - {} bytes",
346 self.ty, self.index, self.length
347 )?;
348 writeln!(fmt, "{INDENT}{INDENT}{:?}", self.name)?;
349 Ok(())
350 }
351}
352
353#[derive(Copy, Clone, Debug, PartialEq, Eq)]
354#[repr(u8)]
355pub enum SymbolType {
356 Task = 0,
357 Sub,
358 Var,
359}
360
361impl SymbolType {
362 pub fn parse(i: &[u8]) -> IResult<&[u8], Self> {
363 let (i, ty) = nom::number::complete::u8(i)?;
364 let ty = match ty {
365 0 => Self::Task,
366 1 => Self::Sub,
367 2 => Self::Var,
368 _ => {
369 return Err(nom::Err::Failure(nom::error::Error {
370 input: i,
371 code: nom::error::ErrorKind::Verify,
372 }));
373 }
374 };
375 Ok((i, ty))
376 }
377}
378
379impl Display for SymbolType {
380 fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
381 Debug::fmt(self, fmt)
382 }
383}
384
385#[cfg(test)]
386mod test {
387 use super::*;
388 use hex_literal::hex;
389
390 const SAMPLE: &[u8] = &hex!(
391 "5243584902010100010000000000 \
392 140013070207e18713010232e1812181 \
393 430264002141000005006d61696e00"
394 );
395
396 const COMPLEX: &[u8] = &hex!(
397 "52435849020103000500000001000400e181218100000e0013070207e187
398 130102321700710100000001330014000232001401020500130100002400
399 00010085420059000008140102feff270d8502000b000006140102020043
400 02640027a800010008007365745f66776400000005006d61696e0000010a
401 006c6f6f705f7461736b0002000600706f776572000201060064656c7461
402 00"
403 );
404
405 #[test]
406 fn err_msg() {
407 let out = print_hex_with_marker_at(COMPLEX, 5);
408 let expected = " 0 1 2 3 4 5 6 7 8 9 a b c d e f
4090x00: 52 43 58 49 02 01 03 00 05 00 00 00 01 00 04 00
410 ^<<
4110x10: e1 81 21 81 00 00 0e 00 13 07 02 07 e1 87 13 01
4120x20: 02 32 17 00 71 01 00 00 00 01 33 00 14 00 02 32
4130x30: 00 14 01 02 05 00 13 01 00 00 24 00 00 01 00 85
4140x40: 42 00 59 00 00 08 14 01 02 fe ff 27 0d 85 02 00
4150x50: 0b 00 00 06 14 01 02 02 00 43 02 64 00 27 a8 00
4160x60: 01 00 08 00 73 65 74 5f 66 77 64 00 00 00 05 00
4170x70: 6d 61 69 6e 00 00 01 0a 00 6c 6f 6f 70 5f 74 61
4180x80: 73 6b 00 02 00 06 00 70 6f 77 65 72 00 02 01 06
4190x90: 00 64 65 6c 74 61 00
420";
421 assert_eq!(out, expected);
422
423 let out = print_hex_with_marker_at(COMPLEX, 35);
424 let expected = " 0 1 2 3 4 5 6 7 8 9 a b c d e f
4250x00: 52 43 58 49 02 01 03 00 05 00 00 00 01 00 04 00
4260x10: e1 81 21 81 00 00 0e 00 13 07 02 07 e1 87 13 01
4270x20: 02 32 17 00 71 01 00 00 00 01 33 00 14 00 02 32
428 ^<<
4290x30: 00 14 01 02 05 00 13 01 00 00 24 00 00 01 00 85
4300x40: 42 00 59 00 00 08 14 01 02 fe ff 27 0d 85 02 00
4310x50: 0b 00 00 06 14 01 02 02 00 43 02 64 00 27 a8 00
4320x60: 01 00 08 00 73 65 74 5f 66 77 64 00 00 00 05 00
4330x70: 6d 61 69 6e 00 00 01 0a 00 6c 6f 6f 70 5f 74 61
4340x80: 73 6b 00 02 00 06 00 70 6f 77 65 72 00 02 01 06
4350x90: 00 64 65 6c 74 61 00
436";
437 assert_eq!(out, expected);
438 }
439
440 #[test]
441 fn parse_sample() {
442 let bin = RcxBin::parse(SAMPLE).unwrap();
443 assert_eq!(
444 bin,
445 RcxBin {
446 signature: *b"RCXI",
447 version: 0x0102,
448 section_count: 1,
449 symbol_count: 1,
450 target_type: TargetType::Rcx,
451 reserved: 0,
452 sections: vec![Section {
453 ty: SectionType::Task,
454 number: 0,
455 length: 20,
456 data: vec![
457 0x13, 0x7, 0x2, 0x7, 0xe1, 0x87, 0x13, 0x1, 0x2, 0x32,
458 0xe1, 0x81, 0x21, 0x81, 0x43, 0x2, 0x64, 0x0, 0x21,
459 0x41
460 ]
461 }],
462 symbols: vec![Symbol {
463 ty: SymbolType::Task,
464 index: 0,
465 length: 5,
466 name: CString::new("main").unwrap(),
467 }],
468 }
469 );
470 }
471
472 #[test]
473 fn parse_complex() {
474 let bin = RcxBin::parse(COMPLEX).unwrap();
475 assert_eq!(
476 bin,
477 RcxBin {
478 signature: *b"RCXI",
479 version: 0x0102,
480 section_count: 3,
481 symbol_count: 5,
482 target_type: TargetType::Rcx,
483 reserved: 0,
484 sections: vec![
485 Section {
486 ty: SectionType::Subroutine,
487 number: 0,
488 length: 4,
489 data: vec![225, 129, 33, 129]
490 },
491 Section {
492 ty: SectionType::Task,
493 number: 0,
494 length: 14,
495 data: vec![
496 19, 7, 2, 7, 225, 135, 19, 1, 2, 50, 23, 0, 113, 1
497 ]
498 },
499 Section {
500 ty: SectionType::Task,
501 number: 1,
502 length: 51,
503 data: vec![
504 20, 0, 2, 50, 0, 20, 1, 2, 5, 0, 19, 1, 0, 0, 36,
505 0, 0, 1, 0, 133, 66, 0, 89, 0, 0, 8, 20, 1, 2, 254,
506 255, 39, 13, 133, 2, 0, 11, 0, 0, 6, 20, 1, 2, 2,
507 0, 67, 2, 100, 0, 39, 168
508 ]
509 },
510 ],
511 symbols: vec![
512 Symbol {
513 ty: SymbolType::Sub,
514 index: 0,
515 length: 8,
516 name: CString::new("set_fwd").unwrap()
517 },
518 Symbol {
519 ty: SymbolType::Task,
520 index: 0,
521 length: 5,
522 name: CString::new("main").unwrap()
523 },
524 Symbol {
525 ty: SymbolType::Task,
526 index: 1,
527 length: 10,
528 name: CString::new("loop_task").unwrap()
529 },
530 Symbol {
531 ty: SymbolType::Var,
532 index: 0,
533 length: 6,
534 name: CString::new("power").unwrap()
535 },
536 Symbol {
537 ty: SymbolType::Var,
538 index: 1,
539 length: 6,
540 name: CString::new("delta").unwrap()
541 }
542 ],
543 }
544 );
545 }
546}