comparison src/th06/std.rs @ 671:266911c0ccfd

Add a parser for the stage background format.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Mon, 12 Aug 2019 20:41:27 +0200
parents
children 6020e33d4fc4
comparison
equal deleted inserted replaced
670:cdb484115c5b 671:266911c0ccfd
1 //! STD background format support.
2
3 use nom::{
4 IResult,
5 bytes::complete::{tag, take_while_m_n},
6 number::complete::{le_u8, le_u16, le_u32, le_i32, le_f32},
7 sequence::tuple,
8 combinator::map,
9 };
10 use encoding_rs::SHIFT_JIS;
11 use std::collections::HashMap;
12
13 /// A float position in the 3D space.
14 #[derive(Debug, Clone)]
15 pub struct Position {
16 /// X component.
17 pub x: f32,
18
19 /// Y component.
20 pub y: f32,
21
22 /// Z component.
23 pub z: f32,
24 }
25
26 /// A 3D box around something.
27 #[derive(Debug, Clone)]
28 struct Box3D {
29 width: f32,
30 height: f32,
31 depth: f32,
32 }
33
34 /// A 2D box around something.
35 #[derive(Debug, Clone)]
36 pub struct Box2D {
37 width: f32,
38 height: f32,
39 }
40
41 /// A quad in the 3D space.
42 #[derive(Debug, Clone)]
43 pub struct Quad {
44 /// The anm script to run for this quad.
45 pub anm_script: u16,
46
47 /// The position of this quad in the 3D space.
48 pub pos: Position,
49
50 /// The size of this quad.
51 pub size_override: Box2D,
52 }
53
54 /// A model formed of multiple quads in space.
55 #[derive(Debug, Clone)]
56 pub struct Model {
57 /// TODO: find what that is.
58 pub unknown: u16,
59
60 /// The bounding box around this model.
61 pub bounding_box: [f32; 6],
62
63 /// The quads composing this model.
64 pub quads: Vec<Quad>,
65 }
66
67 /// An instance of a model.
68 #[derive(Debug, Clone)]
69 pub struct Instance {
70 /// The instance identifier.
71 pub id: u16,
72
73 /// Where to position the instance of this model.
74 pub pos: Position,
75 }
76
77 /// A single instruction, part of a `Script`.
78 #[derive(Debug, Clone)]
79 pub struct Call {
80 /// Time at which this instruction will be called.
81 pub time: u32,
82
83 /// The instruction to call.
84 pub instr: Instruction,
85 }
86
87 /// Parse a SHIFT_JIS byte string of length 128 into a String.
88 pub fn le_String(i: &[u8]) -> IResult<&[u8], String> {
89 let data = i.splitn(2, |c| *c == b'\0').nth(0).unwrap();
90 let (string, encoding, replaced) = SHIFT_JIS.decode(data);
91 Ok((&i[128..], string.into_owned()))
92 }
93
94 /// Main struct of the STD stage format.
95 #[derive(Debug, Clone)]
96 pub struct Stage {
97 /// The name of the stage.
98 pub name: String,
99
100 /// A list of (name, path) of background music.
101 // TODO: there are maximum four of them, and in practice never more than 2.
102 pub musics: Vec<Option<(String, String)>>,
103
104 /// List of models.
105 pub models: Vec<Model>,
106
107 /// List of instances.
108 pub instances: Vec<Instance>,
109
110 /// List of instructions in the script.
111 pub script: Vec<Call>,
112 }
113
114 impl Stage {
115 /// Parse a slice of bytes into an `Stage` struct.
116 pub fn from_slice(data: &[u8]) -> IResult<&[u8], Stage> {
117 parse_stage(data)
118 }
119 }
120
121 macro_rules! declare_stage_instructions {
122 ($($opcode:tt => fn $name:ident($($arg:ident: $arg_type:ident),*)),*,) => {
123 /// Available instructions in an `Stage`.
124 #[allow(missing_docs)]
125 #[derive(Debug, Clone, Copy)]
126 pub enum Instruction {
127 $(
128 $name($($arg_type),*)
129 ),*
130 }
131
132 fn parse_instruction_args(input: &[u8], opcode: u16) -> IResult<&[u8], Instruction> {
133 let mut i = &input[..];
134 let instr = match opcode {
135 $(
136 $opcode => {
137 $(
138 let (i2, $arg) = concat_idents!(le_, $arg_type)(i)?;
139 i = i2;
140 )*
141 Instruction::$name($($arg),*)
142 }
143 )*
144 _ => unreachable!()
145 };
146 Ok((i, instr))
147 }
148 };
149 }
150
151 declare_stage_instructions!{
152 0 => fn SetViewpos(x: f32, y: f32, z: f32),
153 1 => fn SetFog(r: u8, g: u8, b: u8, a: u8, near: f32, far: f32),
154 2 => fn SetViewpos2(x: f32, y: f32, z: f32),
155 3 => fn StartInterpolatingViewpos2(frame: i32, _unused: i32, _unused: i32),
156 4 => fn StartInterpolatingFog(frame: i32, _unused: i32, _unused: i32),
157 5 => fn Unknown(_unused: i32, _unused: i32, _unused: i32),
158 }
159
160 fn parse_stage(input: &[u8]) -> IResult<&[u8], Stage> {
161 let start_offset = 0;
162 let i = &input[start_offset..];
163 let (i, (num_models, num_faces, object_instances_offset, script_offset, _)) = tuple((le_u16, le_u16, le_u32, le_u32, tag(b"\0\0\0\0")))(i)?;
164 let object_instances_offset = object_instances_offset as usize;
165 let script_offset = script_offset as usize;
166
167 let (i, name) = le_String(i)?;
168 let (i, music_names) = map(tuple((le_String, le_String, le_String, le_String)), |(a, b, c, d)| [a, b, c, d])(i)?;
169 let (mut i, music_paths) = map(tuple((le_String, le_String, le_String, le_String)), |(a, b, c, d)| [a, b, c, d])(i)?;
170 let musics = music_names.iter().zip(&music_paths).map(|(name, path)| if name == " " { None } else { Some((name.clone(), path.clone())) }).collect();
171
172 let mut offsets = vec![];
173 for _ in 0..num_models {
174 let (i2, offset) = le_u32(i)?;
175 offsets.push(offset as usize);
176 i = i2;
177 }
178
179 // Read model definitions.
180 let mut models = vec![];
181 for offset in offsets {
182 let i = &input[offset..];
183 let (mut i, (id, unknown, x, y, z, width, height, depth)) = tuple((le_u16, le_u16, le_f32, le_f32, le_f32, le_f32, le_f32, le_f32))(i)?;
184 let bounding_box = [x, y, z, width, height, depth];
185 let mut quads = vec![];
186 loop {
187 let (i2, (unk1, size)) = tuple((le_u16, le_u16))(i)?;
188 if unk1 == 0xffff {
189 break;
190 }
191 assert_eq!(size, 0x1c);
192 let (i2, (anm_script, _, x, y, z, width, height)) = tuple((le_u16, tag(b"\0\0"), le_f32, le_f32, le_f32, le_f32, le_f32))(i2)?;
193 let quad = Quad {
194 anm_script,
195 pos: Position { x, y, z },
196 size_override: Box2D { width, height },
197 };
198 quads.push(quad);
199 i = i2;
200 }
201 let model = Model {
202 unknown,
203 bounding_box,
204 quads,
205 };
206 models.push(model);
207 }
208
209 // Read object usage.
210 let mut instances = vec![];
211 let mut i = &input[object_instances_offset..];
212 loop {
213 let (i2, (id, unknown, x, y, z)) = tuple((le_u16, le_u16, le_f32, le_f32, le_f32))(i)?;
214 if id == 0xffff && unknown == 0xffff {
215 break;
216 }
217 assert_eq!(unknown, 0x100);
218 let instance = Instance {
219 id,
220 pos: Position { x, y, z },
221 };
222 instances.push(instance);
223 i = i2;
224 }
225
226 // Read the script.
227 let mut script = vec![];
228 let mut i = &input[script_offset..];
229 loop {
230 let (i2, (time, opcode, size)) = tuple((le_u32, le_u16, le_u16))(i)?;
231 if time == 0xffffffff && opcode == 0xffff && size == 0xffff {
232 break;
233 }
234 assert_eq!(size, 12);
235 let data = &i2[..12];
236 let (data, instr) = parse_instruction_args(data, opcode)?;
237 assert_eq!(data.len(), 0);
238 println!("{:?}", instr);
239 script.push(Call { time, instr });
240 i = &i2[12..];
241 }
242
243 let stage = Stage {
244 name,
245 musics,
246 models,
247 instances,
248 script,
249 };
250 Ok((b"", stage))
251 }
252
253 #[cfg(test)]
254 mod tests {
255 use super::*;
256 use std::io::{self, Read};
257 use std::fs::File;
258
259 #[test]
260 fn std() {
261 let file = File::open("EoSD/ST/stage1.std").unwrap();
262 let mut file = io::BufReader::new(file);
263 let mut buf = vec![];
264 file.read_to_end(&mut buf).unwrap();
265 let (_, stage) = Stage::from_slice(&buf).unwrap();
266 assert_eq!(stage.name, "夢幻夜行絵巻 ~ Mystic Flier");
267 assert_eq!(stage.musics.len(), 4);
268 assert_eq!(stage.models.len(), 13);
269 assert_eq!(stage.instances.len(), 90);
270 assert_eq!(stage.script.len(), 21);
271 }
272 }