use base64::engine::general_purpose::STANDARD as Base64Standard;
use base64::Engine as _;
use binrw::io::Cursor;
use binrw::BinRead;
use std::cell::RefCell;
use std::collections::VecDeque;
mod decoder;
mod error;
pub mod frame;
pub mod keychain;
pub mod layout;
pub mod record;
mod utils;
pub use error::{Error, Result};
use frame::{records_to_frames, Frame};
use keychain::{EncodedKeychainFeaturePoint, Keychain, KeychainFeaturePoint, KeychainsRequest};
use layout::auxiliary::{Auxiliary, Department};
use layout::details::Details;
use layout::prefix::Prefix;
use record::Record;
use crate::utils::pad_with_zeros;
#[derive(Debug)]
pub struct DJILog {
inner: Vec<u8>,
prefix: Prefix,
pub version: u8,
pub details: Details,
}
impl DJILog {
pub fn from_bytes(bytes: Vec<u8>) -> Result<DJILog> {
let mut prefix = Prefix::read(&mut Cursor::new(&bytes))?;
let version = prefix.version;
let detail_offset = prefix.detail_offset() as usize;
let mut cursor = Cursor::new(pad_with_zeros(&bytes[detail_offset..], 400));
let details = if version < 13 {
Details::read_args(&mut cursor, (version,))?
} else {
if let Auxiliary::Info(data) = Auxiliary::read(&mut cursor)? {
Details::read_args(&mut Cursor::new(&data.info_data), (version,))?
} else {
return Err(Error::MissingAuxilliaryData("Info".into()));
}
};
if prefix.records_offset() == 0 && version >= 13 {
let _ = Auxiliary::read(&mut cursor)?;
prefix.recover_detail_offset(cursor.position() + detail_offset as u64);
}
Ok(DJILog {
inner: bytes,
prefix,
version,
details,
})
}
pub fn keychains_request(&self) -> Result<KeychainsRequest> {
self.keychains_request_with_custom_params(None, None)
}
pub fn keychains_request_with_custom_params(
&self,
department: Option<Department>,
version: Option<u16>,
) -> Result<KeychainsRequest> {
let mut keychain_request = KeychainsRequest::default();
if self.version < 13 {
return Ok(keychain_request);
}
let mut cursor = Cursor::new(&self.inner);
cursor.set_position(self.prefix.detail_offset());
let _ = Auxiliary::read(&mut cursor)?;
if let Auxiliary::Version(data) = Auxiliary::read(&mut cursor)? {
keychain_request.version = version.unwrap_or(data.version);
keychain_request.department = match department {
Some(dept) => dept.into(),
None => match data.department {
Department::Unknown(_) => Department::DJIFly.into(),
_ => data.department.into(),
},
};
} else {
return Err(Error::MissingAuxilliaryData("Version".into()));
}
cursor.set_position(self.prefix.records_offset());
let mut keychain: Vec<EncodedKeychainFeaturePoint> = Vec::new();
while cursor.position() < self.prefix.records_end_offset(self.inner.len() as u64) {
let empty_keychain = &RefCell::new(Keychain::empty());
let record = match Record::read_args(
&mut cursor,
binrw::args! {
version: self.version,
keychain: empty_keychain
},
) {
Ok(record) => record,
Err(_) => break,
};
match record {
Record::KeyStorage(data) => {
keychain.push(EncodedKeychainFeaturePoint {
feature_point: data.feature_point,
aes_ciphertext: Base64Standard.encode(&data.data),
});
}
Record::KeyStorageRecover(_) => {
keychain_request.keychains.push(keychain);
keychain = Vec::new();
}
_ => {}
}
}
keychain_request.keychains.push(keychain);
Ok(keychain_request)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn fetch_keychains(&self, api_key: &str) -> Result<Vec<Vec<KeychainFeaturePoint>>> {
if self.version >= 13 {
self.keychains_request()?.fetch(api_key, None)
} else {
Ok(Vec::new())
}
}
#[cfg(any(target_arch = "wasm32", feature = "native-async"))]
pub async fn fetch_keychains_async(
&self,
api_key: &str,
) -> Result<Vec<Vec<KeychainFeaturePoint>>> {
if self.version >= 13 {
self.keychains_request()?.fetch_async(api_key, None).await
} else {
Ok(Vec::new())
}
}
pub fn records(
&self,
keychains: Option<Vec<Vec<KeychainFeaturePoint>>>,
) -> Result<Vec<Record>> {
if self.version >= 13 && keychains.is_none() {
return Err(Error::KeychainRequired);
}
let mut keychains = VecDeque::from(match keychains {
Some(keychains) => keychains
.iter()
.map(Keychain::from_feature_points)
.collect(),
None => Vec::new(),
});
let mut cursor = Cursor::new(&self.inner);
cursor.set_position(self.prefix.records_offset());
let mut keychain = RefCell::new(keychains.pop_front().unwrap_or(Keychain::empty()));
let mut records = Vec::new();
while cursor.position() < self.prefix.records_end_offset(self.inner.len() as u64) {
let record = match Record::read_args(
&mut cursor,
binrw::args! {
version: self.version,
keychain: &keychain
},
) {
Ok(record) => record,
Err(_) => break,
};
if let Record::KeyStorageRecover(_) = record {
keychain = RefCell::new(keychains.pop_front().unwrap_or(Keychain::empty()));
}
records.push(record);
}
Ok(records)
}
pub fn frames(&self, keychains: Option<Vec<Vec<KeychainFeaturePoint>>>) -> Result<Vec<Frame>> {
let records = self.records(keychains)?;
Ok(records_to_frames(records, self.details.clone()))
}
}