assembly_data/fdb/reader/
mod.rs

1//! Low-Level reader for FDB files
2//!
3//! This module provides a struct which can be used to access
4//! a FDB file in any order the user desires.
5
6pub mod builder;
7
8use std::io::{self, BufRead, Read, Seek, SeekFrom};
9
10use super::{
11    file::{
12        lists::{FDBBucketHeaderList, FDBColumnHeaderList, FDBFieldDataList, FDBTableHeaderList},
13        FDBHeader, FDBRowHeader, FDBRowHeaderListEntry, FDBTableDataHeader, FDBTableDefHeader,
14    },
15    parser::{ParseFDB, ParseLE},
16};
17
18use assembly_core::{
19    nom::{Finish, IResult},
20    reader::{FileError, FileResult},
21};
22use encoding_rs::WINDOWS_1252;
23
24/// Implementation of [`DatabaseReader::get_row_header_addr_iterator`]
25#[allow(clippy::upper_case_acronyms)]
26pub struct FDBRowHeaderAddrIterator<'a, T: ?Sized> {
27    next_addr: u32,
28    file: &'a mut T,
29}
30
31/// Extension trait to `Seek + BufRead` for reading strings
32pub trait DatabaseBufReader
33where
34    Self: Seek + BufRead,
35{
36    /// Read a string from the file
37    fn get_string(&mut self, addr: u32) -> io::Result<String>;
38}
39
40impl<T> DatabaseBufReader for T
41where
42    T: Seek + BufRead,
43{
44    fn get_string(&mut self, addr: u32) -> io::Result<String> {
45        let mut string: Vec<u8> = Vec::new();
46        self.seek(SeekFrom::Start(addr.into()))?;
47        self.read_until(0x00, &mut string)?;
48        if string.ends_with(&[0x00]) {
49            string.pop();
50        }
51        let (decoded, _, _) = WINDOWS_1252.decode(&string);
52        Ok(decoded.into_owned())
53    }
54}
55impl<T> DatabaseReader for T where T: Seek + Read + ?Sized {}
56
57/// Parse a struct at the give offset into the buffer
58fn parse_at<R: Seek + Read + ?Sized, T>(
59    reader: &mut R,
60    addr: impl Into<u64>,
61    buf: &mut [u8],
62    parser: impl Fn(&[u8]) -> IResult<&[u8], T>,
63) -> FileResult<T> {
64    let addr = addr.into();
65    reader.seek(SeekFrom::Start(addr))?;
66    reader.read_exact(buf)?;
67    let (_rest, header) = parser(buf).finish().map_err(|e| FileError::Parse {
68        addr,
69        offset: buf.len() - e.input.len(),
70        code: e.code,
71    })?;
72    Ok(header)
73}
74
75fn bytes<IO: ParseLE>() -> IO::Buf {
76    IO::Buf::default()
77}
78
79fn parse_list_at<R: Seek + Read + ?Sized, T: ParseFDB>(
80    reader: &mut R,
81    addr: impl Into<u64>,
82    count: u32,
83) -> FileResult<Vec<T>> {
84    let addr = addr.into();
85    reader.seek(SeekFrom::Start(addr))?;
86    let buf_len = <T::IO as ParseLE>::BYTE_COUNT;
87    let mut buf = bytes::<T::IO>();
88    let mut offset = 0;
89    let mut list = Vec::with_capacity(count as usize);
90    for _ in 0..count {
91        reader.read_exact(buf.as_mut())?;
92        let (_rest, t) = T::parse(buf.as_mut())
93            .finish()
94            .map_err(|e| FileError::Parse {
95                addr,
96                offset: offset + buf_len - e.input.len(),
97                code: e.code,
98            })?;
99        list.push(t);
100        offset += buf_len;
101    }
102    Ok(list)
103}
104/// Extension to `Seek + Read` to read an FDB file
105pub trait DatabaseReader
106where
107    Self: Seek + Read,
108{
109    /// Read the schema header
110    fn get_header(&mut self) -> FileResult<FDBHeader> {
111        let mut bytes = [0; 8];
112        parse_at(self, 0u64, &mut bytes, FDBHeader::parse)
113    }
114
115    /// Read the table header
116    fn get_table_header_list(&mut self, header: FDBHeader) -> FileResult<FDBTableHeaderList> {
117        let addr = header.tables.base_offset;
118        let count = header.tables.count;
119        parse_list_at(self, addr, count).map(FDBTableHeaderList::from)
120    }
121
122    /// Read the table def header
123    fn get_table_def_header(&mut self, addr: u32) -> FileResult<FDBTableDefHeader> {
124        let mut bytes = [0; std::mem::size_of::<FDBTableDefHeader>()];
125        parse_at(self, addr, &mut bytes, FDBTableDefHeader::parse)
126    }
127
128    /// Get a 64bit integer
129    fn get_i64(&mut self, addr: u32) -> io::Result<i64> {
130        let mut bytes: [u8; 8] = [0; 8];
131        self.seek(SeekFrom::Start(addr.into()))?;
132        self.read_exact(&mut bytes)?;
133        Ok(i64::from_le_bytes(bytes))
134    }
135
136    /// Get the column header list
137    fn get_column_header_list(
138        &mut self,
139        header: &FDBTableDefHeader,
140    ) -> FileResult<FDBColumnHeaderList> {
141        parse_list_at(self, header.column_header_list_addr, header.column_count)
142            .map(FDBColumnHeaderList::from)
143    }
144
145    /// Get the table data header
146    fn get_table_data_header(&mut self, addr: u32) -> FileResult<FDBTableDataHeader> {
147        let mut bytes = bytes::<<FDBTableDataHeader as ParseFDB>::IO>();
148        parse_at(self, addr, &mut bytes, FDBTableDataHeader::parse)
149    }
150
151    /// Get the table bucket header list
152    fn get_bucket_header_list(
153        &mut self,
154        header: &FDBTableDataHeader,
155    ) -> FileResult<FDBBucketHeaderList> {
156        let addr = header.buckets.base_offset;
157        let count = header.buckets.count;
158        parse_list_at(self, addr, count).map(FDBBucketHeaderList::from)
159    }
160
161    /// Get a row header list entry
162    fn get_row_header_list_entry(&mut self, addr: u32) -> FileResult<FDBRowHeaderListEntry> {
163        let mut bytes = [0; std::mem::size_of::<FDBRowHeaderListEntry>()];
164        parse_at(self, addr, &mut bytes, FDBRowHeaderListEntry::parse)
165    }
166
167    /// Get a row header
168    fn get_row_header(&mut self, addr: u32) -> FileResult<FDBRowHeader> {
169        let mut bytes: [u8; 8] = [0; std::mem::size_of::<FDBRowHeader>()];
170        parse_at(self, addr, &mut bytes, FDBRowHeader::parse)
171    }
172
173    /// Returns a vector of `FDBFieldData`
174    fn get_field_data_list(&mut self, header: FDBRowHeader) -> FileResult<FDBFieldDataList> {
175        parse_list_at(self, header.fields.base_offset, header.fields.count)
176            .map(FDBFieldDataList::from)
177    }
178
179    /// Returns an iterator over `FDBRowHeader` offsets
180    fn get_row_header_addr_iterator<'a>(
181        &'a mut self,
182        addr: u32,
183    ) -> FDBRowHeaderAddrIterator<'a, Self> {
184        FDBRowHeaderAddrIterator::<'a> {
185            file: self,
186            next_addr: addr,
187        }
188    }
189}
190
191impl<'a, T> Iterator for FDBRowHeaderAddrIterator<'a, T>
192where
193    T: Read + Seek,
194{
195    type Item = FileResult<u32>;
196
197    fn next(&mut self) -> Option<Self::Item> {
198        match self.next_addr {
199            std::u32::MAX => None,
200            addr => match self.file.get_row_header_list_entry(addr) {
201                Ok(entry) => {
202                    self.next_addr = entry.row_header_list_next_addr;
203                    Some(Ok(entry.row_header_addr))
204                }
205                Err(e) => {
206                    self.next_addr = std::u32::MAX;
207                    Some(Err(e))
208                }
209            },
210        }
211    }
212}