Skip to content

Commit 67b9f44

Browse files
committed
Implement program dowload
1 parent ea0796b commit 67b9f44

File tree

4 files changed

+123
-45
lines changed

4 files changed

+123
-45
lines changed

Cargo.lock

+15-8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ futures = "0.3"
1818
lazy_static = "1"
1919
tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
2020
uuid = "1"
21-
rcx = "0.1.1"
21+
rcx = "0.1.2"
2222
nom = "7.1.3"
23+
hex = "0.4.3"
2324

2425
[dev-dependencies]
2526
hex-literal = "0.4.1"

src/rcx.rs

+28-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::Result;
22
use color_eyre::eyre::eyre;
3-
use rcx::{tower::usb::UsbTower, Rcx};
3+
use rcx::{tower::usb::UsbTower, Rcx, Sound};
44
use std::{
55
ffi::OsString,
66
path::{Path, PathBuf},
@@ -66,42 +66,57 @@ pub fn program(slot: u8, file: PathBuf) -> Result<()> {
6666
let bin = std::fs::read(&file)?;
6767
let bin = RcxBin::parse(&bin)?;
6868

69+
println!("{bin}");
70+
71+
// Stop any running tasks
72+
rcx.stop_all_tasks()?;
73+
6974
// Prepare RCX for download
7075
rcx.set_program_number(slot)?;
7176

77+
// Delete existing tasks and subroutines
78+
rcx.delete_all_tasks()?;
79+
rcx.delete_all_subroutines()?;
80+
7281
// Download the program chunks
73-
for (idx, chunk) in bin.chunks.iter().enumerate() {
82+
for (idx, section) in bin.sections.iter().enumerate() {
7483
println!(
75-
"Downloading chunk {} of {} to task {}",
84+
"[prog {}] Downloading section {} of {} to section number {}",
85+
slot,
7686
idx + 1,
77-
bin.chunks.len(),
78-
chunk.number
87+
bin.sections.len(),
88+
section.number,
7989
);
80-
rcx.start_task_download(chunk.number, chunk.data.len().try_into()?)?;
90+
rcx.start_task_download(
91+
section.number,
92+
section.data.len().try_into()?,
93+
)?;
8194

82-
for (idx, data_chunk) in chunk.data.chunks(256).enumerate() {
83-
let mut buf = [0; 256];
84-
buf[..data_chunk.len()].copy_from_slice(data_chunk);
85-
let checksum = buf
95+
for (idx, data_chunk) in section.data.chunks(256).enumerate() {
96+
let checksum = data_chunk
8697
.iter()
8798
.copied()
8899
.reduce(u8::wrapping_add)
89100
.unwrap_or_default();
90-
let idx = if (idx + 1) * 256 >= chunk.data.len() {
101+
let idx = if (idx + 1) * 256 >= section.data.len() {
91102
// last block
92103
0
93104
} else {
94105
idx as i16 + 1
95106
};
107+
println!("Chunk {}, len {}", idx, data_chunk.len());
108+
rcx.begin_task_chunk(section.number, data_chunk.len().try_into()?)?;
96109
rcx.transfer_data(
97110
idx,
98111
data_chunk.len().try_into()?,
99-
buf,
112+
data_chunk.to_vec(),
100113
checksum,
101114
)?;
102115
}
103116
}
104117

105118
println!("Successfully downloaded {}", file.display());
106-
todo!()
119+
// Play the download successful sound
120+
rcx.play_sound(Sound::FastUpwardTones)?;
121+
Ok(())
107122
}

src/rcx/binfmt.rs

+78-23
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,24 @@
2424
use crate::Result;
2525
use color_eyre::eyre::eyre;
2626
use nom::{number::Endianness, IResult};
27-
use std::ffi::CString;
27+
use std::{
28+
ffi::CString,
29+
fmt::{self, Debug, Display, Formatter},
30+
};
2831

2932
const RCX_TAG: &str = "RCXI";
30-
const MAX_CHUNKS: usize = 10;
33+
const MAX_SECTIONS: usize = 10;
34+
const INDENT: &str = " ";
3135

3236
#[derive(Clone, Debug, PartialEq, Eq)]
3337
pub struct RcxBin {
3438
pub signature: [u8; 4],
3539
pub version: u16,
36-
pub chunk_count: u16,
40+
pub section_count: u16,
3741
pub symbol_count: u16,
3842
pub target_type: u8,
3943
pub reserved: u8,
40-
pub chunks: Vec<Chunk>,
44+
pub sections: Vec<Section>,
4145
pub symbols: Vec<Symbol>,
4246
}
4347

@@ -49,46 +53,71 @@ impl RcxBin {
4953
}
5054

5155
pub fn verify(&self) -> Result<()> {
52-
fn repeated_idx(chunks: &[Chunk]) -> bool {
53-
let mut c = chunks.iter().map(|c| c.number).collect::<Vec<_>>();
56+
fn repeated_idx(sections: &[Section]) -> bool {
57+
let mut c = sections.iter().map(|c| c.number).collect::<Vec<_>>();
5458
c.sort_unstable();
5559
c.dedup();
56-
c.len() != chunks.len()
60+
c.len() != sections.len()
5761
}
5862

5963
// check chunk count
60-
if self.chunk_count as usize != self.chunks.len()
61-
|| self.chunks.len() > MAX_CHUNKS
64+
if self.section_count as usize != self.sections.len()
65+
|| self.sections.len() > MAX_SECTIONS
6266
{
6367
Err(eyre!("Invalid number of chunks"))
64-
} else if repeated_idx(&self.chunks) {
68+
} else if repeated_idx(&self.sections) {
6569
Err(eyre!("Nonunique chunk numbers"))
6670
} else {
6771
Ok(())
6872
}
6973
}
7074
}
7175

76+
impl Display for RcxBin {
77+
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
78+
writeln!(
79+
fmt,
80+
"Signature: {}",
81+
String::from_utf8_lossy(&self.signature),
82+
)?;
83+
writeln!(fmt, "Version: {:x}", self.version)?;
84+
writeln!(
85+
fmt,
86+
"{} sections, {} symbols",
87+
self.section_count, self.symbol_count,
88+
)?;
89+
writeln!(fmt, "Target: {}", self.target_type)?;
90+
writeln!(fmt, "Sections:")?;
91+
for section in &self.sections {
92+
writeln!(fmt, "{section}")?;
93+
}
94+
writeln!(fmt, "Symbols:")?;
95+
for symbol in &self.symbols {
96+
writeln!(fmt, "{symbol}")?;
97+
}
98+
Ok(())
99+
}
100+
}
72101
#[derive(Clone, Debug, PartialEq, Eq)]
73-
pub struct Chunk {
74-
pub ty: ChunkType,
102+
pub struct Section {
103+
pub ty: SectionType,
75104
pub number: u8,
76105
pub length: u16,
77106
pub data: Vec<u8>,
78107
}
79108

80-
fn parse_chunk(i: &[u8]) -> IResult<&[u8], Chunk> {
109+
fn parse_chunk(i: &[u8]) -> IResult<&[u8], Section> {
81110
let read_u16 = nom::number::complete::u16(Endianness::Little);
82111
let read_u8 = nom::number::complete::u8;
83112

84-
let (i, ty) = ChunkType::parse(i)?;
113+
let (i, ty) = SectionType::parse(i)?;
85114
let (i, number) = read_u8(i)?;
86115
let (i, length) = read_u16(i)?;
87116
let (i, data) = nom::bytes::complete::take(length)(i)?;
88117

89118
Ok((
90119
i,
91-
Chunk {
120+
Section {
92121
ty,
93122
number,
94123
length,
@@ -97,17 +126,25 @@ fn parse_chunk(i: &[u8]) -> IResult<&[u8], Chunk> {
97126
))
98127
}
99128

100-
#[derive(Clone, Debug, PartialEq, Eq)]
129+
impl Display for Section {
130+
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
131+
writeln!(fmt, "{INDENT}{} - {} bytes", self.ty, self.length)?;
132+
writeln!(fmt, "{INDENT}{INDENT}{}", hex::encode(&self.data))?;
133+
Ok(())
134+
}
135+
}
136+
137+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
101138
#[repr(u8)]
102-
pub enum ChunkType {
139+
pub enum SectionType {
103140
Task = 0,
104141
SubChunk,
105142
Sound,
106143
Animation,
107144
Count,
108145
}
109146

110-
impl ChunkType {
147+
impl SectionType {
111148
pub fn parse(i: &[u8]) -> IResult<&[u8], Self> {
112149
let (i, ty) = nom::number::complete::u8(i)?;
113150
let ty = match ty {
@@ -127,6 +164,12 @@ impl ChunkType {
127164
}
128165
}
129166

167+
impl Display for SectionType {
168+
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
169+
Debug::fmt(self, fmt)
170+
}
171+
}
172+
130173
#[derive(Clone, Debug, PartialEq, Eq)]
131174
pub struct Symbol {
132175
pub ty: u8,
@@ -160,6 +203,18 @@ fn parse_symbol(i: &[u8]) -> IResult<&[u8], Symbol> {
160203
))
161204
}
162205

206+
impl Display for Symbol {
207+
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
208+
writeln!(
209+
fmt,
210+
"{INDENT}{} at {} - {} bytes",
211+
self.ty, self.index, self.length
212+
)?;
213+
writeln!(fmt, "{INDENT}{INDENT}{:?}", self.name)?;
214+
Ok(())
215+
}
216+
}
217+
163218
fn parse(bin: &[u8]) -> IResult<&[u8], RcxBin> {
164219
let read_u16 = nom::number::complete::u16(Endianness::Little);
165220
let read_u8 = nom::number::complete::u8;
@@ -179,11 +234,11 @@ fn parse(bin: &[u8]) -> IResult<&[u8], RcxBin> {
179234
RcxBin {
180235
signature: signature.try_into().unwrap_or([0; 4]),
181236
version,
182-
chunk_count,
237+
section_count: chunk_count,
183238
symbol_count,
184239
target_type,
185240
reserved,
186-
chunks,
241+
sections: chunks,
187242
symbols,
188243
},
189244
))
@@ -208,12 +263,12 @@ mod test {
208263
RcxBin {
209264
signature: *b"RCXI",
210265
version: 0x0102,
211-
chunk_count: 1,
266+
section_count: 1,
212267
symbol_count: 1,
213268
target_type: 0,
214269
reserved: 0,
215-
chunks: vec![Chunk {
216-
ty: ChunkType::Task,
270+
sections: vec![Section {
271+
ty: SectionType::Task,
217272
number: 0,
218273
length: 20,
219274
data: vec![

0 commit comments

Comments
 (0)