Skip to content

Commit bfc19b1

Browse files
committed
Correctly(?) parse programs with subroutines
1 parent 67b9f44 commit bfc19b1

File tree

2 files changed

+238
-36
lines changed

2 files changed

+238
-36
lines changed

samples/complex_program.nqc

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
task main() {
2+
SetPower(OUT_A, 50);
3+
set_fwd();
4+
start loop_task;
5+
}
6+
7+
task loop_task() {
8+
int power = 50;
9+
int delta = 5;
10+
for(;;) {
11+
SetPower(OUT_A, power);
12+
power += delta;
13+
if (power >= 90) {
14+
delta = -2;
15+
} else if (power <= 10) {
16+
delta = 2;
17+
}
18+
Wait(100);
19+
}
20+
}
21+
22+
sub set_fwd() {
23+
OnFwd(OUT_A);
24+
}

src/rcx/binfmt.rs

+214-36
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,42 @@ use color_eyre::eyre::eyre;
2626
use nom::{number::Endianness, IResult};
2727
use std::{
2828
ffi::CString,
29-
fmt::{self, Debug, Display, Formatter},
29+
fmt::{self, Debug, Display, Formatter, Write},
3030
};
3131

3232
const RCX_TAG: &str = "RCXI";
3333
const MAX_SECTIONS: usize = 10;
3434
const INDENT: &str = " ";
35+
const HEXDUMP_WRAP_BYTES: usize = 16;
36+
37+
fn print_hex_with_marker_at(bin: &[u8], pos: usize) -> String {
38+
let mut out = String::new();
39+
40+
// header
41+
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+
// hexdump
48+
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+
// indent the marker appropriately
58+
out += " "; // indent past the offset display
59+
out.extend(std::iter::repeat(" ").take(pos % HEXDUMP_WRAP_BYTES));
60+
writeln!(&mut out, "^<<").unwrap();
61+
}
62+
}
63+
out
64+
}
3565

3666
#[derive(Clone, Debug, PartialEq, Eq)]
3767
pub struct RcxBin {
@@ -47,14 +77,28 @@ pub struct RcxBin {
4777

4878
impl RcxBin {
4979
pub fn parse(bin: &[u8]) -> Result<Self> {
50-
let (_i, bin) = parse(bin).map_err(|e| eyre!("Parse error: {e}"))?;
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 eyre!("Incomplete input, needed {needed:?}");
86+
}
87+
};
88+
let pos = bin.len() - input.len();
89+
println!("{}", print_hex_with_marker_at(bin, pos));
90+
eyre!("Parse error: {err}")
91+
})?;
5192
bin.verify()?;
5293
Ok(bin)
5394
}
5495

