diff --git a/src/camera/mod.rs b/src/camera/mod.rs index 1f1b81c..a4638be 100644 --- a/src/camera/mod.rs +++ b/src/camera/mod.rs @@ -9,7 +9,7 @@ use byteorder::{LittleEndian, WriteBytesExt}; use devices::SupportedCamera; use log::{debug, error, trace}; use ptp::{ - enums::{CommandCode, ContainerType, PropCode, ResponseCode, UsbMode}, + enums::{CommandCode, ContainerCode, ContainerType, PropCode, ResponseCode, UsbMode}, structs::{ContainerInfo, DeviceInfo}, }; use rusb::{GlobalContext, constants::LIBUSB_CLASS_IMAGE}; @@ -216,7 +216,9 @@ pub trait CameraImpl { transaction: bool, ) -> anyhow::Result> { let transaction_id = if transaction { - Some(ptp.transaction_id) + let transaction_id = Some(ptp.transaction_id); + ptp.transaction_id += 1; + transaction_id } else { None }; @@ -252,10 +254,13 @@ pub trait CameraImpl { } ContainerType::Response => { trace!("Response received: code {:?}", container.code); - let code = ResponseCode::try_from(container.code)?; - if code != ResponseCode::Ok { - bail!(ptp::error::Error::Response(container.code)); + match container.code { + ContainerCode::Command(_) | ContainerCode::Response(ResponseCode::Ok) => {} + ContainerCode::Response(code) => { + bail!(ptp::error::Error::Response(code as u16)); + } } + trace!( "Command {:?} completed successfully with data payload of {} bytes", code, @@ -276,28 +281,12 @@ pub trait CameraImpl { kind: ContainerType, code: CommandCode, payload: &[u8], - // Fuji, for the love of God don't ever write code again. transaction_id: Option, ) -> anyhow::Result<()> { - // Look at what you made me do. Fuck. - let header_len = ContainerInfo::SIZE - - if transaction_id.is_none() { - size_of::() - } else { - 0 - }; - - let first_chunk_len = min(payload.len(), self.chunk_size() - header_len); - let total_len = u32::try_from(payload.len() + header_len)?; - - let mut buffer = Vec::with_capacity(first_chunk_len + header_len); - buffer.write_u32::(total_len)?; - buffer.write_u16::(kind as u16)?; - buffer.write_u16::(code as u16)?; - if let Some(transaction_id) = transaction_id { - buffer.write_u32::(transaction_id)?; - } + let container_info = ContainerInfo::new(kind, code, transaction_id, payload)?; + let first_chunk_len = min(payload.len(), self.chunk_size() - container_info.len()); + let mut buffer: Vec = container_info.try_into()?; buffer.extend_from_slice(&payload[..first_chunk_len]); trace!( @@ -329,13 +318,14 @@ pub trait CameraImpl { trace!("Read {n} bytes from bulk_in"); - let container_info = ContainerInfo::parse(buf)?; - if container_info.payload_len == 0 { + let container_info = ContainerInfo::try_from(buf)?; + + let payload_len = container_info.payload_len(); + if payload_len == 0 { trace!("No payload in container"); return Ok((container_info, Vec::new())); } - let payload_len = container_info.payload_len as usize; let mut payload = Vec::with_capacity(payload_len); if buf.len() > ContainerInfo::SIZE { payload.extend_from_slice(&buf[ContainerInfo::SIZE..]); @@ -415,7 +405,7 @@ pub trait CameraImpl { fn import_backup(&self, ptp: &mut Ptp, buffer: &[u8]) -> anyhow::Result<()> { debug!("Preparing ObjectInfo header for backup"); - let mut obj_info = vec![0u8; 1088]; + let mut obj_info = vec![0u8; 1012]; let mut cursor = Cursor::new(&mut obj_info[..]); cursor.write_u32::(0x0)?; cursor.write_u16::(0x5000)?; @@ -433,13 +423,7 @@ pub trait CameraImpl { debug!("Received response with {} bytes", response.len()); debug!("Sending SendObject command for backup"); - let response = self.send( - ptp, - CommandCode::SendObject, - Some(&[0x0]), - Some(buffer), - false, - )?; + let response = self.send(ptp, CommandCode::SendObject, None, Some(buffer), true)?; debug!("Received response with {} bytes", response.len()); Ok(()) diff --git a/src/camera/ptp/enums.rs b/src/camera/ptp/enums.rs index f2d6f41..2174952 100644 --- a/src/camera/ptp/enums.rs +++ b/src/camera/ptp/enums.rs @@ -31,7 +31,7 @@ impl TryFrom for CommandCode { 0x100D => Ok(Self::SendObject), 0x1015 => Ok(Self::GetDevicePropValue), 0x1016 => Ok(Self::SetDevicePropValue), - v => bail!("Unknown command code '{v}'"), + v => bail!("Unknown command code '{v:x?}'"), } } } @@ -112,11 +112,43 @@ impl std::convert::TryFrom for ResponseCode { 0x201E => Ok(Self::SessionAlreadyOpen), 0x201F => Ok(Self::TransactionCancelled), 0x2020 => Ok(Self::SpecificationOfDestinationUnsupported), - v => bail!("Unknown response code '{v}'"), + v => bail!("Unknown response code '{v:x?}'"), } } } +#[repr(u16)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ContainerCode { + Command(CommandCode), + Response(ResponseCode), +} + +impl From for u16 { + fn from(code: ContainerCode) -> Self { + match code { + ContainerCode::Command(cmd) => cmd as Self, + ContainerCode::Response(resp) => resp as Self, + } + } +} + +impl TryFrom for ContainerCode { + type Error = anyhow::Error; + + fn try_from(value: u16) -> Result { + if let Ok(cmd) = CommandCode::try_from(value) { + return Ok(Self::Command(cmd)); + } + + if let Ok(resp) = ResponseCode::try_from(value) { + return Ok(Self::Response(resp)); + } + + bail!("Unknown container code '{value:x?}'"); + } +} + #[repr(u32)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PropCode { diff --git a/src/camera/ptp/structs.rs b/src/camera/ptp/structs.rs index 8e166f7..35e6adf 100644 --- a/src/camera/ptp/structs.rs +++ b/src/camera/ptp/structs.rs @@ -1,8 +1,11 @@ use std::io::Cursor; -use byteorder::{LittleEndian, ReadBytesExt}; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use super::{enums::ContainerType, read::Read}; +use super::{ + enums::{CommandCode, ContainerCode, ContainerType}, + read::Read, +}; #[allow(dead_code)] #[derive(Debug)] @@ -50,28 +53,81 @@ impl TryFrom<&[u8]> for DeviceInfo { #[derive(Debug, Clone, Copy)] pub struct ContainerInfo { + pub total_len: u32, pub kind: ContainerType, - pub payload_len: u32, - pub code: u16, + pub code: ContainerCode, + pub transaction_id: Option, } impl ContainerInfo { - pub const SIZE: usize = - size_of::() + size_of::() + size_of::() + size_of::(); -} + const BASE_SIZE: usize = size_of::() + size_of::() + size_of::(); + pub const SIZE: usize = Self::BASE_SIZE + size_of::(); -impl ContainerInfo { - pub fn parse(mut r: R) -> anyhow::Result { - let payload_len = r.read_u32::()? - Self::SIZE as u32; - let kind = r.read_u16::()?; - let kind = ContainerType::try_from(kind)?; - let code = r.read_u16::()?; - let _transaction_id = r.read_u32::()?; + pub fn new( + kind: ContainerType, + code: CommandCode, + transaction_id: Option, + payload: &[u8], + ) -> anyhow::Result { + let mut total_len = if transaction_id.is_some() { + Self::SIZE + } else { + Self::BASE_SIZE + }; + total_len += payload.len(); Ok(Self { + total_len: u32::try_from(total_len)?, + kind, + code: ContainerCode::Command(code), + transaction_id, + }) + } + + pub const fn len(&self) -> usize { + if self.transaction_id.is_some() { + Self::SIZE + } else { + Self::BASE_SIZE + } + } + + pub const fn payload_len(&self) -> usize { + self.total_len as usize - self.len() + } +} + +impl TryFrom<&[u8]> for ContainerInfo { + type Error = anyhow::Error; + + fn try_from(bytes: &[u8]) -> Result { + let mut r = Cursor::new(bytes); + + let total_len = r.read_u32::()?; + let kind = ContainerType::try_from(r.read_u16::()?)?; + let code = ContainerCode::try_from(r.read_u16::()?)?; + let transaction_id = Some(r.read_u32::()?); + + Ok(Self { + total_len, kind, - payload_len, code, + transaction_id, }) } } + +impl TryFrom for Vec { + type Error = anyhow::Error; + + fn try_from(val: ContainerInfo) -> Result { + let mut buf = Self::with_capacity(val.len()); + buf.write_u32::(val.total_len)?; + buf.write_u16::(val.kind as u16)?; + buf.write_u16::(val.code.into())?; + if let Some(transaction_id) = val.transaction_id { + buf.write_u32::(transaction_id)?; + } + Ok(buf) + } +}