Implement AFC file seek (#28)

* Implement AFC file seek

* refactored to be more readable
This commit is contained in:
Abdullah Al-Banna
2025-09-21 19:29:08 +03:00
committed by GitHub
parent 23c8808ae7
commit 224fabfa69

View File

@@ -1,5 +1,7 @@
// Jackson Coxson
use std::io::SeekFrom;
use crate::IdeviceError;
use super::{
@@ -19,28 +21,76 @@ pub struct FileDescriptor<'a> {
}
impl FileDescriptor<'_> {
/// Closes the file descriptor
pub async fn close(self) -> Result<(), IdeviceError> {
let header_payload = self.fd.to_le_bytes().to_vec();
/// Generic helper to send an AFC packet and read the response
async fn send_packet(
&mut self,
opcode: AfcOpcode,
header_payload: Vec<u8>,
payload: Vec<u8>,
) -> Result<AfcPacket, IdeviceError> {
let header_len = header_payload.len() as u64 + AfcPacketHeader::LEN;
let header = AfcPacketHeader {
magic: super::MAGIC,
entire_len: header_len,
entire_len: header_len + payload.len() as u64,
header_payload_len: header_len,
packet_num: self.client.package_number,
operation: AfcOpcode::FileClose,
operation: opcode,
};
self.client.package_number += 1;
let packet = AfcPacket {
header,
header_payload,
payload: Vec::new(),
payload,
};
self.client.send(packet).await?;
self.client.read().await?;
self.client.read().await
}
/// Returns the current cursor position for the file
pub async fn seek_tell(&mut self) -> Result<u64, IdeviceError> {
let header_payload = self.fd.to_le_bytes().to_vec();
let res = self
.send_packet(AfcOpcode::FileTell, header_payload, Vec::new())
.await?;
let cur_pos = res
.header_payload
.get(..8)
.ok_or(IdeviceError::UnexpectedResponse)?
.try_into()
.map(u64::from_le_bytes)
.map_err(|_| IdeviceError::UnexpectedResponse)?;
Ok(cur_pos)
}
/// Moves the file cursor
pub async fn seek(&mut self, pos: SeekFrom) -> Result<(), IdeviceError> {
let (offset, whence) = match pos {
SeekFrom::Start(off) => (off as i64, 0),
SeekFrom::Current(off) => (off, 1),
SeekFrom::End(off) => (off, 2),
};
let mut header_payload = Vec::new();
header_payload.extend(self.fd.to_le_bytes());
header_payload.extend((whence as u64).to_le_bytes());
header_payload.extend(offset.to_le_bytes());
self.send_packet(AfcOpcode::FileSeek, header_payload, Vec::new())
.await?;
Ok(())
}
/// Closes the file descriptor
pub async fn close(mut self) -> Result<(), IdeviceError> {
let header_payload = self.fd.to_le_bytes().to_vec();
self.send_packet(AfcOpcode::FileClose, header_payload, Vec::new())
.await?;
Ok(())
}
@@ -49,32 +99,18 @@ impl FileDescriptor<'_> {
/// # Returns
/// A vector containing the file's data
pub async fn read(&mut self) -> Result<Vec<u8>, IdeviceError> {
// Get the file size first
let mut bytes_left = self.client.get_file_info(&self.path).await?.size;
let seek_pos = self.seek_tell().await? as usize;
let file_info = self.client.get_file_info(&self.path).await?;
let mut bytes_left = file_info.size.saturating_sub(seek_pos);
let mut collected_bytes = Vec::with_capacity(bytes_left);
while bytes_left > 0 {
let mut header_payload = self.fd.to_le_bytes().to_vec();
header_payload.extend_from_slice(&MAX_TRANSFER.to_le_bytes());
let header_len = header_payload.len() as u64 + AfcPacketHeader::LEN;
let res = self
.send_packet(AfcOpcode::Read, header_payload, Vec::new())
.await?;
let header = AfcPacketHeader {
magic: super::MAGIC,
entire_len: header_len,
header_payload_len: header_len,
packet_num: self.client.package_number,
operation: AfcOpcode::Read,
};
self.client.package_number += 1;
let packet = AfcPacket {
header,
header_payload,
payload: Vec::new(),
};
self.client.send(packet).await?;
let res = self.client.read().await?;
bytes_left -= res.payload.len();
collected_bytes.extend(res.payload);
}
@@ -87,29 +123,10 @@ impl FileDescriptor<'_> {
/// # Arguments
/// * `bytes` - Data to write to the file
pub async fn write(&mut self, bytes: &[u8]) -> Result<(), IdeviceError> {
let chunks = bytes.chunks(MAX_TRANSFER as usize);
for chunk in chunks {
for chunk in bytes.chunks(MAX_TRANSFER as usize) {
let header_payload = self.fd.to_le_bytes().to_vec();
let header_len = header_payload.len() as u64 + AfcPacketHeader::LEN;
let header = AfcPacketHeader {
magic: super::MAGIC,
entire_len: header_len + chunk.len() as u64,
header_payload_len: header_len,
packet_num: self.client.package_number,
operation: AfcOpcode::Write,
};
self.client.package_number += 1;
let packet = AfcPacket {
header,
header_payload,
payload: chunk.to_vec(),
};
self.client.send(packet).await?;
self.client.read().await?;
self.send_packet(AfcOpcode::Write, header_payload, chunk.to_vec())
.await?;
}
Ok(())
}