5596
pub fn verify(&self) -> Result<()> {
5697
fn repeated_idx(sections: &[Section]) -> bool {
57-
let mut c = sections.iter().map(|c| c.number).collect::<Vec<_>>();
98+
let mut c = sections
99+
.iter()
100+
.map(|c| (c.ty as u8, c.number))
101+
.collect::<Vec<_>>();
58102
c.sort_unstable();
59103
c.dedup();
60104
c.len() != sections.len()
@@ -98,6 +142,40 @@ impl Display for RcxBin {
98142
Ok(())
99143
}
100144
}
145+
146+
fn parse(bin: &[u8]) -> IResult<&[u8], RcxBin> {
147+
println!("Input len: {}", bin.len());
148+
let read_u16 = nom::number::complete::u16(Endianness::Little);
149+
let read_u8 = nom::number::complete::u8;
150+
151+
let (i, signature) = nom::bytes::complete::tag(RCX_TAG)(bin)?;
152+
let (i, version) = read_u16(i)?;
153+
let (i, section_count) = read_u16(i)?;
154+
let (i, symbol_count) = read_u16(i)?;
155+
let (i, target_type) = read_u8(i)?;
156+
let (i, reserved) = read_u8(i)?;
157+
158+
println!("Parse {section_count} sections");
159+
let (i, sections) =
160+
nom::multi::count(parse_section, section_count.into())(i)?;
161+
println!("Parse {symbol_count} symbols");
162+
let (i, symbols) = nom::multi::count(parse_symbol, symbol_count.into())(i)?;
163+
164+
IResult::Ok((
165+
i,
166+
RcxBin {
167+
signature: signature.try_into().unwrap_or([0; 4]),
168+
version,
169+
section_count,
170+
symbol_count,
171+
target_type,
172+
reserved,
173+
sections,
174+
symbols,
175+
},
176+
))
177+
}
178+
101179
#[derive(Clone, Debug, PartialEq, Eq)]
102180
pub struct Section {
103181
pub ty: SectionType,
@@ -106,14 +184,24 @@ pub struct Section {
106184
pub data: Vec<u8>,
107185
}
108186

109-
fn parse_chunk(i: &[u8]) -> IResult<&[u8], Section> {
187+
fn parse_section(i: &[u8]) -> IResult<&[u8], Section> {
188+
let neg_offset = i.len();
110189
let read_u16 = nom::number::complete::u16(Endianness::Little);
111190
let read_u8 = nom::number::complete::u8;
112191

113192
let (i, ty) = SectionType::parse(i)?;
193+
println!("- section type {ty} - offset from back: {neg_offset}");
114194
let (i, number) = read_u8(i)?;
195+
println!(" number {number}");
196+
println!(" length raw: {:02x}{:02x}", i[1], i[0]);
115197
let (i, length) = read_u16(i)?;
198+
println!(" length {length}");
116199
let (i, data) = nom::bytes::complete::take(length)(i)?;
200+
println!(" data len {}", data.len());
201+
println!(" data: {data:02x?}");
202+
203+
// read padding bytes
204+
let (i, _pad) = nom::bytes::complete::take((4 - (length % 4)) & 3)(i)?;
117205

118206
Ok((
119207
i,
@@ -138,7 +226,7 @@ impl Display for Section {
138226
#[repr(u8)]
139227
pub enum SectionType {
140228
Task = 0,
141-
SubChunk,
229+
Subroutine,
142230
Sound,
143231
Animation,
144232
Count,
@@ -149,7 +237,7 @@ impl SectionType {
149237
let (i, ty) = nom::number::complete::u8(i)?;
150238
let ty = match ty {
151239
0 => Self::Task,
152-
1 => Self::SubChunk,
240+
1 => Self::Subroutine,
153241
2 => Self::Sound,
154242
3 => Self::Animation,
155243
4 => Self::Count,
@@ -183,6 +271,7 @@ fn parse_symbol(i: &[u8]) -> IResult<&[u8], Symbol> {
183271
let read_u8 = nom::number::complete::u8;
184272

185273
let (i, ty) = read_u8(i)?;
274+
println!("Symbol type {ty}");
186275
let (i, index) = read_u8(i)?;
187276
let (i, length) = read_u16(i)?;
188277
let (i, name) = nom::bytes::complete::take(length)(i)?;
@@ -214,36 +303,6 @@ impl Display for Symbol {
214303
Ok(())
215304
}
216305
}
217-
218-
fn parse(bin: &[u8]) -> IResult<&[u8], RcxBin> {
219-
let read_u16 = nom::number::complete::u16(Endianness::Little);
220-
let read_u8 = nom::number::complete::u8;
221-
222-
let (i, signature) = nom::bytes::complete::tag(RCX_TAG)(bin)?;
223-
let (i, version) = read_u16(i)?;
224-
let (i, chunk_count) = read_u16(i)?;
225-
let (i, symbol_count) = read_u16(i)?;
226-
let (i, target_type) = read_u8(i)?;
227-
let (i, reserved) = read_u8(i)?;
228-
229-
let (i, chunks) = nom::multi::count(parse_chunk, chunk_count.into())(i)?;
230-
let (i, symbols) = nom::multi::count(parse_symbol, symbol_count.into())(i)?;
231-
232-
IResult::Ok((
233-
i,
234-
RcxBin {
235-
signature: signature.try_into().unwrap_or([0; 4]),
236-
version,
237-
section_count: chunk_count,
238-
symbol_count,
239-
target_type,
240-
reserved,
241-
sections: chunks,
242-
symbols,
243-
},
244-
))
245-
}
246-
247306
#[cfg(test)]
248307
mod test {
249308
use super::*;
@@ -255,6 +314,50 @@ mod test {
255314
430264002141000005006d61696e00"
256315
);
257316

317+
const COMPLEX: &[u8] = &hex!(
318+
"52435849020103000500000001000400e181218100000e0013070207e187
319+
130102321700710100000001330014000232001401020500130100002400
320+
00010085420059000008140102feff270d8502000b000006140102020043
321+
02640027a800010008007365745f66776400000005006d61696e0000010a
322+
006c6f6f705f7461736b0002000600706f776572000201060064656c7461
323+
00"
324+
);
325+
326+
#[test]
327+
fn err_msg() {
328+
let out = print_hex_with_marker_at(COMPLEX, 5);
329+
let expected = " 0 1 2 3 4 5 6 7 8 9 a b c d e f
330+
0x00: 52 43 58 49 02 01 03 00 05 00 00 00 01 00 04 00
331+
^<<
332+
0x10: e1 81 21 81 00 00 0e 00 13 07 02 07 e1 87 13 01
333+
0x20: 02 32 17 00 71 01 00 00 00 01 33 00 14 00 02 32
334+
0x30: 00 14 01 02 05 00 13 01 00 00 24 00 00 01 00 85
335+
0x40: 42 00 59 00 00 08 14 01 02 fe ff 27 0d 85 02 00
336+
0x50: 0b 00 00 06 14 01 02 02 00 43 02 64 00 27 a8 00
337+
0x60: 01 00 08 00 73 65 74 5f 66 77 64 00 00 00 05 00
338+
0x70: 6d 61 69 6e 00 00 01 0a 00 6c 6f 6f 70 5f 74 61
339+
0x80: 73 6b 00 02 00 06 00 70 6f 77 65 72 00 02 01 06
340+
0x90: 00 64 65 6c 74 61 00
341+
";
342+
assert_eq!(out, expected);
343+
344+
let out = print_hex_with_marker_at(COMPLEX, 35);
345+
let expected = " 0 1 2 3 4 5 6 7 8 9 a b c d e f
346+
0x00: 52 43 58 49 02 01 03 00 05 00 00 00 01 00 04 00
347+
0x10: e1 81 21 81 00 00 0e 00 13 07 02 07 e1 87 13 01
348+
0x20: 02 32 17 00 71 01 00 00 00 01 33 00 14 00 02 32
349+
^<<
350+
0x30: 00 14 01 02 05 00 13 01 00 00 24 00 00 01 00 85
351+
0x40: 42 00 59 00 00 08 14 01 02 fe ff 27 0d 85 02 00
352+
0x50: 0b 00 00 06 14 01 02 02 00 43 02 64 00 27 a8 00
353+
0x60: 01 00 08 00 73 65 74 5f 66 77 64 00 00 00 05 00
354+
0x70: 6d 61 69 6e 00 00 01 0a 00 6c 6f 6f 70 5f 74 61
355+
0x80: 73 6b 00 02 00 06 00 70 6f 77 65 72 00 02 01 06
356+
0x90: 00 64 65 6c 74 61 00
357+
";
358+
assert_eq!(out, expected);
359+
}
360+
258361
#[test]
259362
fn parse_sample() {
260363
let bin = RcxBin::parse(SAMPLE).unwrap();
@@ -286,4 +389,79 @@ mod test {
286389
}
287390
);
288391
}
392+
393+
#[test]
394+
fn parse_complex() {
395+
let bin = RcxBin::parse(COMPLEX).unwrap();
396+
assert_eq!(
397+
bin,
398+
RcxBin {
399+
signature: *b"RCXI",
400+
version: 0x0102,
401+
section_count: 3,
402+
symbol_count: 5,
403+
target_type: 0,
404+
reserved: 0,
405+
sections: vec![
406+
Section {
407+
ty: SectionType::Subroutine,
408+
number: 0,
409+
length: 4,
410+
data: vec![225, 129, 33, 129]
411+
},
412+
Section {
413+
ty: SectionType::Task,
414+
number: 0,
415+
length: 14,
416+
data: vec![
417+
19, 7, 2, 7, 225, 135, 19, 1, 2, 50, 23, 0, 113, 1
418+
]
419+
},
420+
Section {
421+
ty: SectionType::Task,
422+
number: 1,
423+
length: 51,
424+
data: vec![
425+
20, 0, 2, 50, 0, 20, 1, 2, 5, 0, 19, 1, 0, 0, 36,
426+
0, 0, 1, 0, 133, 66, 0, 89, 0, 0, 8, 20, 1, 2, 254,
427+
255, 39, 13, 133, 2, 0, 11, 0, 0, 6, 20, 1, 2, 2,
428+
0, 67, 2, 100, 0, 39, 168
429+
]
430+
},
431+
],
432+
symbols: vec![
433+
Symbol {
434+
ty: 1,
435+
index: 0,
436+
length: 8,
437+
name: CString::new("set_fwd").unwrap()
438+
},
439+
Symbol {
440+
ty: 0,
441+
index: 0,
442+
length: 5,
443+
name: CString::new("main").unwrap()
444+
},
445+
Symbol {
446+
ty: 0,
447+
index: 1,
448+
length: 10,
449+
name: CString::new("loop_task").unwrap()
450+
},
451+
Symbol {
452+
ty: 2,
453+
index: 0,
454+
length: 6,
455+
name: CString::new("power").unwrap()
456+
},
457+
Symbol {
458+
ty: 2,
459+
index: 1,
460+
length: 6,
461+
name: CString::new("delta").unwrap()
462+
}
463+
],
464+
}
465+
);
466+
}
289467
}

0 commit comments

Comments
 (0)