use std::{
fmt::Display,
io::{Cursor, Read, Seek, SeekFrom},
ptr::NonNull,
};
use vtable_rs::VPtr;
use crate::{
DLVector,
dlio::DLIOResult,
dlkr::{DLAllocator, DLPlainLightMutex},
dltx::DLString,
dlut::DLDateTime,
};
use shared::OwnedPtr;
use super::{DLFileSeekDirection, OpenFileMode};
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum DLFileDeviceDriveType {
Unknown = 0x0,
CdRom = 0x1,
Default = 0x2,
}
#[repr(C)]
pub struct DLFileDeviceBase {
pub vftable: VPtr<dyn DLFileDeviceVmt, Self>,
unk8: bool,
ref_count: u32,
pub mutex: DLPlainLightMutex,
}
#[vtable_rs::vtable]
pub trait DLFileDeviceVmt {
fn destructor(&mut self);
fn get_file_operator(
&mut self,
path_dlstring: &DLString,
path_u16: *const u16,
operator_container: &mut DLFileOperatorContainer,
allocator: &mut DLAllocator,
is_temp_file: bool,
) -> *const DLFileOperatorBase;
fn file_enumerator(&self) -> *const u8;
fn get_drive_type(&self, _path: *const u16) -> DLFileDeviceDriveType {
DLFileDeviceDriveType::Default
}
fn is_encrypted(&self) -> bool {
false
}
}
#[repr(C)]
pub struct DLFileEnumeratorSPIBase {
pub vftable: VPtr<dyn DLFileEnumeratorSPIVmt, Self>,
}
#[vtable_rs::vtable]
pub trait DLFileEnumeratorSPIVmt {
fn destructor(&mut self);
fn start_search(&mut self, search: &[u16], search_handle: &mut u64, found: &mut DLString);
fn close_search(&self, search_handle: &u64);
fn search_next(&mut self, search_handle: &mut u64, found: &mut DLString);
}
#[vtable_rs::vtable]
pub trait DLFileOperatorVmt {
fn destructor(&mut self);
fn copy_from(&mut self, source: &DLFileOperatorBase) -> bool;
fn set_path(&mut self, path: &DLString, param_3: bool, param_4: bool) -> bool;
fn set_path_other_1(&mut self, path: &DLString, param_3: bool, param_4: bool) -> bool;
fn set_path_other_2(&mut self, path: &DLString, param_3: bool, param_4: bool) -> bool;
fn set_state(&mut self, param_2: bool, param_3: bool) -> bool;
fn clear_file_info(&mut self) -> bool;
fn get_virtual_disk_operator(&self) -> *const DLFileOperatorBase;
fn bind_device_image(
&mut self,
image_spi: &DLFileDeviceImageSPIBase,
) -> *const DLFileDeviceImageSPIBase;
fn is_readable(&mut self) -> bool;
fn is_writable(&mut self) -> bool;
fn last_access_time(&self, ptr: *const DLDateTime) -> *const DLDateTime;
fn last_modify_time(&self, ptr: *const DLDateTime) -> *const DLDateTime;
fn file_size(&mut self) -> usize;
fn get_read_size(&mut self) -> usize;
fn get_write_size(&self) -> usize;
fn set_eof(&mut self);
fn is_eof(&self) -> bool;
fn is_directory(&self) -> bool;
fn is_open(&self) -> bool;
fn open(&mut self, open_mode: OpenFileMode) -> bool;
fn close(&mut self) -> bool;
fn set_read_only(&mut self, is_open: bool) -> bool;
fn seek(&mut self, is_stream: bool, offset: i64, seek_mode: DLFileSeekDirection) -> bool;
fn cursor_position(&mut self) -> usize;
unsafe fn read(&mut self, output: *mut u8, length: usize) -> i32;
fn write(&mut self, input: *const u8, length: usize) -> usize;
fn get_async_block_size(&self) -> usize;
fn get_async_buffer_alignment_size(&self) -> usize;
unsafe fn start_async_read(&mut self, output: *mut u8, length: usize) -> bool;
fn start_async_write(&mut self, input: *const u8, length: usize) -> bool;
fn query_async_status(
&mut self,
bytes_remaining: &mut usize,
bytes_transferred: Option<&mut usize>,
) -> bool;
fn get_open_mode(&self) -> OpenFileMode;
fn delete(&mut self) -> bool;
fn flush(&mut self);
fn populate_file_info(&mut self) -> bool;
fn unk2(&mut self) -> bool;
fn rename_w(&mut self, path: *const u16) -> bool;
fn rename(&mut self, path: *const u8) -> bool;
fn create_directory(&mut self) -> bool;
}
#[repr(C)]
#[derive(Default, Clone, Copy)]
pub struct DLFileOperatorIOState(pub u32);
impl DLFileOperatorIOState {
pub fn is_open(&self) -> bool {
self.0 & 0x1 != 0
}
pub fn unk1(&self) -> bool {
self.0 & 0x2 != 0
}
pub fn unk2(&self) -> bool {
self.0 & 0x4 != 0
}
}
impl Display for DLFileOperatorIOState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"DLFileOperatorIOState {{ is_open: {}, unk1: {}, unk2: {} }}",
self.is_open(),
self.unk1(),
self.unk2()
)
}
}
#[repr(C)]
pub struct DLFileOperatorBase<T: DLFileOperatorVmt = AdapterFileOperator<Cursor<Vec<u8>>>> {
pub vftable: VPtr<dyn DLFileOperatorVmt, T>,
pub allocator: &'static DLAllocator,
pub result: DLIOResult,
pub owning_operator_container: NonNull<DLFileOperatorContainer>,
pub io_state: DLFileOperatorIOState,
pub owning_file_device: NonNull<DLFileDeviceBase>,
pub path: DLString,
}
impl<T> DLFileOperatorBase<T>
where
T: DLFileOperatorVmt,
{
pub fn new(
vftable: VPtr<dyn DLFileOperatorVmt, T>,
allocator: &'static DLAllocator,
path: &DLString,
operator_container: &DLFileOperatorContainer,
file_device: &DLFileDeviceBase,
) -> Self {
Self {
vftable,
allocator,
result: DLIOResult::Success,
owning_operator_container: NonNull::from(operator_container),
io_state: DLFileOperatorIOState::default(),
owning_file_device: NonNull::from(file_device),
path: DLString::transcode_from(path, allocator).expect("Failed to copy DLString"),
}
}
}
#[vtable_rs::vtable]
pub trait DLFileDeviceImageSPIVmt {
fn destructor(&mut self);
}
#[repr(C)]
pub struct DLFileDeviceImageSPIBase {
pub vftable: VPtr<dyn DLFileDeviceImageSPIVmt, Self>,
}
#[repr(C)]
pub struct BndEntry {
pub name: DLString,
pub device: NonNull<DLFileDeviceBase>,
pub file_size: u64,
}
#[repr(C)]
pub struct DLFileOperatorContainer {
allocator: &'static DLAllocator,
read_file_operator: OwnedPtr<DLFileOperatorBase>,
write_file_operator: OwnedPtr<DLFileOperatorBase>,
flags: u32,
}
#[repr(C)]
pub struct DLFileDeviceManager {
pub devices: DLVector<NonNull<DLFileDeviceBase>>,
pub service_providers: DLVector<NonNull<DLFileDeviceImageSPIBase>>,
pub msvc_file_device: OwnedPtr<DLFileDeviceBase>,
pub virtual_roots: DLVector<[DLString; 2]>,
pub bnd3_files: DLVector<BndEntry>,
pub bnd4_files: DLVector<BndEntry>,
pub bnd3_service_provider: OwnedPtr<DLFileDeviceImageSPIBase>,
pub bnd4_service_provider: OwnedPtr<DLFileDeviceImageSPIBase>,
pub mutex: DLPlainLightMutex,
}
impl DLFileDeviceVmt for DLFileDeviceBase {
extern "C" fn destructor(&mut self) {
(self.vftable.destructor)(self);
}
extern "C" fn get_file_operator(
&mut self,
name_dlstring: &DLString,
name_u16: *const u16,
operator_container: &mut DLFileOperatorContainer,
allocator: &mut DLAllocator,
is_temp_file: bool,
) -> *const DLFileOperatorBase {
(self.vftable.get_file_operator)(
self,
name_dlstring,
name_u16,
operator_container,
allocator,
is_temp_file,
)
}
extern "C" fn file_enumerator(&self) -> *const u8 {
(self.vftable.file_enumerator)(self)
}
extern "C" fn get_drive_type(&self, path: *const u16) -> DLFileDeviceDriveType {
(self.vftable.get_drive_type)(self, path)
}
extern "C" fn is_encrypted(&self) -> bool {
(self.vftable.is_encrypted)(self)
}
}
#[repr(C)]
pub struct AdapterFileOperator<R>
where
R: Read + Seek + 'static,
{
pub base: DLFileOperatorBase,
buffer: R,
}
impl<R> AdapterFileOperator<R>
where
R: Read + Seek + 'static,
{
pub fn new(
allocator: &'static DLAllocator,
path: &DLString,
operator_container: &DLFileOperatorContainer,
file_device: &DLFileDeviceBase,
buffer: R,
) -> Self {
Self {
base: DLFileOperatorBase::new(
Default::default(),
allocator,
path,
operator_container,
file_device,
),
buffer,
}
}
}
impl<R> Display for AdapterFileOperator<R>
where
R: Read + Seek + 'static,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "AdapterFileOperator({})", self.base.path)
}
}
impl<R> DLFileOperatorVmt for AdapterFileOperator<R>
where
R: Read + Seek + 'static,
{
extern "C" fn destructor(&mut self) {
unsafe {
std::ptr::drop_in_place(self);
}
}
extern "C" fn copy_from(&mut self, _source: &DLFileOperatorBase) -> bool {
unimplemented!()
}
extern "C" fn set_path(&mut self, path: &DLString, param_3: bool, param_4: bool) -> bool {
self.base.io_state.0 &= 0xfffffff9;
self.base.io_state.0 |= (((param_4 as u32 & 1) * 2) | (param_3 as u32 & 1)) * 2;
self.base.path =
DLString::transcode_from(path, self.base.allocator).expect("Failed to copy DLString");
true
}
extern "C" fn set_path_other_1(
&mut self,
_path: &DLString,
_param_3: bool,
_param_4: bool,
) -> bool {
unimplemented!()
}
extern "C" fn set_path_other_2(
&mut self,
_path: &DLString,
_param_3: bool,
_param_4: bool,
) -> bool {
unimplemented!()
}
extern "C" fn set_state(&mut self, param_2: bool, param_3: bool) -> bool {
self.base.io_state.0 &= 0xfffffff9;
self.base.io_state.0 |= (((param_3 as u32 & 1) * 2) | (param_2 as u32 & 1)) * 2;
true
}
extern "C" fn clear_file_info(&mut self) -> bool {
unimplemented!()
}
extern "C" fn get_virtual_disk_operator(&self) -> *const DLFileOperatorBase {
unimplemented!()
}
extern "C" fn bind_device_image(
&mut self,
_image_spi: &DLFileDeviceImageSPIBase,
) -> *const DLFileDeviceImageSPIBase {
unimplemented!()
}
extern "C" fn is_readable(&mut self) -> bool {
unimplemented!()
}
extern "C" fn is_writable(&mut self) -> bool {
unimplemented!()
}
extern "C" fn last_access_time(&self, _ptr: *const DLDateTime) -> *const DLDateTime {
unimplemented!()
}
extern "C" fn last_modify_time(&self, _ptr: *const DLDateTime) -> *const DLDateTime {
unimplemented!()
}
extern "C" fn file_size(&mut self) -> usize {
let current = self.buffer.stream_position().unwrap();
let end = self.buffer.seek(SeekFrom::End(0)).unwrap() as usize;
let _ = self.buffer.seek(SeekFrom::Start(current));
end
}
extern "C" fn get_read_size(&mut self) -> usize {
unimplemented!()
}
extern "C" fn get_write_size(&self) -> usize {
unimplemented!()
}
extern "C" fn set_eof(&mut self) {
unimplemented!()
}
extern "C" fn is_eof(&self) -> bool {
unimplemented!()
}
extern "C" fn is_directory(&self) -> bool {
unimplemented!()
}
extern "C" fn is_open(&self) -> bool {
true
}
extern "C" fn open(&mut self, _open_mode: OpenFileMode) -> bool {
true
}
extern "C" fn close(&mut self) -> bool {
true
}
extern "C" fn set_read_only(&mut self, _is_open: bool) -> bool {
unimplemented!()
}
extern "C" fn seek(
&mut self,
_is_stream: bool,
offset: i64,
seek_mode: DLFileSeekDirection,
) -> bool {
let seek_from = match seek_mode {
DLFileSeekDirection::Head => SeekFrom::Start(offset as u64),
DLFileSeekDirection::Current => SeekFrom::Current(offset),
DLFileSeekDirection::Tail => SeekFrom::End(offset),
};
self.buffer.seek(seek_from).is_ok()
}
extern "C" fn cursor_position(&mut self) -> usize {
self.buffer.stream_position().unwrap_or(0) as usize
}
unsafe extern "C" fn read(&mut self, output: *mut u8, length: usize) -> i32 {
let mut buffer = vec![0x0u8; length];
self.buffer.read_exact(&mut buffer).unwrap();
unsafe { std::ptr::copy_nonoverlapping(buffer.as_ptr(), output, length) };
self.base.result = DLIOResult::Success;
length as i32
}
extern "C" fn write(&mut self, _input: *const u8, _length: usize) -> usize {
unimplemented!()
}
extern "C" fn get_async_block_size(&self) -> usize {
unimplemented!()
}
extern "C" fn get_async_buffer_alignment_size(&self) -> usize {
unimplemented!()
}
unsafe extern "C" fn start_async_read(&mut self, _output: *mut u8, _length: usize) -> bool {
unimplemented!()
}
extern "C" fn start_async_write(&mut self, _input: *const u8, _length: usize) -> bool {
unimplemented!()
}
extern "C" fn query_async_status(
&mut self,
_bytes_remaining: &mut usize,
_bytes_transferred: Option<&mut usize>,
) -> bool {
unimplemented!()
}
extern "C" fn get_open_mode(&self) -> OpenFileMode {
unimplemented!()
}
extern "C" fn delete(&mut self) -> bool {
unimplemented!()
}
extern "C" fn flush(&mut self) {
unimplemented!()
}
extern "C" fn populate_file_info(&mut self) -> bool {
unimplemented!()
}
extern "C" fn unk2(&mut self) -> bool {
unimplemented!()
}
extern "C" fn rename_w(&mut self, _path: *const u16) -> bool {
unimplemented!()
}
extern "C" fn rename(&mut self, _path: *const u8) -> bool {
unimplemented!()
}
extern "C" fn create_directory(&mut self) -> bool {
unimplemented!()
}
}