1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use crate::{darksiders1::gfc, utils::parsing::derailed};
use byteordered::{ByteOrdered, Endianness};
use failure::Error;
use std::{
io::{Read, Seek},
rc::Rc,
};
pub fn read(file: impl Read + Seek) -> Result<Vec<(String, gfc::Object)>, Error> {
let mut files = Vec::new();
let mut world_package = ByteOrdered::new(file, Endianness::Big);
let world_files = gfc::WorldFactory::read_header(&mut world_package)?;
for world_info in &world_files {
let world_path = world_info.name.clone();
let world = gfc::WorldFactory::read_world(
&mut world_package,
&world_files,
&world_info.name,
)?;
let world = Rc::new(world);
files.push((world_path, world.clone()));
let region_data = match world.get_property("RegionData") {
Some(x) => x.as_array().ok_or_else(derailed)?,
None => &[],
};
for region_data in region_data {
let region_data = region_data.as_object().ok_or_else(derailed)?;
let region_id = region_data
.get_property("ID")
.and_then(gfc::Value::as_int)
.ok_or_else(derailed)?;
let region_name = region_data
.get_property("Name")
.and_then(gfc::Value::as_hstring)
.ok_or_else(derailed)?;
let region_layers = region_data
.get_property("Layers")
.and_then(gfc::Value::as_array)
.ok_or_else(derailed)?;
let region_path = format!("{}/{}", world_info.name, region_name);
let region = gfc::WorldFactory::read_region(
&mut world_package,
&world_files,
&world,
region_id,
)?;
let region = Rc::new(region);
files.push((region_path, region.clone()));
for layer_data in region_layers {
let layer_data = layer_data.as_object().ok_or_else(derailed)?;
let layer_id = layer_data
.get_property("ID")
.and_then(gfc::Value::as_int)
.ok_or_else(derailed)?;
let layer_name = layer_data
.get_property("Name")
.and_then(gfc::Value::as_hstring)
.ok_or_else(derailed)?;
let layer_path =
format!("{}/{}/{}", world_info.name, region_name, layer_name);
let layer = gfc::WorldFactory::read_layer(
&mut world_package,
&world_files,
&world,
region_data,
layer_id,
)?;
files.push((layer_path, Rc::new(layer)));
}
}
}
Ok(files
.into_iter()
.map(|(p, o)| (p, Rc::try_unwrap(o).unwrap()))
.collect())
}
#[cfg(test)]
mod tests {
use crate::{darksiders1::gfc, utils::parsing::derailed, worlds};
use failure::Error;
use std::{
fs,
io::{self, Read, Seek},
};
#[test]
fn smoke_test() -> Result<(), Error> {
let worlds = open_fixture()?;
let files = worlds::read(worlds)?;
let (world_name, world) = &files[0];
assert_eq!(world_name, "overworld");
assert_eq!(
world
.get_property("RegionData")
.and_then(gfc::Value::as_array)
.ok_or_else(derailed)?
.len(),
227,
);
Ok(())
}
fn open_fixture() -> io::Result<impl Read + Seek> {
let root = env!("CARGO_MANIFEST_DIR");
let path = format!("{}/src/worlds/fixtures/worlds.mnfst", root);
let file = fs::File::open(&path)?;
Ok(io::BufReader::new(file))
}
}