rgpre/import/rob/
parser.rs1use super::{RobFile, RobHeader, RobSegment, SegmentBBox};
2use log::warn;
3use nom::{
4 IResult, Parser,
5 bytes::complete::{tag, take},
6 combinator::opt,
7 number::complete::{be_u32, le_u16, le_u32},
8};
9
10pub fn parse_rob_header(input: &[u8]) -> IResult<&[u8], RobHeader> {
12 let (input, found_archive_magic) = opt(tag("OARC")).parse(input)?;
13 if found_archive_magic.is_none() {
14 warn!("OARC header not found where expected");
15 }
16 let (input, unknown_04) = be_u32(input)?;
17 let (input, num_segments) = le_u32(input)?;
18 let (input, found_segment_magic) = opt(tag("OARD")).parse(input)?;
19 if found_segment_magic.is_none() {
20 warn!("OARD header not found where expected");
21 }
22 let (input, payload_size) = be_u32(input)?;
23
24 Ok((
25 input,
26 RobHeader {
27 unknown_04,
28 num_segments,
29 payload_size,
30 },
31 ))
32}
33
34pub fn parse_rob_segment_header(input: &[u8]) -> IResult<&[u8], RobSegment> {
36 let (input, total_size) = le_u32(input)?;
37 let (input, segment_name) = take(8u8)(input)?;
38 let (input, segment_type) = le_u16(input)?;
39 let (input, segment_flags) = le_u16(input)?;
40
41 let (input, segment_attribs) = nom::number::complete::le_u8(input)?;
42 let (input, face_count_low) = take(3usize)(input)?;
43 let (input, unused_14) = be_u32(input)?;
44 let (input, _reserved_18) = le_u32(input)?;
45
46 let (input, extent_x) = le_u32(input)?;
47 let (input, extent_y) = le_u32(input)?;
48 let (input, extent_z) = le_u32(input)?;
49
50 let (input, _reserved_28) = le_u32(input)?;
51 let (input, _reserved_2c) = le_u32(input)?;
52 let (input, _reserved_30) = le_u32(input)?;
53
54 let (input, positive_x) = le_u32(input)?;
55 let (input, positive_y) = le_u32(input)?;
56 let (input, positive_z) = le_u32(input)?;
57
58 let (input, negative_x) = le_u32(input)?;
59 let (input, negative_y) = le_u32(input)?;
60 let (input, negative_z) = le_u32(input)?;
61
62 let (input, data_size) = le_u32(input)?;
63
64 let mut segment_name_bytes = [0_u8; 8];
65 segment_name_bytes.copy_from_slice(segment_name);
66 let mut face_count_low_bytes = [0_u8; 3];
67 face_count_low_bytes.copy_from_slice(face_count_low);
68
69 Ok((
70 input,
71 RobSegment {
72 total_size,
73 segment_name: segment_name_bytes,
74 segment_type,
75 segment_flags,
76 segment_attribs,
77 _face_count_low: face_count_low_bytes,
78 _unused_14: unused_14,
79 bbox: SegmentBBox {
80 extent_x,
81 extent_y,
82 extent_z,
83 positive_x,
84 positive_y,
85 positive_z,
86 negative_x,
87 negative_y,
88 negative_z,
89 },
90 data_size,
91 data: Vec::new(),
92 },
93 ))
94}
95
96pub fn parse_rob_segment(input: &[u8]) -> IResult<&[u8], RobSegment> {
98 let (input, mut segment) = parse_rob_segment_header(input)?;
99 let (input, data) = if segment.data_size > 0 && !segment.points_to_external_file() {
100 take(usize::try_from(segment.data_size).unwrap_or(usize::MAX))(input)?
101 } else {
102 (input, &[] as &[u8])
103 };
104 segment.data = data.to_vec();
105 Ok((input, segment))
106}
107
108pub fn parse_rob_file(input: &[u8]) -> IResult<&[u8], RobFile> {
110 let file_size = u32::try_from(input.len()).unwrap_or(u32::MAX);
111 let (input, header) = parse_rob_header(input)?;
112
113 let expected_payload = file_size.saturating_sub(24);
116 if header.payload_size != expected_payload {
117 warn!(
118 "ROB payload_size {} does not match expected {} (file_size {} - 24)",
119 header.payload_size, expected_payload, file_size
120 );
121 }
122
123 let mut segments = Vec::new();
124 let mut remaining_input = input;
125
126 for _ in 0..header.num_segments {
127 let (input, segment) = parse_rob_segment(remaining_input)?;
128 segments.push(segment);
129 remaining_input = input;
130 }
131
132 let (remaining_input, end_marker) = opt(tag("END ")).parse(remaining_input)?;
133 if end_marker.is_none() {
134 warn!("END marker not found at end of file");
135 }
136
137 Ok((remaining_input, RobFile { header, segments }))
138}