1use crate::chunk_discovery::ChunkDiscovery;
2use crate::chunks::{
3 GfidEntry, McvpEntry, MfogEntry, ModdEntry, ModiEntry, Modn, ModsEntry, MogiEntry, Mogn,
4 MoltEntry, MolvEntry, Mom3Entry, MomtEntry, MopeEntry, MoprEntry, MoptEntry, MopvEntry, Mosb,
5 Motx, MouvEntry, MovbEntry, MovvEntry,
6};
7use binrw::{BinRead, BinReaderExt};
8use std::collections::HashMap;
9use std::io::{Read, Seek, SeekFrom};
10
11#[derive(Debug, Clone)]
13pub struct WmoRoot {
14 pub version: u32,
16 pub n_materials: u32,
18 pub n_groups: u32,
20 pub n_portals: u32,
22 pub n_lights: u32,
24 pub n_doodad_names: u32,
26 pub n_doodad_defs: u32,
28 pub n_doodad_sets: u32,
30 pub ambient_color: [u8; 4],
32 pub wmo_id: u32,
34 pub bounding_box_min: [f32; 3],
36 pub bounding_box_max: [f32; 3],
38 pub flags: u16,
40 pub num_lod: u16,
42
43 pub textures: Vec<String>,
46 pub texture_offset_index_map: HashMap<u32, u32>,
47 pub materials: Vec<MomtEntry>,
49 pub group_names: Vec<String>,
51 pub group_info: Vec<MogiEntry>,
53 pub skybox: Option<String>,
55 pub portal_vertices: Vec<MopvEntry>,
57 pub portals: Vec<MoptEntry>,
59 pub portal_refs: Vec<MoprEntry>,
61 pub visible_vertices: Vec<MovvEntry>,
63 pub visible_blocks: Vec<MovbEntry>,
65 pub lights: Vec<MoltEntry>,
67 pub doodad_sets: Vec<ModsEntry>,
69 pub doodad_names: Vec<String>,
71 pub doodad_defs: Vec<ModdEntry>,
73 pub fogs: Vec<MfogEntry>,
75 pub convex_volume_planes: Vec<McvpEntry>,
77 pub uv_transforms: Vec<MouvEntry>,
79 pub portal_extras: Vec<MopeEntry>,
81 pub light_extensions: Vec<MolvEntry>,
83 pub doodad_ids: Vec<ModiEntry>,
85 pub new_materials: Vec<Mom3Entry>,
87 pub group_file_ids: Vec<GfidEntry>,
89}
90
91#[derive(Debug, Clone, BinRead)]
94#[br(little)]
95struct Mohd {
96 n_materials: u32,
98 n_groups: u32,
100 n_portals: u32,
102 n_lights: u32,
104 n_doodad_names: u32,
106 n_doodad_defs: u32,
108 n_doodad_sets: u32,
110 ambient_color: [u8; 4],
112 wmo_id: u32,
114 bounding_box_min: [f32; 3],
116 bounding_box_max: [f32; 3],
118 flags: u16,
120 num_lod: u16,
122}
123
124pub fn parse_root_file<R: Read + Seek>(
126 reader: &mut R,
127 discovery: ChunkDiscovery,
128) -> Result<WmoRoot, Box<dyn std::error::Error>> {
129 let mut root = WmoRoot {
130 version: 0,
131 n_materials: 0,
132 n_groups: 0,
133 n_portals: 0,
134 n_lights: 0,
135 n_doodad_names: 0,
136 n_doodad_defs: 0,
137 n_doodad_sets: 0,
138 ambient_color: [128, 128, 128, 255], wmo_id: 0,
140 bounding_box_min: [0.0; 3],
141 bounding_box_max: [0.0; 3],
142 flags: 0,
143 num_lod: 0,
144 textures: Vec::new(),
145 texture_offset_index_map: HashMap::new(),
146 materials: Vec::new(),
147 group_names: Vec::new(),
148 group_info: Vec::new(),
149 skybox: None,
150 portal_vertices: Vec::new(),
151 portals: Vec::new(),
152 portal_refs: Vec::new(),
153 visible_vertices: Vec::new(),
154 visible_blocks: Vec::new(),
155 lights: Vec::new(),
156 doodad_sets: Vec::new(),
157 doodad_names: Vec::new(),
158 doodad_defs: Vec::new(),
159 fogs: Vec::new(),
160 convex_volume_planes: Vec::new(),
161 uv_transforms: Vec::new(),
162 portal_extras: Vec::new(),
163 light_extensions: Vec::new(),
164 doodad_ids: Vec::new(),
165 new_materials: Vec::new(),
166 group_file_ids: Vec::new(),
167 };
168
169 for chunk_info in &discovery.chunks {
171 reader.seek(SeekFrom::Start(chunk_info.offset + 8))?;
173
174 match chunk_info.id.as_str() {
175 "MVER" => {
176 root.version = reader.read_le()?;
178 }
179 "MOHD" => {
180 let mohd = Mohd::read(reader)?;
182 root.n_materials = mohd.n_materials;
183 root.n_groups = mohd.n_groups;
184 root.n_portals = mohd.n_portals;
185 root.n_lights = mohd.n_lights;
186 root.n_doodad_names = mohd.n_doodad_names;
187 root.n_doodad_defs = mohd.n_doodad_defs;
188 root.n_doodad_sets = mohd.n_doodad_sets;
189 root.ambient_color = mohd.ambient_color;
190 root.wmo_id = mohd.wmo_id;
191 root.bounding_box_min = mohd.bounding_box_min;
192 root.bounding_box_max = mohd.bounding_box_max;
193 root.flags = mohd.flags;
194 root.num_lod = mohd.num_lod;
195 }
196 "MOTX" => {
197 let mut data = vec![0u8; chunk_info.size as usize];
199 reader.read_exact(&mut data)?;
200 let motx = Motx::parse(&data)?;
201 root.textures = motx.textures;
202 root.texture_offset_index_map = motx.texture_offset_index_map;
203 }
204 "MOMT" => {
205 let count = chunk_info.size / 64; for _ in 0..count {
208 root.materials.push(MomtEntry::read(reader)?);
209 }
210 }
211 "MOGN" => {
212 let mut data = vec![0u8; chunk_info.size as usize];
214 reader.read_exact(&mut data)?;
215 let mogn = Mogn::parse(&data)?;
216 root.group_names = mogn.names;
217 }
218 "MOGI" => {
219 let count = chunk_info.size / 32; for _ in 0..count {
222 root.group_info.push(MogiEntry::read(reader)?);
223 }
224 }
225 "MOSB" => {
226 let mut data = vec![0u8; chunk_info.size as usize];
228 reader.read_exact(&mut data)?;
229 let mosb = Mosb::parse(&data)?;
230 root.skybox = mosb.skybox;
231 }
232 "MOPV" => {
233 let count = chunk_info.size / 12; for _ in 0..count {
236 root.portal_vertices.push(MopvEntry::read(reader)?);
237 }
238 }
239 "MOPT" => {
240 let count = chunk_info.size / 20; for _ in 0..count {
243 root.portals.push(MoptEntry::read(reader)?);
244 }
245 }
246 "MOPR" => {
247 let count = chunk_info.size / 8; for _ in 0..count {
250 root.portal_refs.push(MoprEntry::read(reader)?);
251 }
252 }
253 "MOVV" => {
254 let count = chunk_info.size / 12; for _ in 0..count {
257 root.visible_vertices.push(MovvEntry::read(reader)?);
258 }
259 }
260 "MOVB" => {
261 let count = chunk_info.size / 4; for _ in 0..count {
264 root.visible_blocks.push(MovbEntry::read(reader)?);
265 }
266 }
267 "MOLT" => {
268 let count = chunk_info.size / 48; for _ in 0..count {
271 root.lights.push(MoltEntry::read(reader)?);
272 }
273 }
274 "MODS" => {
275 let count = chunk_info.size / 32; for _ in 0..count {
278 root.doodad_sets.push(ModsEntry::read(reader)?);
279 }
280 }
281 "MODN" => {
282 let mut data = vec![0u8; chunk_info.size as usize];
284 reader.read_exact(&mut data)?;
285 let modn = Modn::parse(&data)?;
286 root.doodad_names = modn.names;
287 }
288 "MODD" => {
289 let count = chunk_info.size / 40; for _ in 0..count {
292 root.doodad_defs.push(ModdEntry::read(reader)?);
293 }
294 }
295 "MFOG" => {
296 let count = chunk_info.size / 48; for _ in 0..count {
299 root.fogs.push(MfogEntry::read(reader)?);
300 }
301 }
302 "MCVP" => {
303 let count = chunk_info.size / 16; for _ in 0..count {
306 root.convex_volume_planes.push(McvpEntry::read(reader)?);
307 }
308 }
309 "MOUV" => {
310 let count = chunk_info.size / 16; for _ in 0..count {
313 root.uv_transforms.push(MouvEntry::read(reader)?);
314 }
315 }
316 "MOPE" => {
317 let count = chunk_info.size / 16; for _ in 0..count {
320 root.portal_extras.push(MopeEntry::read(reader)?);
321 }
322 }
323 "MOLV" => {
324 let count = chunk_info.size / 100; for _ in 0..count {
327 root.light_extensions.push(MolvEntry::read(reader)?);
328 }
329 }
330 "MODI" => {
331 let count = chunk_info.size / 4; for _ in 0..count {
334 root.doodad_ids.push(reader.read_le()?);
335 }
336 }
337 "MOM3" => {
338 let mut data = vec![0u8; chunk_info.size as usize];
341 reader.read_exact(&mut data)?;
342 root.new_materials.push(Mom3Entry { data });
343 }
344 "MOMO" => {
345 }
348 "GFID" => {
349 let count = chunk_info.size / 4; for _ in 0..count {
352 root.group_file_ids.push(reader.read_le()?);
353 }
354 }
355 _ => {
356 }
358 }
359 }
360
361 Ok(root)
362}