1use std::io::Cursor;
5use std::io::SeekFrom;
6
7use crate::ByteSpan;
8use crate::common_file_operations::read_bool_from;
9use crate::common_file_operations::read_string_until_null;
10use binrw::BinRead;
11use binrw::BinReaderExt;
12use binrw::BinResult;
13use binrw::binread;
14
15#[binread]
16#[derive(Debug)]
17#[brw(little)]
18#[brw(magic = b"LVB1")]
19pub struct Lvb {
20 pub file_size: u32,
22 #[br(temp)]
24 #[bw(calc = scns.len() as u32)]
25 scn_count: u32,
26 #[br(count = scn_count)]
27 pub scns: Vec<Scn>,
28}
29
30#[binread]
31#[derive(Debug)]
32#[brw(little)]
33#[brw(magic = b"SCN1")]
34pub struct Scn {
35 total_size: u32,
36 pub header: ScnHeader,
37 #[br(seek_before = SeekFrom::Current(header.offset_general as i64 - ScnHeader::SIZE as i64))]
38 #[br(restore_position)]
39 pub general: ScnGeneralSection,
40 #[br(seek_before = SeekFrom::Current(header.offset_unk1 as i64 - ScnHeader::SIZE as i64))]
41 #[br(restore_position)]
42 pub unk1: ScnUnknown1Section,
43 #[br(seek_before = SeekFrom::Current(header.offset_unk2 as i64 - ScnHeader::SIZE as i64))]
44 #[br(restore_position)]
45 pub unk2: ScnUnknown2Section,
46}
47
48#[binrw::parser(reader)]
49pub(crate) fn strings_from_offsets(offsets: &Vec<i32>) -> BinResult<Vec<String>> {
50 let base_offset = reader.stream_position()?;
51
52 let mut strings: Vec<String> = vec![];
53
54 for offset in offsets {
55 let string_offset = *offset as u64;
56
57 let mut string = String::new();
58
59 reader.seek(SeekFrom::Start(base_offset + string_offset))?;
60 let mut next_char = reader.read_le::<u8>().unwrap() as char;
61 while next_char != '\0' {
62 string.push(next_char);
63 next_char = reader.read_le::<u8>().unwrap() as char;
64 }
65
66 strings.push(string);
67 }
68
69 Ok(strings)
70}
71
72#[binread]
73#[derive(Debug)]
74#[brw(little)]
75pub struct ScnHeader {
76 offset_embedded_layer_groups: i32,
78 num_embedded_layer_groups: i32,
79 offset_general: i32,
81 offset_filters: i32,
83 offset_unk1: i32,
84 offset_layer_group_resources: i32,
86 num_layer_group_resources: i32,
87 unk2: i32,
88 offset_unk2: i32,
89 unk4: i32,
90 unk5: i32,
91 unk6: i32,
92 unk7: i32,
93 unk8: i32,
94 unk9: i32,
95 unk10: i32,
96
97 #[br(count = num_layer_group_resources)]
98 #[br(seek_before = SeekFrom::Current(offset_layer_group_resources as i64 - ScnHeader::SIZE as i64))]
99 #[br(restore_position)]
100 offset_path_layer_group_resources: Vec<i32>,
101
102 #[br(parse_with = strings_from_offsets)]
103 #[br(args(&offset_path_layer_group_resources))]
104 #[br(restore_position)]
105 #[br(seek_before = SeekFrom::Current(offset_layer_group_resources as i64 - ScnHeader::SIZE as i64))]
106 pub path_layer_group_resources: Vec<String>,
107}
108
109impl ScnHeader {
110 pub const SIZE: usize = 0x40;
111}
112
113#[binread]
114#[derive(Debug)]
115#[br(little)]
116pub struct ScnGeneralSection {
117 #[br(map = read_bool_from::<i32>)]
118 pub have_layer_groups: bool,
119 offset_path_terrain: i32,
120 offset_env_spaces: i32,
121 num_env_spaces: i32,
122 unk1: i32,
123 offset_path_sky_visibility: i32,
124 unk2: i32,
125 unk3: i32,
126 unk4: i32,
127 unk5: i32,
128 unk6: i32,
129 unk7: i32,
130 unk8: i32,
131 offset_path_lcb: i32,
132 unk10: i32,
133 unk11: i32,
134 unk12: i32,
135 unk13: i32,
136 unk14: i32,
137 unk15: i32,
138 unk16: i32,
139 #[br(map = read_bool_from::<i32>)]
140 pub have_lcbuw: bool,
141
142 #[br(seek_before = SeekFrom::Current(offset_path_terrain as i64 - ScnGeneralSection::SIZE as i64))]
143 #[br(restore_position, parse_with = read_string_until_null)]
144 pub path_terrain: String,
145
146 #[br(seek_before = SeekFrom::Current(offset_path_sky_visibility as i64 - ScnGeneralSection::SIZE as i64))]
147 #[br(restore_position, parse_with = read_string_until_null)]
148 pub path_sky_visibility: String,
149
150 #[br(seek_before = SeekFrom::Current(offset_path_lcb as i64 - ScnGeneralSection::SIZE as i64))]
151 #[br(restore_position, parse_with = read_string_until_null)]
152 pub path_lcb: String,
153}
154
155impl ScnGeneralSection {
156 pub const SIZE: usize = 0x58;
157}
158
159#[binread]
160#[derive(Debug)]
161#[br(little)]
162pub struct ScnUnknown1Section {
163 unk1: i32,
164 unk2: i32,
165}
166
167impl ScnUnknown1Section {
168 pub const SIZE: usize = 0x8;
169}
170
171#[binread]
173#[derive(Debug)]
174#[br(little)]
175pub struct ScnUnknown2Section {
176 unk1: i32,
177 unk2: i32,
178}
179
180impl ScnUnknown2Section {
181 pub const SIZE: usize = 0x8;
182}
183
184impl Lvb {
185 pub fn from_existing(buffer: ByteSpan) -> Option<Self> {
187 let mut cursor = Cursor::new(buffer);
188 Lvb::read(&mut cursor).ok()
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use std::fs::read;
195 use std::path::PathBuf;
196
197 use super::*;
198
199 #[test]
200 fn test_invalid() {
201 let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
202 d.push("resources/tests");
203 d.push("random");
204
205 Lvb::from_existing(&read(d).unwrap());
207 }
208}