comparison formats/src/th06/std.rs @ 757:21b186be2590

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