use super::storage::{AccessCapability, FilesystemType, StorageType};
use crate::ptp::pack::{unpack_string, unpack_u16, unpack_u16_array, unpack_u32, unpack_u64};
use crate::ptp::{EventCode, ObjectFormatCode, OperationCode};
#[derive(Debug, Clone, Default)]
pub struct DeviceInfo {
pub standard_version: u16,
pub vendor_extension_id: u32,
pub vendor_extension_version: u16,
pub vendor_extension_desc: String,
pub functional_mode: u16,
pub operations_supported: Vec<OperationCode>,
pub events_supported: Vec<EventCode>,
pub device_properties_supported: Vec<u16>,
pub capture_formats: Vec<ObjectFormatCode>,
pub playback_formats: Vec<ObjectFormatCode>,
pub manufacturer: String,
pub model: String,
pub device_version: String,
pub serial_number: String,
}
impl DeviceInfo {
pub fn from_bytes(buf: &[u8]) -> Result<Self, crate::Error> {
let mut offset = 0;
let standard_version = unpack_u16(&buf[offset..])?;
offset += 2;
let vendor_extension_id = unpack_u32(&buf[offset..])?;
offset += 4;
let vendor_extension_version = unpack_u16(&buf[offset..])?;
offset += 2;
let (vendor_extension_desc, consumed) = unpack_string(&buf[offset..])?;
offset += consumed;
let functional_mode = unpack_u16(&buf[offset..])?;
offset += 2;
let (ops_raw, consumed) = unpack_u16_array(&buf[offset..])?;
let operations_supported: Vec<OperationCode> =
ops_raw.into_iter().map(OperationCode::from).collect();
offset += consumed;
let (events_raw, consumed) = unpack_u16_array(&buf[offset..])?;
let events_supported: Vec<EventCode> =
events_raw.into_iter().map(EventCode::from).collect();
offset += consumed;
let (device_properties_supported, consumed) = unpack_u16_array(&buf[offset..])?;
offset += consumed;
let (capture_raw, consumed) = unpack_u16_array(&buf[offset..])?;
let capture_formats: Vec<ObjectFormatCode> = capture_raw
.into_iter()
.map(ObjectFormatCode::from)
.collect();
offset += consumed;
let (playback_raw, consumed) = unpack_u16_array(&buf[offset..])?;
let playback_formats: Vec<ObjectFormatCode> = playback_raw
.into_iter()
.map(ObjectFormatCode::from)
.collect();
offset += consumed;
let (manufacturer, consumed) = unpack_string(&buf[offset..])?;
offset += consumed;
let (model, consumed) = unpack_string(&buf[offset..])?;
offset += consumed;
let (device_version, consumed) = unpack_string(&buf[offset..])?;
offset += consumed;
let (serial_number, _) = unpack_string(&buf[offset..])?;
Ok(DeviceInfo {
standard_version,
vendor_extension_id,
vendor_extension_version,
vendor_extension_desc,
functional_mode,
operations_supported,
events_supported,
device_properties_supported,
capture_formats,
playback_formats,
manufacturer,
model,
device_version,
serial_number,
})
}
#[must_use]
pub fn supports_operation(&self, operation: OperationCode) -> bool {
self.operations_supported.contains(&operation)
}
#[must_use]
pub fn supports_rename(&self) -> bool {
self.supports_operation(OperationCode::SetObjectPropValue)
}
}
#[derive(Debug, Clone, Default)]
pub struct StorageInfo {
pub storage_type: StorageType,
pub filesystem_type: FilesystemType,
pub access_capability: AccessCapability,
pub max_capacity: u64,
pub free_space_bytes: u64,
pub free_space_objects: u32,
pub description: String,
pub volume_identifier: String,
}
impl StorageInfo {
pub fn from_bytes(buf: &[u8]) -> Result<Self, crate::Error> {
let mut offset = 0;
let storage_type = StorageType::from(unpack_u16(&buf[offset..])?);
offset += 2;
let filesystem_type = FilesystemType::from(unpack_u16(&buf[offset..])?);
offset += 2;
let access_capability = AccessCapability::from(unpack_u16(&buf[offset..])?);
offset += 2;
let max_capacity = unpack_u64(&buf[offset..])?;
offset += 8;
let free_space_bytes = unpack_u64(&buf[offset..])?;
offset += 8;
let free_space_objects = unpack_u32(&buf[offset..])?;
offset += 4;
let (description, consumed) = unpack_string(&buf[offset..])?;
offset += consumed;
let (volume_identifier, _) = unpack_string(&buf[offset..])?;
Ok(StorageInfo {
storage_type,
filesystem_type,
access_capability,
max_capacity,
free_space_bytes,
free_space_objects,
description,
volume_identifier,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ptp::pack::{pack_string, pack_u16, pack_u16_array, pack_u32};
fn build_minimal_device_info_bytes() -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(&pack_u16(100));
buf.extend_from_slice(&pack_u32(0));
buf.extend_from_slice(&pack_u16(0));
buf.push(0x00);
buf.extend_from_slice(&pack_u16(0));
buf.extend_from_slice(&pack_u16_array(&[]));
buf.extend_from_slice(&pack_u16_array(&[]));
buf.extend_from_slice(&pack_u16_array(&[]));
buf.extend_from_slice(&pack_u16_array(&[]));
buf.extend_from_slice(&pack_u16_array(&[]));
buf.push(0x00);
buf.push(0x00);
buf.push(0x00);
buf.push(0x00);
buf
}
#[test]
fn device_info_parse_minimal() {
let buf = build_minimal_device_info_bytes();
let info = DeviceInfo::from_bytes(&buf).unwrap();
assert_eq!(info.standard_version, 100);
assert_eq!(info.vendor_extension_id, 0);
assert_eq!(info.vendor_extension_version, 0);
assert_eq!(info.vendor_extension_desc, "");
assert_eq!(info.functional_mode, 0);
assert!(info.operations_supported.is_empty());
assert!(info.events_supported.is_empty());
assert!(info.device_properties_supported.is_empty());
assert!(info.capture_formats.is_empty());
assert!(info.playback_formats.is_empty());
assert_eq!(info.manufacturer, "");
assert_eq!(info.model, "");
assert_eq!(info.device_version, "");
assert_eq!(info.serial_number, "");
}
fn build_full_device_info_bytes() -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(&pack_u16(100));
buf.extend_from_slice(&pack_u32(6));
buf.extend_from_slice(&pack_u16(100));
buf.extend_from_slice(&pack_string("microsoft.com: 1.0"));
buf.extend_from_slice(&pack_u16(0));
buf.extend_from_slice(&pack_u16_array(&[0x1001, 0x1002, 0x1003]));
buf.extend_from_slice(&pack_u16_array(&[0x4002, 0x4003]));
buf.extend_from_slice(&pack_u16_array(&[0x5001, 0x5002]));
buf.extend_from_slice(&pack_u16_array(&[0x3801]));
buf.extend_from_slice(&pack_u16_array(&[0x3801, 0x3009]));
buf.extend_from_slice(&pack_string("Test Manufacturer"));
buf.extend_from_slice(&pack_string("Test Model"));
buf.extend_from_slice(&pack_string("1.0.0"));
buf.extend_from_slice(&pack_string("ABC123"));
buf
}
#[test]
fn device_info_parse_full() {
let buf = build_full_device_info_bytes();
let info = DeviceInfo::from_bytes(&buf).unwrap();
assert_eq!(info.standard_version, 100);
assert_eq!(info.vendor_extension_id, 6);
assert_eq!(info.vendor_extension_version, 100);
assert_eq!(info.vendor_extension_desc, "microsoft.com: 1.0");
assert_eq!(info.functional_mode, 0);
assert_eq!(info.operations_supported.len(), 3);
assert_eq!(info.operations_supported[0], OperationCode::GetDeviceInfo);
assert_eq!(info.operations_supported[1], OperationCode::OpenSession);
assert_eq!(info.operations_supported[2], OperationCode::CloseSession);
assert_eq!(info.events_supported.len(), 2);
assert_eq!(info.events_supported[0], EventCode::ObjectAdded);
assert_eq!(info.events_supported[1], EventCode::ObjectRemoved);
assert_eq!(info.device_properties_supported, vec![0x5001, 0x5002]);
assert_eq!(info.capture_formats.len(), 1);
assert_eq!(info.capture_formats[0], ObjectFormatCode::Jpeg);
assert_eq!(info.playback_formats.len(), 2);
assert_eq!(info.playback_formats[0], ObjectFormatCode::Jpeg);
assert_eq!(info.playback_formats[1], ObjectFormatCode::Mp3);
assert_eq!(info.manufacturer, "Test Manufacturer");
assert_eq!(info.model, "Test Model");
assert_eq!(info.device_version, "1.0.0");
assert_eq!(info.serial_number, "ABC123");
}
#[test]
fn device_info_parse_insufficient_bytes() {
let buf = vec![0x00, 0x01]; assert!(DeviceInfo::from_bytes(&buf).is_err());
}
fn build_storage_info_bytes() -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(&pack_u16(4));
buf.extend_from_slice(&pack_u16(2));
buf.extend_from_slice(&pack_u16(0));
buf.extend_from_slice(&32_000_000_000u64.to_le_bytes());
buf.extend_from_slice(&16_000_000_000u64.to_le_bytes());
buf.extend_from_slice(&pack_u32(0xFFFFFFFF));
buf.extend_from_slice(&pack_string("SD Card"));
buf.extend_from_slice(&pack_string("VOL001"));
buf
}
#[test]
fn storage_info_parse() {
let buf = build_storage_info_bytes();
let info = StorageInfo::from_bytes(&buf).unwrap();
assert_eq!(info.storage_type, StorageType::RemovableRam);
assert_eq!(info.filesystem_type, FilesystemType::GenericHierarchical);
assert_eq!(info.access_capability, AccessCapability::ReadWrite);
assert_eq!(info.max_capacity, 32_000_000_000);
assert_eq!(info.free_space_bytes, 16_000_000_000);
assert_eq!(info.free_space_objects, 0xFFFFFFFF);
assert_eq!(info.description, "SD Card");
assert_eq!(info.volume_identifier, "VOL001");
}
#[test]
fn storage_info_parse_insufficient_bytes() {
let buf = vec![0x00; 10]; assert!(StorageInfo::from_bytes(&buf).is_err());
}
#[test]
fn device_info_supports_operation() {
let info = DeviceInfo {
operations_supported: vec![
OperationCode::GetDeviceInfo,
OperationCode::OpenSession,
OperationCode::SetObjectPropValue,
],
..Default::default()
};
assert!(info.supports_operation(OperationCode::GetDeviceInfo));
assert!(info.supports_operation(OperationCode::OpenSession));
assert!(info.supports_operation(OperationCode::SetObjectPropValue));
assert!(!info.supports_operation(OperationCode::DeleteObject));
assert!(!info.supports_operation(OperationCode::GetObjectPropValue));
}
#[test]
fn device_info_supports_rename_true() {
let info = DeviceInfo {
operations_supported: vec![
OperationCode::GetDeviceInfo,
OperationCode::SetObjectPropValue, ],
..Default::default()
};
assert!(info.supports_rename());
}
#[test]
fn device_info_supports_rename_false() {
let info = DeviceInfo {
operations_supported: vec![
OperationCode::GetDeviceInfo,
OperationCode::GetObjectPropValue, ],
..Default::default()
};
assert!(!info.supports_rename());
}
#[test]
fn device_info_supports_rename_empty() {
let info = DeviceInfo::default();
assert!(!info.supports_rename());
}
crate::fuzz_bytes!(fuzz_device_info, DeviceInfo, 200);
crate::fuzz_bytes!(fuzz_storage_info, StorageInfo, 100);
#[test]
fn device_info_minimum_valid() {
assert!(DeviceInfo::from_bytes(&[]).is_err());
assert!(DeviceInfo::from_bytes(&[0; 1]).is_err());
assert!(DeviceInfo::from_bytes(&[0; 7]).is_err());
assert!(DeviceInfo::from_bytes(&[0; 8]).is_err()); }
#[test]
fn storage_info_minimum_valid() {
assert!(StorageInfo::from_bytes(&[]).is_err());
assert!(StorageInfo::from_bytes(&[0; 25]).is_err());
assert!(StorageInfo::from_bytes(&[0; 26]).is_err()); }
#[test]
fn storage_info_max_capacity() {
let mut buf = build_storage_info_bytes();
let max_bytes = u64::MAX.to_le_bytes();
buf[6..14].copy_from_slice(&max_bytes);
let info = StorageInfo::from_bytes(&buf).unwrap();
assert_eq!(info.max_capacity, u64::MAX);
}
}