1use crate::{
10 ArrayDesc, ConstValue, CustDataEntry, CustDataIter, Error, FuncIter, FuncRecord, Guid,
11 GuidEntryIter, ImpFile, ImpFileIter, ImpInfo, ImpInfoIter, ImplTypeIter, NameIter, ParamIter,
12 ParameterInfo, TypeInfoEntry, TypeInfoIter, VarIter,
13 util::{
14 read_f32_le, read_f64_le, read_i16_le, read_i32_le, read_i64_le, read_u16_le, read_u32_le,
15 read_u64_le,
16 },
17};
18
19#[derive(Debug)]
22pub enum ResolvedHreftype<'a> {
23 Internal(usize),
25 External {
27 imp_info: ImpInfo<'a>,
29 imp_file: Option<ImpFile<'a>>,
31 },
32}
33
34const SEG_TYPEINFO: usize = 0;
36const SEG_IMPINFO: usize = 1;
38const SEG_IMPFILES: usize = 2;
40const SEG_REFTAB: usize = 3;
42const SEG_GUIDHASH: usize = 4;
44const SEG_GUIDTAB: usize = 5;
46const SEG_NAMEHASH: usize = 6;
48const SEG_NAMETAB: usize = 7;
50const SEG_STRINGTAB: usize = 8;
52const SEG_TYPDESC: usize = 9;
54const SEG_ARRAYDESC: usize = 10;
56const SEG_CUSTDATA: usize = 11;
58const SEG_CDGUIDS: usize = 12;
60const NUM_SEGMENTS: usize = 15;
62
63pub struct TypeLib<'a> {
80 data: &'a [u8],
82 nrtypeinfos: usize,
84 offset_table_start: usize,
86 seg_offsets: [(i32, i32); NUM_SEGMENTS],
88}
89
90impl<'a> TypeLib<'a> {
91 pub const HEADER_SIZE: usize = 0x54;
93 pub const MAGIC: u32 = 0x5446534D;
95 pub const DISPATCH_HREF_OFFSET: u32 = 0x0100_0000;
97
98 const SEGDIR_ENTRY_SIZE: usize = 16;
100
101 pub fn parse(data: &'a [u8]) -> Result<Self, Error> {
116 if data.len() < Self::HEADER_SIZE {
117 return Err(Error::TooShort {
118 expected: Self::HEADER_SIZE,
119 actual: data.len(),
120 context: "MSFT header",
121 });
122 }
123
124 let magic = read_u32_le(data, 0x00).ok_or(Error::InvalidData {
125 context: "cannot read magic",
126 })?;
127 if magic != Self::MAGIC {
128 return Err(Error::BadMagic {
129 expected: Self::MAGIC,
130 got: magic,
131 });
132 }
133
134 let nrtypeinfos = read_i32_le(data, 0x20).ok_or(Error::InvalidData {
135 context: "cannot read nrtypeinfos",
136 })? as usize;
137
138 let offset_table_start = Self::HEADER_SIZE;
143 let segdir_start = offset_table_start
144 .checked_add(nrtypeinfos.checked_mul(4).ok_or(Error::InvalidData {
145 context: "typeinfo count overflow",
146 })?)
147 .and_then(|v| v.checked_add(4))
148 .ok_or(Error::InvalidData {
149 context: "segdir offset overflow",
150 })?;
151 let segdir_end = segdir_start
152 .checked_add(NUM_SEGMENTS * Self::SEGDIR_ENTRY_SIZE)
153 .ok_or(Error::InvalidData {
154 context: "segdir size overflow",
155 })?;
156
157 if data.len() < segdir_end {
158 return Err(Error::TooShort {
159 expected: segdir_end,
160 actual: data.len(),
161 context: "segment directory",
162 });
163 }
164
165 let mut seg_offsets = [(0i32, 0i32); NUM_SEGMENTS];
166 for (i, entry) in seg_offsets.iter_mut().enumerate() {
167 let pos = segdir_start + i * Self::SEGDIR_ENTRY_SIZE;
168 *entry = (
169 read_i32_le(data, pos).unwrap_or(-1),
170 read_i32_le(data, pos + 4).unwrap_or(0),
171 );
172 }
173
174 Ok(TypeLib {
175 data,
176 nrtypeinfos,
177 offset_table_start,
178 seg_offsets,
179 })
180 }
181
182 #[inline]
184 pub fn data(&self) -> &'a [u8] {
185 self.data
186 }
187
188 #[inline]
190 pub fn format_version(&self) -> u32 {
191 read_u32_le(self.data, 0x04).unwrap_or(0)
192 }
193
194 #[inline]
196 pub fn lib_guid_offset(&self) -> i32 {
197 read_i32_le(self.data, 0x08).unwrap_or(-1)
198 }
199
200 #[inline]
202 pub fn lcid(&self) -> u32 {
203 read_u32_le(self.data, 0x0C).unwrap_or(0)
204 }
205
206 #[inline]
208 pub fn lcid2(&self) -> u32 {
209 read_u32_le(self.data, 0x10).unwrap_or(0)
210 }
211
212 #[inline]
217 pub fn varflags(&self) -> u32 {
218 read_u32_le(self.data, 0x14).unwrap_or(0)
219 }
220
221 #[inline]
225 pub fn syskind(&self) -> u8 {
226 (self.varflags() & 0x0F) as u8
227 }
228
229 #[inline]
232 pub fn has_help_dll(&self) -> bool {
233 self.varflags() & 0x100 != 0
234 }
235
236 #[inline]
240 pub fn version(&self) -> u32 {
241 read_u32_le(self.data, 0x18).unwrap_or(0)
242 }
243
244 #[inline]
246 pub fn version_major(&self) -> u16 {
247 (self.version() & 0xFFFF) as u16
248 }
249
250 #[inline]
252 pub fn version_minor(&self) -> u16 {
253 (self.version() >> 16) as u16
254 }
255
256 #[inline]
258 pub fn flags(&self) -> u32 {
259 read_u32_le(self.data, 0x1C).unwrap_or(0)
260 }
261
262 #[inline]
264 pub fn typeinfo_count(&self) -> usize {
265 self.nrtypeinfos
266 }
267
268 #[inline]
270 pub fn helpstring_offset(&self) -> i32 {
271 read_i32_le(self.data, 0x24).unwrap_or(-1)
272 }
273
274 #[inline]
276 pub fn helpstringcontext(&self) -> i32 {
277 read_i32_le(self.data, 0x28).unwrap_or(0)
278 }
279
280 #[inline]
282 pub fn helpcontext(&self) -> i32 {
283 read_i32_le(self.data, 0x2C).unwrap_or(0)
284 }
285
286 #[inline]
288 pub fn nametablecount(&self) -> i32 {
289 read_i32_le(self.data, 0x30).unwrap_or(0)
290 }
291
292 #[inline]
294 pub fn nametablechars(&self) -> i32 {
295 read_i32_le(self.data, 0x34).unwrap_or(0)
296 }
297
298 #[inline]
300 pub fn name_offset(&self) -> i32 {
301 read_i32_le(self.data, 0x38).unwrap_or(-1)
302 }
303
304 #[inline]
306 pub fn helpfile_offset(&self) -> i32 {
307 read_i32_le(self.data, 0x3C).unwrap_or(-1)
308 }
309
310 #[inline]
312 pub fn custom_data_offset(&self) -> i32 {
313 read_i32_le(self.data, 0x40).unwrap_or(-1)
314 }
315
316 #[inline]
321 pub fn dispatchpos(&self) -> i32 {
322 read_i32_le(self.data, 0x4C).unwrap_or(-1)
323 }
324
325 #[inline]
327 pub fn nimpinfos(&self) -> i32 {
328 read_i32_le(self.data, 0x50).unwrap_or(0)
329 }
330
331 pub fn segment(&self, index: usize) -> Option<(usize, usize)> {
335 let &(offset, length) = self.seg_offsets.get(index)?;
336 if offset < 0 || length <= 0 {
337 return None;
338 }
339 Some((offset as usize, length as usize))
340 }
341
342 pub fn segment_data(&self, index: usize) -> Option<&'a [u8]> {
346 let (offset, length) = self.segment(index)?;
347 let end = offset.checked_add(length)?;
348 self.data.get(offset..end)
349 }
350
351 pub fn guid_hash_data(&self) -> Option<&'a [u8]> {
353 self.segment_data(SEG_GUIDHASH)
354 }
355
356 pub fn name_hash_data(&self) -> Option<&'a [u8]> {
358 self.segment_data(SEG_NAMEHASH)
359 }
360
361 pub fn name(&self, offset: i32) -> Option<&'a str> {
366 if offset < 0 {
367 return None;
368 }
369 let (seg_offset, _) = self.segment(SEG_NAMETAB)?;
370 let abs = seg_offset.checked_add(offset as usize)?;
371 let namelen = *self.data.get(abs.checked_add(8)?)? as usize;
373 let name_start = abs.checked_add(12)?;
374 let name_end = name_start.checked_add(namelen)?;
375 std::str::from_utf8(self.data.get(name_start..name_end)?).ok()
376 }
377
378 pub fn guid(&self, offset: i32) -> Option<Guid<'a>> {
383 if offset < 0 {
384 return None;
385 }
386 let (seg_offset, _) = self.segment(SEG_GUIDTAB)?;
387 let abs = seg_offset.checked_add(offset as usize)?;
388 let end = abs.checked_add(16)?;
389 Some(Guid::new(self.data.get(abs..end)?))
390 }
391
392 pub fn string(&self, offset: i32) -> Option<&'a str> {
397 if offset < 0 {
398 return None;
399 }
400 let (seg_offset, _) = self.segment(SEG_STRINGTAB)?;
401 let abs = seg_offset.checked_add(offset as usize)?;
402 let len = read_u16_le(self.data, abs)? as usize;
403 let start = abs.checked_add(2)?;
404 let end = start.checked_add(len)?;
405 std::str::from_utf8(self.data.get(start..end)?).ok()
406 }
407
408 pub fn lib_name(&self) -> Option<&'a str> {
410 self.name(self.name_offset())
411 }
412
413 pub fn lib_guid(&self) -> Option<Guid<'a>> {
415 self.guid(self.lib_guid_offset())
416 }
417
418 pub fn lib_helpstring(&self) -> Option<&'a str> {
420 self.string(self.helpstring_offset())
421 }
422
423 pub fn typeinfo_offset(&self, index: usize) -> Option<i32> {
427 if index >= self.nrtypeinfos {
428 return None;
429 }
430 let pos = self.offset_table_start.checked_add(index.checked_mul(4)?)?;
431 read_i32_le(self.data, pos)
432 }
433
434 pub fn member_names(&self, index: usize) -> Vec<&'a str> {
439 let ti_offset = match self.typeinfo_offset(index) {
440 Some(o) => o,
441 None => return Vec::new(),
442 };
443 let mut names: Vec<&'a str> = Vec::new();
444 for entry in self.names() {
445 if entry.hreftype() == ti_offset {
446 names.push(entry.name());
447 }
448 }
449 if !names.is_empty() {
450 names.remove(0);
451 }
452 names
453 }
454
455 pub fn typeinfo(&self, index: usize) -> Result<TypeInfoEntry<'a>, Error> {
464 if index >= self.nrtypeinfos {
465 return Err(Error::TypeInfoOutOfRange {
466 index,
467 count: self.nrtypeinfos,
468 });
469 }
470 let offset_pos =
471 self.offset_table_start
472 .checked_add(index * 4)
473 .ok_or(Error::InvalidData {
474 context: "offset table position overflow",
475 })?;
476 let ti_offset = read_i32_le(self.data, offset_pos).ok_or(Error::InvalidData {
477 context: "cannot read typeinfo offset",
478 })?;
479 if ti_offset < 0 {
480 return Err(Error::TypeInfoOutOfRange {
481 index,
482 count: self.nrtypeinfos,
483 });
484 }
485 let (seg_offset, _) = self.segment(SEG_TYPEINFO).ok_or(Error::InvalidData {
486 context: "typeinfo segment missing",
487 })?;
488 let abs = seg_offset
489 .checked_add(ti_offset as usize)
490 .ok_or(Error::InvalidData {
491 context: "typeinfo absolute offset overflow",
492 })?;
493 if abs + TypeInfoEntry::SIZE > self.data.len() {
494 return Err(Error::TooShort {
495 expected: TypeInfoEntry::SIZE,
496 actual: self.data.len().saturating_sub(abs),
497 context: "TypeInfoBase",
498 });
499 }
500 Ok(TypeInfoEntry::new(
501 &self.data[abs..abs + TypeInfoEntry::SIZE],
502 ))
503 }
504
505 pub fn typeinfos(&'a self) -> TypeInfoIter<'a> {
507 TypeInfoIter::new(self)
508 }
509
510 pub fn funcs(&self, ti: &TypeInfoEntry<'_>) -> FuncIter<'a> {
515 let empty = FuncIter::new(self.data, 0, 0, 0);
516 let memoffset = ti.memoffset();
517 let func_count = ti.func_count();
518 if memoffset < 0 || func_count == 0 {
519 return empty;
520 }
521 let block_start = memoffset as usize;
522 let block_size = match read_u32_le(self.data, block_start) {
523 Some(v) => v as usize,
524 None => return empty,
525 };
526 let funcs_start = match block_start.checked_add(4) {
527 Some(v) => v,
528 None => return empty,
529 };
530 let end = match funcs_start.checked_add(block_size) {
531 Some(v) => v,
532 None => return empty,
533 };
534 FuncIter::new(self.data, funcs_start, end, func_count as usize)
535 }
536
537 pub fn vars(&self, ti: &TypeInfoEntry<'_>) -> VarIter<'a> {
545 let empty = VarIter::new(self.data, 0, 0, 0);
546 let memoffset = ti.memoffset();
547 let var_count = ti.var_count();
548 if memoffset < 0 || var_count == 0 {
549 return empty;
550 }
551 let block_start = memoffset as usize;
552 let block_size = match read_u32_le(self.data, block_start) {
553 Some(v) => v as usize,
554 None => return empty,
555 };
556 let mut pos = match block_start.checked_add(4) {
557 Some(v) => v,
558 None => return empty,
559 };
560 let end = match pos.checked_add(block_size) {
561 Some(v) => v,
562 None => return empty,
563 };
564 for _ in 0..ti.func_count() {
566 let size = match read_u32_le(self.data, pos) {
567 Some(v) => (v & 0xFFFF) as usize,
568 None => return empty,
569 };
570 if size == 0 {
571 break;
572 }
573 pos = match pos.checked_add(size) {
574 Some(v) => v,
575 None => return empty,
576 };
577 }
578 VarIter::new(self.data, pos, end, var_count as usize)
579 }
580
581 pub fn params(&self, func: &FuncRecord<'a>) -> ParamIter<'a> {
586 let nrargs = func.nrargs().max(0) as usize;
587 if nrargs == 0 {
588 return ParamIter::new(&[], 0, 0);
589 }
590 let record_len = func.as_bytes().len();
591 let params_size = match nrargs.checked_mul(ParameterInfo::SIZE) {
592 Some(v) => v,
593 None => return ParamIter::new(&[], 0, 0),
594 };
595 if params_size > record_len {
596 return ParamIter::new(&[], 0, 0);
597 }
598 ParamIter::new(func.as_bytes(), record_len - params_size, nrargs)
599 }
600
601 fn aux_start(&self, ti: &TypeInfoEntry<'_>) -> Option<usize> {
620 let memoffset = ti.memoffset();
621 if memoffset < 0 {
622 return None;
623 }
624 let block_start = memoffset as usize;
625 let block_size = read_u32_le(self.data, block_start)? as usize;
626 block_start.checked_add(4)?.checked_add(block_size)
627 }
628
629 fn read_aux_i32(&self, ti: &TypeInfoEntry<'_>, array_index: usize) -> Option<i32> {
635 let aux = self.aux_start(ti)?;
636 let pos = aux.checked_add(array_index.checked_mul(4)?)?;
637 read_i32_le(self.data, pos)
638 }
639
640 pub fn func_memid(&self, ti: &TypeInfoEntry<'_>, index: usize) -> Option<i32> {
646 self.read_aux_i32(ti, index)
647 }
648
649 pub fn var_memid(&self, ti: &TypeInfoEntry<'_>, index: usize) -> Option<i32> {
655 let fc = ti.func_count() as usize;
656 self.read_aux_i32(ti, fc.checked_add(index)?)
657 }
658
659 pub fn func_name(&self, ti: &TypeInfoEntry<'_>, index: usize) -> Option<&'a str> {
664 let fc = ti.func_count() as usize;
665 let vc = ti.var_count() as usize;
666 let array_idx = fc.checked_add(vc)?.checked_add(index)?;
667 let name_offset = self.read_aux_i32(ti, array_idx)?;
668 self.name(name_offset)
669 }
670
671 pub fn var_name(&self, ti: &TypeInfoEntry<'_>, index: usize) -> Option<&'a str> {
676 let fc = ti.func_count() as usize;
677 let vc = ti.var_count() as usize;
678 let array_idx = fc.checked_mul(2)?.checked_add(vc)?.checked_add(index)?;
679 let name_offset = self.read_aux_i32(ti, array_idx)?;
680 self.name(name_offset)
681 }
682
683 pub fn const_value(&self, offs_value: i32) -> Option<ConstValue<'a>> {
695 if offs_value < 0 {
696 let raw = offs_value as u32;
697 let vt = ((raw & 0x7C00_0000) >> 26) as u16;
698 let val = (raw & 0x03FF_FFFF) as i32;
699 return Some(match vt {
700 0 => ConstValue::Empty,
701 1 => ConstValue::Null,
702 2 => ConstValue::I2(val as i16),
703 3 => ConstValue::I4(val),
704 10 => ConstValue::Error(val),
705 11 => ConstValue::Bool(val != 0),
706 16 => ConstValue::I1(val as i8),
707 17 => ConstValue::UI1(val as u8),
708 18 => ConstValue::UI2(val as u16),
709 19 => ConstValue::UI4(val as u32),
710 22 => ConstValue::Int(val),
711 23 => ConstValue::UInt(val as u32),
712 24 => ConstValue::Void,
713 25 => ConstValue::Hresult(val),
714 _ => ConstValue::I4(val),
715 });
716 }
717 let (seg_offset, _) = self.segment(SEG_CUSTDATA)?;
718 let abs = seg_offset.checked_add(offs_value as usize)?;
719 let vt = read_u16_le(self.data, abs)?;
720 Some(match vt {
721 0 => ConstValue::Empty,
722 1 => ConstValue::Null,
723 2 => ConstValue::I2(read_i32_le(self.data, abs + 2)? as i16),
724 3 => ConstValue::I4(read_i32_le(self.data, abs + 2)?),
725 4 => ConstValue::R4(read_f32_le(self.data, abs + 2)?),
726 5 => ConstValue::R8(read_f64_le(self.data, abs + 2)?),
727 6 => ConstValue::Cy(read_i64_le(self.data, abs + 2)?),
728 7 => ConstValue::Date(read_f64_le(self.data, abs + 2)?),
729 8 => {
730 let len_raw = read_i32_le(self.data, abs + 2)?;
731 if len_raw < 0 {
732 return None;
733 }
734 let len = len_raw as usize;
735 let start = abs.checked_add(6)?;
736 let end = start.checked_add(len)?;
737 ConstValue::Bstr(self.data.get(start..end)?)
738 }
739 10 => ConstValue::Error(read_i32_le(self.data, abs + 2)?),
740 11 => ConstValue::Bool(read_i16_le(self.data, abs + 2)? != 0),
741 14 => {
742 let start = abs.checked_add(2)?;
743 let end = start.checked_add(16)?;
744 ConstValue::Decimal(self.data.get(start..end)?)
745 }
746 16 => ConstValue::I1(read_i32_le(self.data, abs + 2)? as i8),
747 17 => ConstValue::UI1(read_i32_le(self.data, abs + 2)? as u8),
748 18 => ConstValue::UI2(read_i32_le(self.data, abs + 2)? as u16),
749 19 => ConstValue::UI4(read_u32_le(self.data, abs + 2)?),
750 20 => ConstValue::I8(read_i64_le(self.data, abs + 2)?),
751 21 => ConstValue::UI8(read_u64_le(self.data, abs + 2)?),
752 22 => ConstValue::Int(read_i32_le(self.data, abs + 2)?),
753 23 => ConstValue::UInt(read_u32_le(self.data, abs + 2)?),
754 24 => ConstValue::Void,
755 25 => ConstValue::Hresult(read_i32_le(self.data, abs + 2)?),
756 64 => ConstValue::Filetime(read_u64_le(self.data, abs + 2)?),
757 _ => ConstValue::Raw {
758 vt,
759 bits: read_i32_le(self.data, abs + 2)?,
760 },
761 })
762 }
763
764 pub fn type_desc(&self, offset: i32) -> Option<(i32, i32)> {
774 if offset < 0 {
775 return None;
776 }
777 let (seg_offset, _) = self.segment(SEG_TYPDESC)?;
778 let abs = seg_offset.checked_add(offset as usize)?;
779 Some((
780 read_i32_le(self.data, abs)?,
781 read_i32_le(self.data, abs + 4)?,
782 ))
783 }
784
785 pub fn resolve_hreftype(&self, hreftype: i32) -> Option<usize> {
795 if hreftype < 0 {
796 return None;
797 }
798 let index = hreftype as usize / TypeInfoEntry::SIZE;
799 if index < self.nrtypeinfos {
800 Some(index)
801 } else {
802 None
803 }
804 }
805
806 pub fn resolve_hreftype_full(&self, hreftype: i32) -> Option<ResolvedHreftype<'a>> {
813 if hreftype < 0 {
814 return None;
815 }
816 let effective = if (hreftype as u32) & Self::DISPATCH_HREF_OFFSET != 0 {
818 (hreftype as u32 & !Self::DISPATCH_HREF_OFFSET) as i32
819 } else {
820 hreftype
821 };
822
823 let index = effective as usize / TypeInfoEntry::SIZE;
825 if index < self.nrtypeinfos {
826 return Some(ResolvedHreftype::Internal(index));
827 }
828
829 let imp_byte_offset = (effective & !0x03) as usize;
831 let (seg_offset, seg_len) = self.segment(SEG_IMPINFO)?;
832 if imp_byte_offset + ImpInfo::SIZE > seg_len {
833 return None;
834 }
835 let abs = seg_offset + imp_byte_offset;
836 let imp_info = ImpInfo::new(&self.data[abs..abs + ImpInfo::SIZE]);
837 let imp_file = self.imp_file(imp_info.imp_file_offset());
838 Some(ResolvedHreftype::External { imp_info, imp_file })
839 }
840
841 pub fn guids(&self) -> GuidEntryIter<'a> {
843 let (offset, length) = self.segment(SEG_GUIDTAB).unwrap_or((0, 0));
844 GuidEntryIter::new(self.data, offset, offset.saturating_add(length))
845 }
846
847 pub fn names(&self) -> NameIter<'a> {
849 let (offset, length) = self.segment(SEG_NAMETAB).unwrap_or((0, 0));
850 NameIter::new(self.data, offset, offset.saturating_add(length), offset)
851 }
852
853 pub fn impl_types(&self, ti: &TypeInfoEntry<'_>) -> ImplTypeIter<'a> {
858 let ref_offset = ti.res2();
859 let (seg_offset, _) = self.segment(SEG_REFTAB).unwrap_or((0, 0));
860 ImplTypeIter::new(self.data, seg_offset, ref_offset)
861 }
862
863 pub fn imports(&self) -> ImpInfoIter<'a> {
865 let (offset, length) = self.segment(SEG_IMPINFO).unwrap_or((0, 0));
866 ImpInfoIter::new(self.data, offset, offset.saturating_add(length))
867 }
868
869 pub fn imp_files(&self) -> ImpFileIter<'a> {
872 let (offset, length) = self.segment(SEG_IMPFILES).unwrap_or((0, 0));
873 ImpFileIter::new(self.data, offset, offset.saturating_add(length))
874 }
875
876 pub fn imp_file(&self, offset: i32) -> Option<ImpFile<'a>> {
884 if offset < 0 {
885 return None;
886 }
887 let (seg_offset, seg_len) = self.segment(SEG_IMPFILES)?;
888 let abs = seg_offset.checked_add(offset as usize)?;
889 if abs.checked_add(ImpFile::HEADER_SIZE)? > seg_offset.checked_add(seg_len)? {
890 return None;
891 }
892 let size_field = read_u16_le(self.data, abs + 0x0C)?;
893 let name_len = (size_field >> 2) as usize;
894 let entry_size = (ImpFile::HEADER_SIZE + name_len + 3) & !3;
895 let end = abs.checked_add(entry_size)?;
896 if end > self.data.len() {
897 return None;
898 }
899 Some(ImpFile::new(&self.data[abs..end]))
900 }
901
902 pub fn array_desc(&self, offset: i32) -> Option<ArrayDesc<'a>> {
907 if offset < 0 {
908 return None;
909 }
910 let (seg_offset, _) = self.segment(SEG_ARRAYDESC)?;
911 let abs = seg_offset.checked_add(offset as usize)?;
912 if abs.checked_add(8)? > self.data.len() {
914 return None;
915 }
916 let num_dims = read_u16_le(self.data, abs + 4)? as usize;
917 let size = 8 + num_dims * 8;
918 let end = abs.checked_add(size)?;
919 if end > self.data.len() {
920 return None;
921 }
922 Some(ArrayDesc::new(&self.data[abs..end]))
923 }
924
925 pub fn cust_data(&self, offset: i32) -> CustDataIter<'a> {
931 let (seg_offset, _) = self.segment(SEG_CDGUIDS).unwrap_or((0, 0));
932 CustDataIter::new(self.data, seg_offset, offset)
933 }
934
935 pub fn lib_cust_data(&self) -> CustDataIter<'a> {
937 self.cust_data(self.custom_data_offset())
938 }
939
940 pub fn resolve_cust_data_entry(
944 &self,
945 entry: &CustDataEntry<'_>,
946 ) -> Option<(Guid<'a>, ConstValue<'a>)> {
947 let guid = self.guid(entry.guid_offset())?;
948 let value = self.const_value(entry.data_offset())?;
949 Some((guid, value))
950 }
951}