1use std::borrow::Cow;
45use std::collections::HashMap;
46use std::io::{Read, Seek, BufReader};
47use std::path::Path;
48use nom::{
49 number::complete::{le_u8, le_u32, le_u64},
50 bytes::complete::tag,
51 sequence::tuple,
52};
53use thiserror::Error;
54use cdragon_hashes::rst::compute_rst_hash_full;
55use cdragon_utils::{
56 parsing::{ParseError, ReadArray},
57 parse_buf,
58};
59pub use cdragon_hashes::rst::RstHashMapper;
60
61
62type Result<T, E = RstError> = std::result::Result<T, E>;
64
65#[derive(Debug)]
67pub enum RstRawValue<'a> {
68 String(&'a [u8]),
69 Encrypted(&'a [u8]),
70}
71
72
73pub struct Rst {
78 pub version: u8,
80 pub font_config: Option<String>,
82 hash_bits: u8,
84 has_trenc: bool,
86 entry_offsets: HashMap<u64, usize>,
88 entry_data: Vec<u8>,
90}
91
92impl Rst {
93 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
95 let file = std::fs::File::open(path.as_ref())?;
96 let reader = BufReader::new(file);
97 Rst::read(reader)
98 }
99
100 pub fn read<R: Read + Seek>(mut reader: R) -> Result<Self> {
102 let (version, hash_bits, font_config, entry_count) = Self::parse_header(&mut reader)?;
103
104 let entry_offsets = {
105 let mut entry_offsets = HashMap::with_capacity(entry_count as usize);
106 let mut buf = vec![0; 8 * entry_count as usize];
107 reader.read_exact(&mut buf)?;
108
109 let hash_mask = (1 << hash_bits) - 1;
110 let mut it = nom::combinator::iterator(buf.as_slice(), le_u64);
111 entry_offsets.extend(it
112 .take(entry_count as usize)
113 .map(|v: u64| (v & hash_mask, (v >> hash_bits) as usize))
114 );
115 let result: nom::IResult<_, _, ()> = it.finish();
116 let _ = result.map_err(ParseError::from)?;
117 entry_offsets
118 };
119
120 let has_trenc = version < 5 && reader.read_array::<1>()?[0] != 0;
121
122 let mut entry_data = Vec::new();
123 reader.read_to_end(&mut entry_data)?;
124
125 Ok(Self {
126 version,
127 font_config,
128 hash_bits,
129 has_trenc,
130 entry_offsets,
131 entry_data,
132 })
133 }
134
135 fn parse_header<R: Read + Seek>(reader: &mut R) -> Result<(u8, u8, Option<String>, u32)> {
137 let version = {
138 let buf = reader.read_array::<{3 + 1}>()?;
139 let (_, version) = parse_buf!(buf, tuple((tag("RST"), le_u8)));
140 version
141 };
142
143 let hash_bits: u8 = match version {
144 2 | 3 => 40,
145 4 | 5 => 39,
146 _ => return Err(RstError::UnsupportedVersion(version)),
147 };
148
149 let font_config = if version == 2 && reader.read_array::<1>()?[0] != 0 {
150 let buf = reader.read_array::<4>()?;
151 let n = parse_buf!(buf, le_u32);
152 let mut buf = vec![0; n as usize];
153 reader.read_exact(&mut buf)?;
154 Some(String::from_utf8(buf)?)
155 } else {
156 None
157 };
158
159 let entry_count = {
160 let buf = reader.read_array::<4>()?;
161 parse_buf!(buf, le_u32)
162 };
163
164 Ok((version, hash_bits, font_config, entry_count))
165 }
166
167 pub fn hash_bits(&self) -> u8 {
169 self.hash_bits
170 }
171
172 pub fn truncate_hash_key(&self, key: u64) -> u64 {
174 key & ((1 << self.hash_bits) - 1)
175 }
176
177 pub fn get<K: IntoRstKey>(&self, key: K) -> Option<Cow<'_, str>> {
182 match self.get_raw_by_hash(key.into_rst_key())? {
183 RstRawValue::String(s) => Some(String::from_utf8_lossy(s)),
184 _ => None
185 }
186 }
187
188 pub fn get_raw<K: IntoRstKey>(&self, key: K) -> Option<RstRawValue> {
190 self.get_raw_by_hash(key.into_rst_key())
191 }
192
193 fn get_raw_by_hash(&self, key: u64) -> Option<RstRawValue> {
195 let key = self.truncate_hash_key(key);
196 let offset = *self.entry_offsets.get(&key)?;
197 self.get_raw_by_offset(offset)
198 }
199
200 fn get_raw_by_offset(&self, offset: usize) -> Option<RstRawValue> {
202 let data = &self.entry_data[offset..];
203 if data[0] == 0xff && self.has_trenc {
204 let size = u16::from_le_bytes(data[1..3].try_into().unwrap());
205 Some(RstRawValue::Encrypted(&data[3..3+size as usize]))
206 } else {
207 let pos = data.iter().position(|&b| b == 0)?;
208 Some(RstRawValue::String(&data[..pos]))
209 }
210 }
211
212 pub fn iter(&self) -> impl Iterator<Item=(u64, Cow<'_, str>)> {
214 self.entry_offsets.iter().filter_map(|(key, offset)| {
215 match self.get_raw_by_offset(*offset)? {
216 RstRawValue::String(s) => Some(String::from_utf8_lossy(s)),
217 _ => None
218 }.map(|value| (*key, value))
219 })
220 }
221}
222
223impl std::fmt::Debug for Rst {
224 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225 f.debug_struct("Rst")
226 .field("version", &self.version)
227 .field("font_config", &self.font_config)
228 .field("hash_bits", &self.hash_bits)
229 .field("has_trenc", &self.has_trenc)
230 .field("len", &self.entry_offsets.len())
231 .finish()
232 }
233}
234
235
236pub trait IntoRstKey {
237 fn into_rst_key(self) -> u64;
238}
239
240impl IntoRstKey for u64 {
241 fn into_rst_key(self) -> u64 {
242 self
243 }
244}
245
246impl IntoRstKey for &str {
247 fn into_rst_key(self) -> u64 {
248 compute_rst_hash_full(self)
249 }
250}
251
252
253
254#[allow(missing_docs)]
256#[derive(Error, Debug)]
257pub enum RstError {
258 #[error(transparent)]
259 Io(#[from] std::io::Error),
260 #[error(transparent)]
261 Utf8(#[from] std::string::FromUtf8Error),
262 #[error("parsing error")]
263 Parsing(#[from] ParseError),
264 #[error("version not supported: {0}")]
265 UnsupportedVersion(u8),
266}
267