use binrw::io::TakeSeekExt;
use std::io::SeekFrom;
use binrw::prelude::*;
use modular_bitfield::prelude::*;
use crate::packets::{binrw_util::prelude::*, fscc::*};
use super::FileId;
#[binrw::binrw]
#[derive(Debug)]
pub struct QueryDirectoryRequest {
#[bw(calc = 33)]
#[br(assert(_structure_size == 33))]
_structure_size: u16,
pub file_information_class: QueryDirectoryInfoClass,
pub flags: QueryDirectoryFlags,
#[bw(assert(flags.index_specified() || *file_index == 0))]
pub file_index: u32,
pub file_id: FileId,
#[bw(calc = PosMarker::default())]
pub file_name_offset: PosMarker<u16>,
#[bw(try_calc = file_name.size().try_into())]
file_name_length: u16, pub output_buffer_length: u32,
#[br(seek_before = SeekFrom::Start(file_name_offset.value as u64))]
#[br(args(file_name_length as u64))]
#[bw(write_with = PosMarker::write_aoff, args(&file_name_offset))]
pub file_name: SizedWideString,
}
#[bitfield]
#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
#[bw(map = |&x| Self::into_bytes(x))]
#[br(map = Self::from_bytes)]
pub struct QueryDirectoryFlags {
pub restart_scans: bool,
pub return_single_entry: bool,
pub index_specified: bool,
pub reopen: bool,
#[skip]
__: B4,
}
#[binrw::binrw]
#[derive(Debug)]
pub struct QueryDirectoryResponse {
#[bw(calc = 9)]
#[br(assert(_structure_size == 9))]
_structure_size: u16,
#[bw(calc = PosMarker::default())]
output_buffer_offset: PosMarker<u16>,
#[bw(try_calc = output_buffer.len().try_into())]
output_buffer_length: u32,
#[br(seek_before = SeekFrom::Start(output_buffer_offset.value as u64))]
#[br(map_stream = |s| s.take_seek(output_buffer_length as u64), parse_with = binrw::helpers::until_eof)]
#[bw(write_with = PosMarker::write_aoff, args(&output_buffer_offset))]
pub output_buffer: Vec<u8>,
}
impl QueryDirectoryResponse {
pub fn read_output<T>(&self) -> BinResult<Vec<T>>
where
T: QueryDirectoryInfoValue,
{
let mut reader = std::io::Cursor::new(&self.output_buffer);
let mut result = vec![];
while reader.position() < self.output_buffer.len() as u64 {
let item = T::read_le(&mut reader)?;
result.push(item);
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use time::macros::datetime;
use super::*;
#[test]
pub fn test_both_directory_information_attribute_parse() {
let data = [
0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x39, 0x75, 0x91, 0xbf, 0xc8, 0x4b, 0xdb, 0x1,
0xe7, 0xb8, 0x48, 0xcd, 0xc8, 0x5d, 0xdb, 0x1, 0xe7, 0x1b, 0xed, 0xd4, 0x6a, 0x58,
0xdb, 0x1, 0xe7, 0x1b, 0xed, 0xd4, 0x6a, 0x58, 0xdb, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7b,
0x80, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x2e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x70, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x8, 0x38, 0x96, 0xae, 0x4b, 0xdb, 0x1, 0x10, 0x6a,
0x87, 0x4b, 0x49, 0x5d, 0xdb, 0x1, 0x62, 0xc, 0xcd, 0xc1, 0xc8, 0x4b, 0xdb, 0x1, 0x62,
0xc, 0xcd, 0xc1, 0xc8, 0x4b, 0xdb, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0xe7, 0x1, 0x0,
0x0, 0x0, 0x4, 0x0, 0x2e, 0x0, 0x2e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x78, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x5b, 0x6c, 0x44, 0xce, 0x6a, 0x58, 0xdb, 0x1, 0x5b, 0x6c, 0x44, 0xce,
0x6a, 0x58, 0xdb, 0x1, 0x5b, 0x6c, 0x44, 0xce, 0x6a, 0x58, 0xdb, 0x1, 0x5f, 0xd9, 0xd5,
0xce, 0x6a, 0x58, 0xdb, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xa4, 0x0, 0x0, 0x0, 0x0,
0xa, 0x0, 0x61, 0x0, 0x2e, 0x0, 0x74, 0x0, 0x78, 0x0, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd8, 0xce, 0xec, 0xcf, 0x6a, 0x58,
0xdb, 0x1, 0x7e, 0xc, 0x17, 0xd9, 0x6a, 0x58, 0xdb, 0x1, 0x7e, 0xc, 0x17, 0xd9, 0x6a,
0x58, 0xdb, 0x1, 0x7e, 0xc, 0x17, 0xd9, 0x6a, 0x58, 0xdb, 0x1, 0x6, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0xa, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xb9, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x62, 0x0, 0x2e, 0x0, 0x74, 0x0, 0x78, 0x0,
0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x78, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x57,
0x8e, 0x2f, 0xd0, 0x6a, 0x58, 0xdb, 0x1, 0xe2, 0xa8, 0xc1, 0xdd, 0x6a, 0x58, 0xdb, 0x1,
0xe2, 0xa8, 0xc1, 0xdd, 0x6a, 0x58, 0xdb, 0x1, 0xe2, 0xa8, 0xc1, 0xdd, 0x6a, 0x58,
0xdb, 0x1, 0xe6, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe8, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x20, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbb, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x63, 0x0,
0x2e, 0x0, 0x74, 0x0, 0x78, 0x0, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x66, 0x47, 0xd0, 0x6a, 0x58, 0xdb, 0x1, 0x3, 0xc,
0x39, 0x53, 0x49, 0x5d, 0xdb, 0x1, 0x3, 0xc, 0x39, 0x53, 0x49, 0x5d, 0xdb, 0x1, 0x3,
0xc, 0x39, 0x53, 0x49, 0x5d, 0xdb, 0x1, 0x26, 0x3e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbc, 0xf8, 0x0, 0x0,
0x0, 0x0, 0x4, 0x0, 0x64, 0x0, 0x2e, 0x0, 0x74, 0x0, 0x78, 0x0, 0x74, 0x0,
];
let _res: Vec<FileIdBothDirectoryInformation> = QueryDirectoryResponse {
output_buffer: data.to_vec(),
}
.read_output()
.unwrap();
assert_eq!(
vec![
ChainedItem::new(FileIdBothDirectoryInformationInner {
file_index: 0,
creation_time: FileTime::from(datetime!(2024-12-11 12:32:31.7084985)),
last_access_time: FileTime::from(datetime!(2025-01-03 10:18:15.6499175)),
last_write_time: FileTime::from(datetime!(2024-12-27 14:22:59.9648231)),
change_time: FileTime::from(datetime!(2024-12-27 14:22:59.9648231)),
end_of_file: 0,
allocation_size: 0,
file_attributes: FileAttributes::new().with_directory(true),
ea_size: Some(0),
reparse_tag: None,
short_name_length: 0,
short_name: [0; 12],
fild_id: 562949953454203,
file_name: ".".into(),
}),
ChainedItem::new(FileIdBothDirectoryInformationInner {
file_index: 0,
creation_time: FileTime::from(datetime!(2024-12-11 9:25:15.4208828)),
last_access_time: FileTime::from(datetime!(2025-01-02 19:05:31.8723088)),
last_write_time: FileTime::from(datetime!(2024-12-11 12:32:35.4544738)),
change_time: FileTime::from(datetime!(2024-12-11 12:32:35.4544738)),
end_of_file: 0,
allocation_size: 0,
file_attributes: FileAttributes::new().with_directory(true),
ea_size: Some(0),
reparse_tag: None,
short_name_length: 0,
short_name: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,],
fild_id: 1125899906967338,
file_name: "..".into(),
}),
ChainedItem::new(FileIdBothDirectoryInformationInner {
file_index: 0,
creation_time: FileTime::from(datetime!(2024-12-27 14:22:48.7929947)),
last_access_time: FileTime::from(datetime!(2024-12-27 14:22:48.7929947)),
last_write_time: FileTime::from(datetime!(2024-12-27 14:22:48.7929947)),
change_time: FileTime::from(datetime!(2024-12-27 14:22:49.7460575)),
end_of_file: 0,
allocation_size: 0,
file_attributes: FileAttributes::new().with_archive(true),
ea_size: Some(0),
reparse_tag: None,
short_name_length: 0,
short_name: [0; 12],
fild_id: 2814749767148784,
file_name: "a.txt".into(),
}),
ChainedItem::new(FileIdBothDirectoryInformationInner {
file_index: 0,
creation_time: FileTime::from(datetime!(2024-12-27 14:22:51.5742424)),
last_access_time: FileTime::from(datetime!(2024-12-27 14:23:06.9505662)),
last_write_time: FileTime::from(datetime!(2024-12-27 14:23:06.9505662)),
change_time: FileTime::from(datetime!(2024-12-27 14:23:06.9505662)),
end_of_file: 6,
allocation_size: 8,
file_attributes: FileAttributes::new().with_archive(true),
ea_size: Some(0),
reparse_tag: None,
short_name_length: 0,
short_name: [0; 12],
fild_id: 1125899906906297,
file_name: "b.txt".into(),
}),
ChainedItem::new(FileIdBothDirectoryInformationInner {
file_index: 0,
creation_time: FileTime::from(datetime!(2024-12-27 14:22:52.0116823)),
last_access_time: FileTime::from(datetime!(2024-12-27 14:23:14.7795682)),
last_write_time: FileTime::from(datetime!(2024-12-27 14:23:14.7795682)),
change_time: FileTime::from(datetime!(2024-12-27 14:23:14.7795682)),
end_of_file: 486,
allocation_size: 488,
file_attributes: FileAttributes::new().with_archive(true),
ea_size: Some(0),
reparse_tag: None,
short_name_length: 0,
short_name: [0; 12],
fild_id: 1125899906906299,
file_name: "c.txt".into(),
},),
ChainedItem::new(FileIdBothDirectoryInformationInner {
file_index: 0,
creation_time: FileTime::from(datetime!(2024-12-27 14:22:52.167941),),
last_access_time: FileTime::from(datetime!(2025-01-02 19:05:44.7804931),),
last_write_time: FileTime::from(datetime!(2025-01-02 19:05:44.7804931),),
change_time: FileTime::from(datetime!(2025-01-02 19:05:44.7804931),),
end_of_file: 15910,
allocation_size: 16384,
file_attributes: FileAttributes::new().with_archive(true),
ea_size: Some(0),
reparse_tag: None,
short_name_length: 0,
short_name: [0; 12],
fild_id: 1125899906906300,
file_name: "d.txt".into(),
})
],
_res
);
}
}