1use {
139 crate::{
140 error::Error,
141 path::{BomPath, BomPathType},
142 },
143 scroll::{IOwrite, Pread, Pwrite, SizeWith},
144 std::{
145 borrow::Cow,
146 collections::HashMap,
147 ffi::CStr,
148 io::{Cursor, Write},
149 },
150};
151
152#[repr(C)]
154#[derive(Clone, Copy, Default, Debug, IOwrite, Pread, Pwrite, SizeWith)]
155pub struct BomHeader {
156 pub magic: [u8; 8],
158
159 pub version: u32,
161
162 pub number_of_blocks: u32,
164
165 pub blocks_index_offset: u32,
167
168 pub blocks_index_length: u32,
170
171 pub vars_index_offset: u32,
173
174 pub vars_index_length: u32,
176}
177
178impl BomHeader {
179 pub fn blocks_index_data<'a>(&self, data: &'a [u8]) -> &'a [u8] {
181 &data[self.blocks_index_offset as usize
182 ..(self.blocks_index_offset + self.blocks_index_length) as usize]
183 }
184
185 pub fn blocks_index(&self, data: &[u8]) -> Result<BomBlocksIndex, Error> {
187 self.blocks_index_data(data)
188 .pread_with::<BomBlocksIndex>(0, scroll::BE)
189 }
190
191 pub fn vars_index_data<'a>(&self, data: &'a [u8]) -> &'a [u8] {
193 &data[self.vars_index_offset as usize
194 ..(self.vars_index_offset + self.vars_index_length) as usize]
195 }
196
197 pub fn vars_index(&self, data: &[u8]) -> Result<BomVarsIndex, Error> {
199 self.vars_index_data(data)
200 .pread_with::<BomVarsIndex>(0, scroll::BE)
201 }
202}
203
204#[derive(Clone, Default, Debug)]
217pub struct BomBlocksIndex {
218 pub count: u32,
220
221 pub blocks: Vec<BomBlocksEntry>,
223}
224
225impl BomBlocksIndex {
226 pub fn write(&self, writer: &mut impl Write) -> Result<(), Error> {
228 writer.iowrite_with(self.count, scroll::BE)?;
229
230 for entry in &self.blocks {
231 writer.iowrite_with(*entry, scroll::BE)?;
232 }
233
234 Ok(())
235 }
236
237 pub fn to_vec(&self) -> Result<Vec<u8>, Error> {
239 let mut writer = Cursor::new(Vec::<u8>::new());
240 self.write(&mut writer)?;
241 Ok(writer.into_inner())
242 }
243}
244
245impl<'a> scroll::ctx::TryFromCtx<'a, scroll::Endian> for BomBlocksIndex {
246 type Error = Error;
247
248 fn try_from_ctx(data: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> {
249 let offset = &mut 0;
250
251 let count = data.gread_with::<u32>(offset, le)?;
252 let mut blocks = Vec::with_capacity(count as usize);
253
254 for _ in 0..count {
255 blocks.push(data.gread_with::<BomBlocksEntry>(offset, le)?);
256 }
257
258 Ok((Self { count, blocks }, *offset))
259 }
260}
261
262#[repr(C)]
266#[derive(Clone, Copy, Default, Debug, IOwrite, Pread, Pwrite, SizeWith)]
267pub struct BomBlocksEntry {
268 pub file_offset: u32,
270
271 pub length: u32,
273}
274
275#[derive(Clone, Debug)]
280pub struct BomVar {
281 pub block_index: u32,
283
284 pub name_length: u8,
286
287 pub name: String,
289}
290
291impl BomVar {
292 pub fn new(block_index: u32, name: impl ToString) -> Result<Self, Error> {
294 let name = name.to_string();
295
296 if name.as_bytes().len() > 254 {
297 return Err(Error::BadVariableString);
298 }
299
300 Ok(Self {
301 block_index,
302 name_length: name.as_bytes().len() as u8 + 1,
303 name,
304 })
305 }
306
307 pub fn write(&self, writer: &mut impl Write) -> Result<(), Error> {
309 writer.iowrite_with(self.block_index, scroll::BE)?;
310 writer.iowrite_with(self.name_length, scroll::BE)?;
311 writer.write_all(self.name.as_bytes())?;
312 writer.write_all(b"\0")?;
313
314 Ok(())
315 }
316}
317
318impl<'a> scroll::ctx::TryFromCtx<'a, scroll::Endian> for BomVar {
319 type Error = Error;
320
321 fn try_from_ctx(data: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> {
322 let index = data.pread_with(0, le)?;
323 let length = data.pread_with(4, le)?;
324
325 let name_data = &data[5..5 + length as usize];
326 let name = String::from_utf8(name_data.to_vec()).map_err(|_| Error::BadVariableString)?;
327
328 Ok((
329 Self {
330 block_index: index,
331 name_length: length,
332 name,
333 },
334 5 + name_data.len(),
335 ))
336 }
337}
338
339#[repr(C)]
344#[derive(Clone, Default, Debug)]
345pub struct BomBlockBomInfo {
346 pub version: u32,
348
349 pub number_of_paths: u32,
351
352 pub number_of_info_entries: u32,
354
355 pub entries: Vec<BomInfoEntry>,
357}
358
359impl BomBlockBomInfo {
360 pub fn write(&self, writer: &mut impl Write) -> Result<(), Error> {
362 writer.iowrite_with(self.version, scroll::BE)?;
363 writer.iowrite_with(self.number_of_paths, scroll::BE)?;
364 writer.iowrite_with(self.number_of_info_entries, scroll::BE)?;
365
366 for entry in &self.entries {
367 writer.iowrite_with(*entry, scroll::BE)?;
368 }
369
370 Ok(())
371 }
372}
373
374impl<'a> scroll::ctx::TryFromCtx<'a, scroll::Endian> for BomBlockBomInfo {
375 type Error = Error;
376
377 fn try_from_ctx(data: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> {
378 let offset = &mut 0;
379
380 let version = data.gread_with(offset, le)?;
381 let number_of_paths = data.gread_with(offset, le)?;
382 let number_of_info_entries = data.gread_with(offset, le)?;
383 let mut entries = Vec::with_capacity(number_of_info_entries as usize);
384
385 for _ in 0..number_of_info_entries {
386 entries.push(data.gread_with(offset, le)?);
387 }
388
389 Ok((
390 Self {
391 version,
392 number_of_paths,
393 number_of_info_entries,
394 entries,
395 },
396 *offset,
397 ))
398 }
399}
400
401#[repr(C)]
406#[derive(Clone, Copy, Default, Debug, IOwrite, Pread, Pwrite, SizeWith)]
407pub struct BomInfoEntry {
408 pub a: u32,
409 pub b: u32,
410 pub c: u32,
411 pub d: u32,
412}
413
414#[derive(Clone, Debug)]
416pub struct BomBlockFile<'a> {
417 pub parent_path_id: u32,
421
422 pub name: Cow<'a, CStr>,
427}
428
429impl<'a> BomBlockFile<'a> {
430 pub fn write(&self, writer: &mut impl Write) -> Result<(), Error> {
432 writer.iowrite_with(self.parent_path_id, scroll::BE)?;
433 writer.write_all(self.name.to_bytes_with_nul())?;
434
435 Ok(())
436 }
437
438 pub fn string_file_name(&self) -> String {
440 self.name.to_string_lossy().to_string()
441 }
442}
443
444impl<'a> scroll::ctx::TryFromCtx<'a, scroll::Endian> for BomBlockFile<'a> {
445 type Error = Error;
446
447 fn try_from_ctx(data: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> {
448 let parent = data.pread_with(0, le)?;
449 let name =
450 Cow::from(CStr::from_bytes_with_nul(&data[4..]).map_err(|_| Error::BadVariableString)?);
451
452 Ok((
453 Self {
454 parent_path_id: parent,
455 name,
456 },
457 data.len(),
458 ))
459 }
460}
461
462#[repr(C)]
470#[derive(Clone, Copy, Default, Debug, IOwrite, Pread, Pwrite, SizeWith)]
471pub struct BomBlockPathRecordPointer {
472 pub block_path_record_index: u32,
474}
475
476impl BomBlockPathRecordPointer {
477 pub fn path_record<'a>(&self, bom: &'a ParsedBom) -> Result<BomBlockPathRecord<'a>, Error> {
478 bom.block_as_path_record(self.block_path_record_index as _)
479 }
480}
481
482#[repr(C)]
495#[derive(Clone, Default, Debug)]
496pub struct BomBlockPaths {
497 pub is_path_info: u16,
501
502 pub count: u16,
504
505 pub next_paths_block_index: u32,
507
508 pub previous_paths_block_index: u32,
510
511 pub paths: Vec<BomPathsEntry>,
513}
514
515impl BomBlockPaths {
516 pub fn write(&self, writer: &mut impl Write) -> Result<(), Error> {
518 writer.iowrite_with(self.is_path_info, scroll::BE)?;
519 writer.iowrite_with(self.count, scroll::BE)?;
520 writer.iowrite_with(self.next_paths_block_index, scroll::BE)?;
521 writer.iowrite_with(self.previous_paths_block_index, scroll::BE)?;
522
523 for entry in &self.paths {
524 writer.iowrite_with(*entry, scroll::BE)?;
525 }
526
527 Ok(())
528 }
529
530 pub fn file_at<'a>(&self, bom: &'a ParsedBom, index: usize) -> Result<BomBlockFile<'a>, Error> {
532 self.paths.get(index).ok_or(Error::BadIndex)?.file(bom)
533 }
534
535 pub fn path_info_at(
537 &self,
538 bom: &ParsedBom,
539 index: usize,
540 ) -> Result<BomBlockPathInfoIndex, Error> {
541 self.paths.get(index).ok_or(Error::BadIndex)?.path_info(bom)
542 }
543
544 pub fn path_id_at(&self, bom: &ParsedBom, index: usize) -> Result<u32, Error> {
546 Ok(self.path_info_at(bom, index)?.path_id)
547 }
548
549 pub fn path_record_at<'a>(
551 &self,
552 bom: &'a ParsedBom,
553 index: usize,
554 ) -> Result<BomBlockPathRecord<'a>, Error> {
555 self.path_info_at(bom, index)?.path_record(bom)
556 }
557
558 pub fn path_entry_at<'a>(
560 &self,
561 bom: &'a ParsedBom,
562 index: usize,
563 ) -> Result<(u32, BomBlockFile<'a>, BomBlockPathRecord<'a>), Error> {
564 let path_info = self.path_info_at(bom, index)?;
565 let file = self.file_at(bom, index)?;
566 let record = path_info.path_record(bom)?;
567
568 Ok((path_info.path_id, file, record))
569 }
570
571 pub fn iter_path_entries<'a, 'b: 'a>(
578 &'a self,
579 bom: &'b ParsedBom,
580 ) -> impl Iterator<Item = Result<(u32, BomBlockFile<'b>, BomBlockPathRecord<'b>), Error>> + 'a
581 {
582 self.paths
583 .iter()
584 .enumerate()
585 .map(move |(i, _)| self.path_entry_at(bom, i))
586 }
587}
588
589impl<'a> scroll::ctx::TryFromCtx<'a, scroll::Endian> for BomBlockPaths {
590 type Error = scroll::Error;
591
592 fn try_from_ctx(data: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> {
593 let offset = &mut 0;
594
595 let is_path_info = data.gread_with::<u16>(offset, le)?;
596 let count = data.gread_with::<u16>(offset, le)?;
597 let forward = data.gread_with::<u32>(offset, le)?;
598 let backward = data.gread_with::<u32>(offset, le)?;
599
600 let mut paths = Vec::with_capacity(count as usize);
601 for _ in 0..count {
602 paths.push(data.gread_with::<BomPathsEntry>(offset, le)?);
603 }
604
605 Ok((
606 Self {
607 is_path_info,
608 count,
609 next_paths_block_index: forward,
610 previous_paths_block_index: backward,
611 paths,
612 },
613 *offset,
614 ))
615 }
616}
617
618#[repr(C)]
622#[derive(Clone, Copy, Default, Debug, IOwrite, Pread, Pwrite, SizeWith)]
623pub struct BomPathsEntry {
624 pub block_index: u32,
632
633 pub file_index: u32,
636}
637
638impl BomPathsEntry {
639 pub fn path_info(&self, bom: &ParsedBom) -> Result<BomBlockPathInfoIndex, Error> {
641 bom.block_as_path_info_index(self.block_index as _)
642 }
643
644 pub fn paths(&self, bom: &ParsedBom) -> Result<BomBlockPaths, Error> {
646 bom.block_as_paths(self.block_index as _)
647 }
648
649 pub fn file<'a>(&self, bom: &'a ParsedBom) -> Result<BomBlockFile<'a>, Error> {
651 bom.block_as_file(self.file_index as _)
652 }
653}
654
655#[repr(C)]
657#[derive(Clone, Copy, Default, Debug, IOwrite, Pread, Pwrite, SizeWith)]
658pub struct BomBlockPathInfoIndex {
659 pub path_id: u32,
663
664 pub path_record_index: u32,
666}
667
668impl BomBlockPathInfoIndex {
669 pub fn path_record<'a>(&self, bom: &'a ParsedBom) -> Result<BomBlockPathRecord<'a>, Error> {
671 bom.block_as_path_record(self.path_record_index as _)
672 }
673}
674
675#[repr(C)]
679#[derive(Clone, Default, Debug)]
680pub struct BomBlockPathRecord<'a> {
681 pub path_type: u8,
685
686 pub a: u8,
688
689 pub architecture: u16,
693
694 pub mode: u16,
696
697 pub user: u32,
699
700 pub group: u32,
702
703 pub mtime: u32,
705
706 pub size: u32,
708
709 pub b: u8,
711
712 pub checksum_or_type: u32,
714
715 pub link_name_length: u32,
721
722 pub link_name: Option<Cow<'a, CStr>>,
724}
725
726impl<'a> BomBlockPathRecord<'a> {
727 pub fn write(&self, writer: &mut impl Write) -> Result<(), Error> {
729 writer.iowrite_with(self.path_type, scroll::BE)?;
730 writer.iowrite_with(self.a, scroll::BE)?;
731 writer.iowrite_with(self.architecture, scroll::BE)?;
732 writer.iowrite_with(self.mode, scroll::BE)?;
733 writer.iowrite_with(self.user, scroll::BE)?;
734 writer.iowrite_with(self.group, scroll::BE)?;
735 writer.iowrite_with(self.mtime, scroll::BE)?;
736 writer.iowrite_with(self.size, scroll::BE)?;
737 writer.iowrite_with(self.b, scroll::BE)?;
738 writer.iowrite_with(self.checksum_or_type, scroll::BE)?;
739 writer.iowrite_with(self.link_name_length, scroll::BE)?;
740 if let Some(link_name) = &self.link_name {
741 writer.write_all(link_name.to_bytes_with_nul())?;
742 }
743
744 Ok(())
745 }
746
747 pub fn string_link_name(&self) -> Option<String> {
749 self.link_name
750 .as_ref()
751 .map(|s| s.to_string_lossy().to_string())
752 }
753}
754
755impl<'a> scroll::ctx::TryFromCtx<'a, scroll::Endian> for BomBlockPathRecord<'a> {
756 type Error = Error;
757
758 fn try_from_ctx(data: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> {
759 let offset = &mut 0;
760
761 let path_type = data.gread_with(offset, le)?;
762 let a = data.gread_with(offset, le)?;
763 let architecture = data.gread_with(offset, le)?;
764 let mode = data.gread_with(offset, le)?;
765 let user = data.gread_with(offset, le)?;
766 let group = data.gread_with(offset, le)?;
767 let mtime = data.gread_with(offset, le)?;
768 let size = data.gread_with(offset, le)?;
769 let b = data.gread_with(offset, le)?;
770 let checksum_or_type = data.gread_with(offset, le)?;
771 let link_name_length = data.gread_with(offset, le)?;
772
773 let link_name = if path_type == BomPathType::Link.into() && link_name_length > 0 {
774 let link_name_data = &data[*offset..*offset + link_name_length as usize];
775 Some(Cow::from(
776 CStr::from_bytes_with_nul(link_name_data).map_err(|_| Error::BadVariableString)?,
777 ))
778 } else {
779 None
780 };
781
782 Ok((
783 Self {
784 path_type,
785 a,
786 architecture,
787 mode,
788 user,
789 group,
790 mtime,
791 size,
792 b,
793 checksum_or_type,
794 link_name_length,
795 link_name,
796 },
797 *offset,
798 ))
799 }
800}
801
802#[repr(C)]
804#[derive(Clone, Copy, Debug, IOwrite, Pwrite, SizeWith)]
805pub struct BomBlockTree {
806 pub tree: [u8; 4],
808
809 pub version: u32,
811
812 pub block_paths_index: u32,
814
815 pub block_size: u32,
817
818 pub path_count: u32,
820
821 pub a: u8,
823}
824
825impl Default for BomBlockTree {
826 fn default() -> Self {
827 Self {
828 tree: *b"tree",
829 version: 1,
830 block_paths_index: 0,
831 block_size: 0,
832 path_count: 0,
833 a: 0,
834 }
835 }
836}
837
838impl<'a> scroll::ctx::TryFromCtx<'a, scroll::Endian> for BomBlockTree {
839 type Error = Error;
840
841 fn try_from_ctx(data: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> {
842 let offset = &mut 4;
843
844 let tree: [u8; 4] = [data[0], data[1], data[2], data[3]];
845 let version = data.gread_with(offset, le)?;
846 let block_paths_index = data.gread_with(offset, le)?;
847 let block_size = data.gread_with(offset, le)?;
848 let path_count = data.gread_with(offset, le)?;
849 let a = data.gread_with(offset, le)?;
850
851 if &tree != b"tree" {
852 return Err(Error::Scroll(scroll::Error::Custom(
853 "bad tree magic".into(),
854 )));
855 }
856
857 Ok((
858 Self {
859 tree,
860 version,
861 block_paths_index,
862 block_size,
863 path_count,
864 a,
865 },
866 *offset,
867 ))
868 }
869}
870
871impl BomBlockTree {
872 pub fn paths(&self, bom: &ParsedBom) -> Result<BomBlockPaths, Error> {
874 bom.block_as_paths(self.block_paths_index as _)
875 }
876
877 pub fn root_paths(&self, bom: &ParsedBom) -> Result<BomBlockPaths, Error> {
879 let mut paths = self.paths(bom)?;
880
881 while paths.is_path_info == 0 {
882 let entry = paths.paths.get(0).ok_or(Error::BadIndex)?;
883 paths = entry.paths(bom)?;
884 }
885
886 Ok(paths)
887 }
888
889 pub fn bom_paths(&self, bom: &ParsedBom) -> Result<Vec<BomPath>, Error> {
893 let mut res = Vec::with_capacity(self.path_count as _);
894
895 let mut paths = self.root_paths(bom)?;
896 let mut files_by_id = HashMap::with_capacity(res.capacity());
897
898 loop {
899 for entry in paths.iter_path_entries(bom) {
900 let (path_id, file, record) = entry?;
901
902 let mut resolve_file = &file;
905 let mut filename = file.string_file_name();
906
907 while resolve_file.parent_path_id != 0 {
908 resolve_file = files_by_id
909 .get(&resolve_file.parent_path_id)
910 .ok_or(Error::BadIndex)?;
911 filename = format!("{}/{}", resolve_file.string_file_name(), filename);
912 }
913
914 res.push(BomPath::from_record(filename, &record)?);
915
916 files_by_id.insert(path_id, file);
917 }
918
919 if paths.next_paths_block_index != 0 {
920 paths = bom.block_as_paths(paths.next_paths_block_index as _)?;
921 } else {
922 break;
923 }
924 }
925
926 Ok(res)
927 }
928}
929
930#[repr(C)]
935#[derive(Clone, Copy, Default, Debug, IOwrite, Pread, Pwrite, SizeWith)]
936pub struct BomBlockTreePointer {
937 pub block_tree_index: u32,
939}
940
941impl BomBlockTreePointer {
942 pub fn tree(&self, bom: &ParsedBom) -> Result<BomBlockTree, Error> {
943 bom.block_as_tree(self.block_tree_index as _)
944 }
945}
946
947#[repr(C)]
951#[derive(Clone, Copy, Default, Debug, IOwrite, Pread, Pwrite, SizeWith)]
952pub struct BomBlockVIndex {
953 pub a: u32,
955
956 pub tree_block_index: u32,
958
959 pub b: u32,
961
962 pub c: u8,
964}
965
966impl BomBlockVIndex {
967 pub fn tree(&self, bom: &ParsedBom) -> Result<BomBlockTree, Error> {
969 bom.block_as_tree(self.tree_block_index as _)
970 }
971}
972
973#[derive(Clone, Debug)]
978pub struct BomVarsIndex {
979 pub count: u32,
981
982 pub vars: Vec<BomVar>,
984}
985
986impl BomVarsIndex {
987 pub fn write(&self, writer: &mut impl Write) -> Result<(), Error> {
989 writer.iowrite_with(self.count, scroll::BE)?;
990
991 for var in &self.vars {
992 var.write(writer)?;
993 }
994
995 Ok(())
996 }
997
998 pub fn to_vec(&self) -> Result<Vec<u8>, Error> {
1000 let mut writer = Cursor::new(Vec::<u8>::new());
1001 self.write(&mut writer)?;
1002 Ok(writer.into_inner())
1003 }
1004}
1005
1006impl<'a> scroll::ctx::TryFromCtx<'a, scroll::Endian> for BomVarsIndex {
1007 type Error = Error;
1008
1009 fn try_from_ctx(data: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> {
1010 let offset = &mut 0;
1011
1012 let count = data.gread_with::<u32>(offset, le)?;
1013 let mut vars = Vec::with_capacity(count as usize);
1014
1015 for _ in 0..count {
1016 vars.push(data.gread_with::<BomVar>(offset, le)?);
1017 }
1018
1019 Ok((Self { count, vars }, *offset))
1020 }
1021}
1022
1023#[derive(Clone, Debug)]
1025pub enum BomBlock<'a> {
1026 Empty,
1027 BomInfo(BomBlockBomInfo),
1028 File(BomBlockFile<'a>),
1029 PathInfoIndex(BomBlockPathInfoIndex),
1030 PathRecord(BomBlockPathRecord<'a>),
1031 PathRecordPointer(BomBlockPathRecordPointer),
1032 Paths(BomBlockPaths),
1033 Tree(BomBlockTree),
1034 TreePointer(BomBlockTreePointer),
1035 VIndex(BomBlockVIndex),
1036}
1037
1038impl<'a> BomBlock<'a> {
1039 pub fn try_parse(bom: &'a ParsedBom, index: usize) -> Result<Self, Error> {
1041 if index == 1 {
1042 if let Ok(info) = bom.block_as_bom_info(index) {
1043 return Ok(Self::BomInfo(info));
1044 }
1045 }
1046
1047 if let Ok(tree) = bom.block_as_tree(index) {
1051 if tree.paths(bom).is_ok() {
1052 return Ok(Self::Tree(tree));
1053 }
1054 }
1055
1056 if let Ok(paths) = bom.block_as_paths(index) {
1057 if paths.iter_path_entries(bom).all(|x| x.is_ok()) {
1058 return Ok(Self::Paths(paths));
1059 }
1060 }
1061
1062 if let Ok(vindex) = bom.block_as_vindex(index) {
1063 if vindex.tree(bom).is_ok() {
1064 return Ok(Self::VIndex(vindex));
1065 }
1066 }
1067
1068 if let Ok(index) = bom.block_as_path_info_index(index) {
1069 if index.path_record(bom).is_ok() {
1070 return Ok(Self::PathInfoIndex(index));
1071 }
1072 }
1073
1074 if let Ok(record) = bom.block_as_path_record(index) {
1076 return Ok(Self::PathRecord(record));
1077 }
1078
1079 if let Ok(file) = bom.block_as_file(index) {
1080 return Ok(Self::File(file));
1081 }
1082
1083 if let Ok(pointer) = bom.block_as_path_record_pointer(index) {
1084 if pointer.path_record(bom).is_ok() {
1085 return Ok(Self::PathRecordPointer(pointer));
1086 }
1087 }
1088
1089 if let Ok(pointer) = bom.block_as_tree_pointer(index) {
1090 if pointer.tree(bom).is_ok() {
1091 return Ok(Self::TreePointer(pointer));
1092 }
1093 }
1094
1095 if let Ok(info) = bom.block_as_bom_info(index) {
1096 return Ok(Self::BomInfo(info));
1097 }
1098
1099 Err(Error::UnknownBlockType)
1100 }
1101
1102 pub fn write(&self, writer: &mut impl Write) -> Result<(), Error> {
1104 match self {
1105 Self::Empty => {}
1106 Self::BomInfo(b) => {
1107 b.write(writer)?;
1108 }
1109 Self::File(b) => {
1110 b.write(writer)?;
1111 }
1112 Self::PathInfoIndex(b) => {
1113 writer.iowrite_with(*b, scroll::BE)?;
1114 }
1115 Self::PathRecord(b) => {
1116 b.write(writer)?;
1117 }
1118 Self::PathRecordPointer(b) => {
1119 writer.iowrite_with(*b, scroll::BE)?;
1120 }
1121 Self::Paths(b) => {
1122 b.write(writer)?;
1123 }
1124 Self::Tree(b) => {
1125 writer.iowrite_with(*b, scroll::BE)?;
1126 }
1127 Self::TreePointer(b) => {
1128 writer.iowrite_with(*b, scroll::BE)?;
1129 }
1130 Self::VIndex(b) => {
1131 writer.iowrite_with(*b, scroll::BE)?;
1132 }
1133 }
1134
1135 Ok(())
1136 }
1137
1138 pub fn to_vec(&self) -> Result<Vec<u8>, Error> {
1140 let mut writer = Cursor::new(Vec::<u8>::new());
1141 self.write(&mut writer)?;
1142
1143 Ok(writer.into_inner())
1144 }
1145}
1146
1147pub struct ParsedBom<'a> {
1151 pub data: Cow<'a, [u8]>,
1153
1154 pub header: BomHeader,
1156
1157 pub blocks: BomBlocksIndex,
1159
1160 pub vars: BomVarsIndex,
1162}
1163
1164impl<'a> ParsedBom<'a> {
1165 pub fn parse(data: &'a [u8]) -> Result<Self, Error> {
1170 let header = data.pread_with::<BomHeader>(0, scroll::BE)?;
1171
1172 let blocks_index = header.blocks_index(data)?;
1173 let vars = header.vars_index(data)?;
1174
1175 Ok(Self {
1176 data: Cow::Borrowed(data),
1177 header,
1178 blocks: blocks_index,
1179 vars,
1180 })
1181 }
1182
1183 pub fn to_owned(&self) -> ParsedBom<'static> {
1185 ParsedBom {
1186 data: Cow::Owned(self.data.clone().into_owned()),
1187 header: self.header,
1188 blocks: self.blocks.clone(),
1189 vars: self.vars.clone(),
1190 }
1191 }
1192
1193 pub fn find_variable(&self, name: &str) -> Result<&BomVar, Error> {
1195 self.vars
1196 .vars
1197 .iter()
1198 .find(|v| v.name == name)
1199 .ok_or_else(|| Error::NoVar(name.to_string()))
1200 }
1201
1202 pub fn bom_info(&self) -> Result<BomBlockBomInfo, Error> {
1204 let var = self.find_variable("BomInfo")?;
1205
1206 self.block_as_bom_info(var.block_index as _)
1207 }
1208
1209 pub fn hl_index(&self) -> Result<Vec<BomPath>, Error> {
1210 let var = self.find_variable("HLIndex")?;
1211 let tree = self.block_as_tree(var.block_index as _)?;
1212
1213 tree.bom_paths(self)
1214 }
1215
1216 pub fn paths(&self) -> Result<Vec<BomPath>, Error> {
1217 let index = self.find_variable("Paths")?;
1218 let tree = self.block_as_tree(index.block_index as _)?;
1219
1220 tree.bom_paths(self)
1221 }
1222
1223 pub fn size64(&self) -> Result<Vec<BomPath>, Error> {
1225 let var = self.find_variable("Size64")?;
1226 let tree = self.block_as_tree(var.block_index as _)?;
1227
1228 tree.bom_paths(self)
1229 }
1230
1231 pub fn vindex(&self) -> Result<Vec<BomPath>, Error> {
1233 let var = self.find_variable("VIndex")?;
1234 let index = self.block_as_vindex(var.block_index as _)?;
1235 let tree = index.tree(self)?;
1236
1237 tree.bom_paths(self)
1238 }
1239
1240 pub fn block_data(&self, index: usize) -> Result<&[u8], Error> {
1242 let entry = self.blocks.blocks.get(index).ok_or(Error::BadIndex)?;
1243
1244 Ok(&self.data[entry.file_offset as usize..(entry.file_offset + entry.length) as usize])
1245 }
1246
1247 pub fn block_as_bom_info(&self, index: usize) -> Result<BomBlockBomInfo, Error> {
1249 self.block_data(index)?.pread_with(0, scroll::BE)
1250 }
1251
1252 pub fn block_as_file(&self, index: usize) -> Result<BomBlockFile<'_>, Error> {
1254 self.block_data(index)?.pread_with(0, scroll::BE)
1255 }
1256
1257 pub fn block_as_path_info_index(&self, index: usize) -> Result<BomBlockPathInfoIndex, Error> {
1259 Ok(self.block_data(index)?.pread_with(0, scroll::BE)?)
1260 }
1261
1262 pub fn block_as_path_record(&self, index: usize) -> Result<BomBlockPathRecord, Error> {
1264 self.block_data(index)?.pread_with(0, scroll::BE)
1265 }
1266
1267 pub fn block_as_path_record_pointer(
1269 &self,
1270 index: usize,
1271 ) -> Result<BomBlockPathRecordPointer, Error> {
1272 Ok(self.block_data(index)?.pread_with(0, scroll::BE)?)
1273 }
1274
1275 pub fn block_as_paths(&self, index: usize) -> Result<BomBlockPaths, Error> {
1277 let data = self.block_data(index)?;
1278 Ok(data.pread_with(0, scroll::BE)?)
1279 }
1280
1281 pub fn block_as_tree(&self, index: usize) -> Result<BomBlockTree, Error> {
1283 let data = self.block_data(index)?;
1284 data.pread_with(0, scroll::BE)
1285 }
1286
1287 pub fn block_as_tree_pointer(&self, index: usize) -> Result<BomBlockTreePointer, Error> {
1289 Ok(self.block_data(index)?.pread_with(0, scroll::BE)?)
1290 }
1291
1292 pub fn block_as_vindex(&self, index: usize) -> Result<BomBlockVIndex, Error> {
1294 Ok(self.block_data(index)?.pread_with(0, scroll::BE)?)
1295 }
1296}
1297
1298#[cfg(test)]
1299mod tests {
1300 use super::*;
1301
1302 const PYTHON_DATA: &[u8] = include_bytes!("testdata/python-applications.bom");
1303
1304 #[test]
1305 fn parse_python() -> Result<(), Error> {
1306 let bom = crate::format::ParsedBom::parse(PYTHON_DATA)?;
1307
1308 bom.bom_info()?;
1309
1310 for _ in bom.hl_index()? {}
1312 for _ in bom.paths()? {}
1313 for _ in bom.size64()? {}
1314 for _ in bom.vindex()? {}
1315
1316 let root = bom.paths()?.into_iter().find(|p| p.path() == ".").unwrap();
1317 assert_eq!(root.symbolic_mode(), "drwxr-xr-x");
1318
1319 let readme = bom
1320 .paths()?
1321 .into_iter()
1322 .find(|p| p.path() == "./Python 3.9/ReadMe.rtf")
1323 .unwrap();
1324 assert_eq!(readme.symbolic_mode(), "-rw-r--r--");
1325
1326 Ok(())
1327 }
1328}