1use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
4use std::io::{self, Cursor, Read, Seek, SeekFrom, Write};
5
6use super::{block_id::*, read_write::*, root::*, *};
7use crate::{crc::compute_crc, AnsiPstFile, PstFile, UnicodePstFile};
8
9const HEADER_MAGIC: u32 = u32::from_be_bytes(*b"NDB!");
14
15const HEADER_MAGIC_CLIENT: u16 = u16::from_be_bytes(*b"MS");
16
17#[repr(u16)]
22#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
23pub enum NdbVersion {
24 Ansi = 15,
25 #[default]
26 Unicode = 23,
27}
28
29impl TryFrom<u16> for NdbVersion {
30 type Error = NdbError;
31
32 fn try_from(value: u16) -> Result<Self, Self::Error> {
33 match value {
34 14..=15 => Ok(NdbVersion::Ansi),
35 23 => Ok(NdbVersion::Unicode),
36 _ => Err(NdbError::InvalidNdbVersion(value)),
37 }
38 }
39}
40
41const NDB_CLIENT_VERSION: u16 = 19;
42const NDB_PLATFORM_CREATE: u8 = 0x01;
43const NDB_PLATFORM_ACCESS: u8 = 0x01;
44const NDB_DEFAULT_NIDS: [u32; 32] = [
45 0x400, 0x400, 0x400, 0x4000, 0x10000, 0x400, 0x400, 0x400, 0x8000, 0x400, 0x400, 0x400, 0x400,
46 0x400, 0x400, 0x400, 0x400, 0x400, 0x400, 0x400, 0x400, 0x400, 0x400, 0x400, 0x400, 0x400,
47 0x400, 0x400, 0x400, 0x400, 0x400, 0x400,
48];
49const NDB_SENTINEL: u8 = 0x80;
50
51#[repr(u8)]
56#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
57pub enum NdbCryptMethod {
58 #[default]
60 None = 0x00,
61 Permute = 0x01,
63 Cyclic = 0x02,
65}
66
67impl TryFrom<u8> for NdbCryptMethod {
68 type Error = NdbError;
69
70 fn try_from(value: u8) -> Result<Self, Self::Error> {
71 match value {
72 0x00 => Ok(NdbCryptMethod::None),
73 0x01 => Ok(NdbCryptMethod::Permute),
74 0x02 => Ok(NdbCryptMethod::Cyclic),
75 _ => Err(NdbError::InvalidNdbCryptMethod(value)),
76 }
77 }
78}
79
80pub trait Header<Pst>: Clone
81where
82 Pst: PstFile,
83{
84 fn version(&self) -> NdbVersion;
85 fn crypt_method(&self) -> NdbCryptMethod;
86 fn next_block(&self) -> <Pst as PstFile>::BlockId;
87 fn next_page(&self) -> <Pst as PstFile>::PageId;
88 fn unique_value(&self) -> u32;
89 fn root(&self) -> &<Pst as PstFile>::Root;
90 fn root_mut(&mut self) -> &mut <Pst as PstFile>::Root;
91}
92
93#[derive(Clone, Debug)]
94pub struct UnicodeHeader {
95 next_page: UnicodePageId,
96 unique: u32,
97 nids: [u32; 32],
98 root: UnicodeRoot,
99 free_map: [u8; 128],
100 free_page_map: [u8; 128],
101 crypt_method: NdbCryptMethod,
102 next_block: UnicodeBlockId,
103
104 reserved1: u32,
105 reserved2: u32,
106 unused1: u64,
107 unused2: u64,
108 reserved3: [u8; 36],
109}
110
111impl UnicodeHeader {
112 pub fn new(root: UnicodeRoot, crypt_method: NdbCryptMethod) -> Self {
113 Self {
114 next_page: UnicodePageId::from(1),
115 unique: 0,
116 nids: NDB_DEFAULT_NIDS,
117 root,
118 free_map: [0xFF; 128],
119 free_page_map: [0xFF; 128],
120 crypt_method,
121 next_block: UnicodeBlockId::from(4),
122 reserved1: 0,
123 reserved2: 0,
124 unused1: 0,
125 unused2: 0,
126 reserved3: [0; 36],
127 }
128 }
129}
130
131impl Header<UnicodePstFile> for UnicodeHeader {
132 fn version(&self) -> NdbVersion {
133 NdbVersion::Unicode
134 }
135
136 fn crypt_method(&self) -> NdbCryptMethod {
137 self.crypt_method
138 }
139
140 fn next_block(&self) -> <UnicodePstFile as PstFile>::BlockId {
141 self.next_block
142 }
143
144 fn next_page(&self) -> <UnicodePstFile as PstFile>::PageId {
145 self.next_page
146 }
147
148 fn unique_value(&self) -> u32 {
149 self.unique
150 }
151
152 fn root(&self) -> &<UnicodePstFile as PstFile>::Root {
153 &self.root
154 }
155
156 fn root_mut(&mut self) -> &mut <UnicodePstFile as PstFile>::Root {
157 &mut self.root
158 }
159}
160
161impl HeaderReadWrite<UnicodePstFile> for UnicodeHeader {
162 fn read(f: &mut dyn Read) -> io::Result<Self> {
163 let magic = f.read_u32::<LittleEndian>()?;
165 if magic != HEADER_MAGIC {
166 return Err(NdbError::InvalidNdbHeaderMagicValue(magic).into());
167 }
168
169 let crc_partial = f.read_u32::<LittleEndian>()?;
171
172 let mut crc_data = [0_u8; 516];
173 f.read_exact(&mut crc_data[..471])?;
174 if crc_partial != compute_crc(0, &crc_data[..471]) {
175 return Err(NdbError::InvalidNdbHeaderPartialCrc(crc_partial).into());
176 }
177
178 let mut cursor = Cursor::new(crc_data);
179
180 let magic = cursor.read_u16::<LittleEndian>()?;
182 if magic != HEADER_MAGIC_CLIENT {
183 return Err(NdbError::InvalidNdbHeaderMagicClientValue(magic).into());
184 }
185
186 let version = NdbVersion::try_from(cursor.read_u16::<LittleEndian>()?)?;
188 if version != NdbVersion::Unicode {
189 return Err(NdbError::AnsiPstVersion(version as u16).into());
190 }
191
192 let mut crc_data = cursor.into_inner();
193 f.read_exact(&mut crc_data[471..])?;
194
195 let crc_full = f.read_u32::<LittleEndian>()?;
197 if crc_full != compute_crc(0, &crc_data) {
198 return Err(NdbError::InvalidNdbHeaderFullCrc(crc_full).into());
199 }
200
201 let mut cursor = Cursor::new(crc_data);
202 cursor.seek(SeekFrom::Start(4))?;
203
204 let version = cursor.read_u16::<LittleEndian>()?;
206 if version != NDB_CLIENT_VERSION {
207 return Err(NdbError::InvalidNdbHeaderClientVersion(version).into());
208 }
209
210 let platform_create = cursor.read_u8()?;
212 if platform_create != NDB_PLATFORM_CREATE {
213 return Err(NdbError::InvalidNdbHeaderPlatformCreate(platform_create).into());
214 }
215
216 let platform_access = cursor.read_u8()?;
218 if platform_access != NDB_PLATFORM_ACCESS {
219 return Err(NdbError::InvalidNdbHeaderPlatformAccess(platform_access).into());
220 }
221
222 let reserved1 = cursor.read_u32::<LittleEndian>()?;
224
225 let reserved2 = cursor.read_u32::<LittleEndian>()?;
227
228 let unused1 = cursor.read_u64::<LittleEndian>()?;
230
231 let next_page = UnicodePageId::read(&mut cursor)?;
233
234 let unique = cursor.read_u32::<LittleEndian>()?;
236
237 let mut nids = [0_u32; 32];
239 for nid in nids.iter_mut() {
240 *nid = cursor.read_u32::<LittleEndian>()?;
241 }
242
243 let unused2 = cursor.read_u64::<LittleEndian>()?;
245
246 let root = UnicodeRoot::read(&mut cursor)?;
248
249 let align = cursor.read_u32::<LittleEndian>()?;
251 if align != 0 {
252 return Err(NdbError::InvalidNdbHeaderAlignValue(align).into());
253 }
254
255 let mut free_map = [0; 128];
257 cursor.read_exact(&mut free_map)?;
258
259 let mut free_page_map = [0; 128];
261 cursor.read_exact(&mut free_page_map)?;
262
263 let sentinel = cursor.read_u8()?;
265 if sentinel != NDB_SENTINEL {
266 return Err(NdbError::InvalidNdbHeaderSentinelValue(sentinel).into());
267 }
268
269 let crypt_method = NdbCryptMethod::try_from(cursor.read_u8()?)?;
271
272 let reserved = cursor.read_u16::<LittleEndian>()?;
274 if reserved != 0 {
275 return Err(NdbError::InvalidNdbHeaderReservedValue(reserved).into());
276 }
277
278 let next_block = UnicodeBlockId::read(&mut cursor)?;
280
281 let mut reserved3 = [0_u8; 36];
283 f.read_exact(&mut reserved3)?;
284
285 Ok(Self {
286 next_page,
287 unique,
288 nids,
289 root,
290 free_map,
291 free_page_map,
292 crypt_method,
293 next_block,
294 reserved1,
295 reserved2,
296 unused1,
297 unused2,
298 reserved3,
299 })
300 }
301
302 fn write(&self, f: &mut dyn Write) -> io::Result<()> {
303 let mut cursor = Cursor::new([0_u8; 516]);
304 cursor.write_u16::<LittleEndian>(HEADER_MAGIC_CLIENT)?;
306 cursor.write_u16::<LittleEndian>(NdbVersion::Unicode as u16)?;
308 cursor.write_u16::<LittleEndian>(NDB_CLIENT_VERSION)?;
310 cursor.write_u8(NDB_PLATFORM_CREATE)?;
312 cursor.write_u8(NDB_PLATFORM_ACCESS)?;
314 cursor.write_u32::<LittleEndian>(self.reserved1)?;
316 cursor.write_u32::<LittleEndian>(self.reserved2)?;
318 cursor.write_u64::<LittleEndian>(self.unused1)?;
320 self.next_page.write(&mut cursor)?;
322 cursor.write_u32::<LittleEndian>(self.unique)?;
324 for nid in self.nids.iter() {
326 cursor.write_u32::<LittleEndian>(*nid)?;
327 }
328 cursor.write_u64::<LittleEndian>(self.unused2)?;
330 self.root.write(&mut cursor)?;
332 cursor.write_u32::<LittleEndian>(0)?;
334 cursor.write_all(&self.free_map)?;
336 cursor.write_all(&self.free_page_map)?;
338 cursor.write_u8(NDB_SENTINEL)?;
340 cursor.write_u8(self.crypt_method as u8)?;
342 cursor.write_u16::<LittleEndian>(0)?;
344 self.next_block.write(&mut cursor)?;
346
347 let crc_data = cursor.into_inner();
348 let crc_partial = compute_crc(0, &crc_data[..471]);
349 let crc_full = compute_crc(0, &crc_data);
350
351 f.write_u32::<LittleEndian>(HEADER_MAGIC)?;
353 f.write_u32::<LittleEndian>(crc_partial)?;
355
356 f.write_all(&crc_data)?;
357
358 f.write_u32::<LittleEndian>(crc_full)?;
360
361 f.write_all(&self.reserved3)
363 }
364
365 fn update_unique(&mut self) {
366 self.unique = self.unique.wrapping_add(1);
367 }
368
369 fn first_free_map(&mut self) -> &mut [u8] {
370 &mut self.free_map
371 }
372
373 fn first_free_page_map(&mut self) -> &mut [u8] {
374 &mut self.free_page_map
375 }
376}
377
378#[derive(Clone, Debug)]
379pub struct AnsiHeader {
380 next_block: AnsiBlockId,
381 next_page: AnsiPageId,
382 unique: u32,
383 nids: [u32; 32],
384 root: AnsiRoot,
385 free_map: [u8; 128],
386 free_page_map: [u8; 128],
387 crypt_method: NdbCryptMethod,
388
389 reserved1: u32,
390 reserved2: u32,
391 reserved3: [u8; 36],
392}
393
394impl AnsiHeader {
395 pub fn new(root: AnsiRoot, crypt_method: NdbCryptMethod) -> Self {
396 Self {
397 next_block: AnsiBlockId::from(4),
398 next_page: AnsiPageId::from(1),
399 unique: 0,
400 nids: NDB_DEFAULT_NIDS,
401 root,
402 free_map: [0xFF; 128],
403 free_page_map: [0xFF; 128],
404 crypt_method,
405 reserved1: 0,
406 reserved2: 0,
407 reserved3: [0; 36],
408 }
409 }
410}
411
412impl Header<AnsiPstFile> for AnsiHeader {
413 fn version(&self) -> NdbVersion {
414 NdbVersion::Ansi
415 }
416
417 fn crypt_method(&self) -> NdbCryptMethod {
418 self.crypt_method
419 }
420
421 fn next_block(&self) -> <AnsiPstFile as PstFile>::BlockId {
422 self.next_block
423 }
424
425 fn next_page(&self) -> <AnsiPstFile as PstFile>::PageId {
426 self.next_page
427 }
428
429 fn unique_value(&self) -> u32 {
430 self.unique
431 }
432
433 fn root(&self) -> &<AnsiPstFile as PstFile>::Root {
434 &self.root
435 }
436
437 fn root_mut(&mut self) -> &mut <AnsiPstFile as PstFile>::Root {
438 &mut self.root
439 }
440}
441
442impl HeaderReadWrite<AnsiPstFile> for AnsiHeader {
443 fn read(f: &mut dyn Read) -> io::Result<Self> {
444 let magic = f.read_u32::<LittleEndian>()?;
446 if magic != HEADER_MAGIC {
447 return Err(NdbError::InvalidNdbHeaderMagicValue(magic).into());
448 }
449
450 let crc_partial = f.read_u32::<LittleEndian>()?;
452
453 let mut crc_data = [0_u8; 504];
454 f.read_exact(&mut crc_data)?;
455 if crc_partial != compute_crc(0, &crc_data[..471]) {
456 return Err(NdbError::InvalidNdbHeaderPartialCrc(crc_partial).into());
457 }
458
459 let mut cursor = Cursor::new(crc_data);
460
461 let magic = cursor.read_u16::<LittleEndian>()?;
463 if magic != HEADER_MAGIC_CLIENT {
464 return Err(NdbError::InvalidNdbHeaderMagicClientValue(magic).into());
465 }
466
467 let version = NdbVersion::try_from(cursor.read_u16::<LittleEndian>()?)?;
469 if version != NdbVersion::Ansi {
470 return Err(NdbError::UnicodePstVersion(version as u16).into());
471 }
472
473 let version = cursor.read_u16::<LittleEndian>()?;
475 if version != NDB_CLIENT_VERSION {
476 return Err(NdbError::InvalidNdbHeaderClientVersion(version).into());
477 }
478
479 let platform_create = cursor.read_u8()?;
481 if platform_create != NDB_PLATFORM_CREATE {
482 return Err(NdbError::InvalidNdbHeaderPlatformCreate(platform_create).into());
483 }
484
485 let platform_access = cursor.read_u8()?;
487 if platform_access != NDB_PLATFORM_ACCESS {
488 return Err(NdbError::InvalidNdbHeaderPlatformAccess(platform_access).into());
489 }
490
491 let reserved1 = cursor.read_u32::<LittleEndian>()?;
493
494 let reserved2 = cursor.read_u32::<LittleEndian>()?;
496
497 let next_block = AnsiBlockId::read(&mut cursor)?;
499
500 let next_page = AnsiPageId::read(&mut cursor)?;
502
503 let unique = cursor.read_u32::<LittleEndian>()?;
505
506 let mut nids = [0_u32; 32];
508 for nid in nids.iter_mut() {
509 *nid = cursor.read_u32::<LittleEndian>()?;
510 }
511
512 let root = AnsiRoot::read(&mut cursor)?;
514
515 let mut free_map = [0; 128];
517 cursor.read_exact(&mut free_map)?;
518
519 let mut free_page_map = [0; 128];
521 cursor.read_exact(&mut free_page_map)?;
522
523 let sentinel = cursor.read_u8()?;
525 if sentinel != NDB_SENTINEL {
526 return Err(NdbError::InvalidNdbHeaderSentinelValue(sentinel).into());
527 }
528
529 let crypt_method = NdbCryptMethod::try_from(cursor.read_u8()?)?;
531
532 let reserved = cursor.read_u16::<LittleEndian>()?;
534 if reserved != 0 {
535 return Err(NdbError::InvalidNdbHeaderReservedValue(reserved).into());
536 }
537
538 let mut reserved = [0_u8; 12];
540 cursor.read_exact(&mut reserved)?;
541 if reserved != [0; 12] {
542 return Err(NdbError::InvalidNdbHeaderAnsiReservedBytes.into());
543 }
544
545 let mut reserved3 = [0_u8; 36];
547 cursor.read_exact(&mut reserved3)?;
548
549 Ok(Self {
550 next_page,
551 unique,
552 nids,
553 root,
554 free_map,
555 free_page_map,
556 crypt_method,
557 next_block,
558 reserved1,
559 reserved2,
560 reserved3,
561 })
562 }
563
564 fn write(&self, f: &mut dyn Write) -> io::Result<()> {
565 let mut cursor = Cursor::new([0_u8; 504]);
566 cursor.write_u16::<LittleEndian>(HEADER_MAGIC_CLIENT)?;
568 cursor.write_u16::<LittleEndian>(NdbVersion::Ansi as u16)?;
570 cursor.write_u16::<LittleEndian>(NDB_CLIENT_VERSION)?;
572 cursor.write_u8(NDB_PLATFORM_CREATE)?;
574 cursor.write_u8(NDB_PLATFORM_ACCESS)?;
576 cursor.write_u32::<LittleEndian>(self.reserved1)?;
578 cursor.write_u32::<LittleEndian>(self.reserved2)?;
580 self.next_block.write(&mut cursor)?;
582 self.next_page.write(&mut cursor)?;
584 cursor.write_u32::<LittleEndian>(self.unique)?;
586 for nid in self.nids.iter() {
588 cursor.write_u32::<LittleEndian>(*nid)?;
589 }
590 self.root.write(&mut cursor)?;
592 cursor.write_all(&self.free_map)?;
594 cursor.write_all(&self.free_page_map)?;
596 cursor.write_u8(NDB_SENTINEL)?;
598 cursor.write_u8(self.crypt_method as u8)?;
600 cursor.write_u16::<LittleEndian>(0)?;
602 cursor.write_all(&[0_u8; 12])?;
604 cursor.write_all(&self.reserved3)?;
606
607 let crc_data = cursor.into_inner();
608 let crc_partial = compute_crc(0, &crc_data[..471]);
609
610 f.write_u32::<LittleEndian>(HEADER_MAGIC)?;
612 f.write_u32::<LittleEndian>(crc_partial)?;
614
615 f.write_all(&crc_data)
616 }
617
618 fn update_unique(&mut self) {
619 self.unique = self.unique.wrapping_add(1);
620 }
621
622 fn first_free_map(&mut self) -> &mut [u8] {
623 &mut self.free_map
624 }
625
626 fn first_free_page_map(&mut self) -> &mut [u8] {
627 &mut self.free_page_map
628 }
629}
630
631#[cfg(test)]
632mod tests {
633 use super::*;
634
635 #[test]
636 fn test_magic_values() {
637 assert_eq!(HEADER_MAGIC, 0x4E444221);
638 assert_eq!(HEADER_MAGIC_CLIENT, 0x4D53);
639 }
640}