lvd/
writer.rs

1use crate::*;
2use std::io::{BufWriter, Seek, Write};
3use std::path::Path;
4
5use binrw::{BinWrite, WriteOptions};
6
7impl LvdFile {
8    const MAGIC: &'static [u8] = b"\x00\x00\x00\x01\x0D\x01\x4C\x56\x44\x31";
9
10    pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<(), binrw::Error> {
11        let mut file = BufWriter::new(std::fs::File::create(path.as_ref())?);
12
13        self.write(&mut file)
14    }
15
16    pub fn write<W: Write + Seek>(&self, writer: &mut W) -> Result<(), binrw::Error> {
17        (
18            Self::MAGIC,
19            &self.collisions,
20            &self.spawns,
21            &self.respawns,
22            &self.camera_boundary,
23            &self.blast_zone,
24            (&self.enemy_generators, &self.unk1, &self.unk2, &self.unk3),
25            &self.fs_area_cam,
26            &self.fs_cam_limit,
27            &self.damage_shapes,
28            &self.item_spawners,
29            &self.ptrainer_ranges,
30            &self.ptrainer_platforms,
31            &self.general_shapes,
32            &self.general_points,
33            (&self.unk4, &self.unk5, &self.unk6, &self.unk7),
34            &self.shrunken_camera_boundary,
35            &self.shrunken_blast_zone,
36        )
37            .write_options(writer, &binrw::WriteOptions::new(binrw::Endian::Big), ())
38    }
39}
40
41impl<T: BinWrite<Args = ()> + BinRead<Args = ()>> BinWrite for Section<T> {
42    type Args = ();
43
44    fn write_options<W: Write + Seek>(
45        &self,
46        writer: &mut W,
47        options: &WriteOptions,
48        _: Self::Args,
49    ) -> Result<(), binrw::Error> {
50        (1u8, self.data.len() as u32, &self.data).write_options(writer, options, ())
51    }
52}
53
54impl BinWrite for UnsupportedSection {
55    type Args = ();
56
57    fn write_options<W: Write + Seek>(
58        &self,
59        writer: &mut W,
60        options: &WriteOptions,
61        _: Self::Args,
62    ) -> Result<(), binrw::Error> {
63        (1u8, 0u32).write_options(writer, options, ())
64    }
65}
66
67struct LvdList<'a, T>(&'a Vec<T>);
68
69impl<'a, T: BinWrite<Args = ()>> BinWrite for LvdList<'a, T> {
70    type Args = ();
71
72    fn write_options<W: Write + Seek>(
73        &self,
74        writer: &mut W,
75        options: &WriteOptions,
76        _: Self::Args,
77    ) -> Result<(), binrw::Error> {
78        let mut iter = self.0.iter();
79        if let Some(first) = iter.next() {
80            first.write_options(writer, options, ())?;
81
82            for item in iter {
83                1u8.write_options(writer, options, ())?;
84                item.write_options(writer, options, ())?;
85            }
86        }
87
88        Ok(())
89    }
90}
91
92impl BinWrite for CollisionMaterial {
93    type Args = ();
94
95    fn write_options<W: Write + Seek>(
96        &self,
97        writer: &mut W,
98        options: &WriteOptions,
99        _: Self::Args,
100    ) -> Result<(), binrw::Error> {
101        (self.line_material as u32, 0u32, &self.line_flags).write_options(writer, options, ())
102    }
103}
104
105impl BinWrite for Collision {
106    type Args = ();
107
108    fn write_options<W: Write + Seek>(
109        &self,
110        writer: &mut W,
111        options: &WriteOptions,
112        _: Self::Args,
113    ) -> Result<(), binrw::Error> {
114        (
115            (
116                b"\x04\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02",
117                &self.entry,
118                &self.col_flags,
119            ),
120            1u8,
121            self.vertices.len() as u32,
122            1u8,
123            LvdList(&self.vertices),
124            1u8,
125            self.normals.len() as u32,
126            1u8,
127            LvdList(&self.normals),
128            1u8,
129            self.cliffs.len() as u32,
130            &self.cliffs,
131            1u8,
132            self.materials.len() as u32,
133            1u8,
134            LvdList(&self.materials),
135            1u8,
136            self.unknowns.len() as u32,
137            &self.unknowns,
138        )
139            .write_options(writer, options, ())
140    }
141}
142
143impl BinWrite for CollisionCliff {
144    type Args = ();
145
146    fn write_options<W: Write + Seek>(
147        &self,
148        writer: &mut W,
149        options: &WriteOptions,
150        _: Self::Args,
151    ) -> Result<(), binrw::Error> {
152        (
153            b"\x03\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02",
154            &self.entry,
155            1u8,
156            &self.pos,
157            &self.angle,
158            &self.line_index,
159        )
160            .write_options(writer, options, ())
161    }
162}
163
164impl BinWrite for UnknownEntry {
165    type Args = ();
166
167    fn write_options<W: Write + Seek>(
168        &self,
169        writer: &mut W,
170        options: &WriteOptions,
171        _: Self::Args,
172    ) -> Result<(), binrw::Error> {
173        (
174            b"\x02\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02",
175            &self.entry,
176            self.unk,
177            1u8,
178            string40(&self.string),
179            &self.unk2,
180            &self.unk3,
181            &self.unk4,
182        )
183            .write_options(writer, options, ())
184    }
185}
186
187#[derive(BinWrite)]
188struct String38<'a> {
189    #[bw(map(cstr), pad_size_to(0x38))]
190    s: &'a str,
191}
192
193fn string38(s: &str) -> String38 {
194    String38 { s }
195}
196
197#[derive(BinWrite)]
198struct String40<'a> {
199    #[bw(map(cstr), pad_size_to(0x40))]
200    s: &'a str,
201}
202
203fn string40(s: &str) -> String40 {
204    String40 { s }
205}
206
207fn cstr(s: &&str) -> Vec<u8> {
208    s.bytes().chain(std::iter::once(0u8)).collect()
209}
210
211pub(crate) fn c_bool(&x: &bool) -> u8 {
212    if x {
213        1
214    } else {
215        0
216    }
217}
218
219impl BinWrite for LvdEntry {
220    type Args = ();
221
222    fn write_options<W: Write + Seek>(
223        &self,
224        writer: &mut W,
225        options: &WriteOptions,
226        _: Self::Args,
227    ) -> Result<(), binrw::Error> {
228        (
229            1u8,
230            string38(&self.name),
231            1u8,
232            string40(&self.subname),
233            1u8,
234            &self.start_pos,
235            c_bool(&self.use_start),
236            1u8,
237            self.unk,
238            1u8,
239            &self.unk2,
240            self.unk3,
241            1u8,
242            string40(&self.bone_name),
243        )
244            .write_options(writer, options, ())
245    }
246}
247
248impl BinWrite for Spawn {
249    type Args = ();
250
251    fn write_options<W: Write + Seek>(
252        &self,
253        writer: &mut W,
254        options: &WriteOptions,
255        _: Self::Args,
256    ) -> Result<(), binrw::Error> {
257        (
258            b"\x02\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02",
259            &self.entry,
260            1u8,
261            &self.pos,
262        )
263            .write_options(writer, options, ())
264    }
265}
266
267impl BinWrite for Bounds {
268    type Args = ();
269
270    fn write_options<W: Write + Seek>(
271        &self,
272        writer: &mut W,
273        options: &WriteOptions,
274        _: Self::Args,
275    ) -> Result<(), binrw::Error> {
276        (
277            b"\x02\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02",
278            &self.entry,
279            1u8,
280            self.left,
281            self.right,
282            self.top,
283            self.bottom,
284        )
285            .write_options(writer, options, ())
286    }
287}
288
289impl BinWrite for ItemSpawner {
290    type Args = ();
291
292    fn write_options<W: Write + Seek>(
293        &self,
294        writer: &mut W,
295        options: &WriteOptions,
296        _: Self::Args,
297    ) -> Result<(), binrw::Error> {
298        (
299            b"\x01\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02",
300            &self.entry,
301            1u8,
302            self.id,
303            self.unk,
304            1u8,
305            self.sections.len() as u32,
306        )
307            .write_options(writer, options, ())?;
308
309        if !self.sections.is_empty() {
310            1u8.write_options(writer, options, ())?;
311        }
312
313        LvdList(&self.sections).write_options(writer, options, ())
314    }
315}
316
317impl BinWrite for LvdShape {
318    type Args = ();
319
320    fn write_options<W: Write + Seek>(
321        &self,
322        writer: &mut W,
323        options: &WriteOptions,
324        _: Self::Args,
325    ) -> Result<(), binrw::Error> {
326        match self {
327            Self::Point { x, y } => (b"\x03\0\0\0\x01", x, y, [0u8; 8], 1u8, 1u8, 0u32)
328                .write_options(writer, options, ()),
329            Self::Circle { x, y, radius } => {
330                (b"\x03\0\0\0\x02", x, y, radius, [0u8; 4], 1u8, 1u8, 0u32).write_options(
331                    writer,
332                    options,
333                    (),
334                )
335            }
336            Self::Rectangle {
337                left,
338                right,
339                bottom,
340                top,
341            } => (b"\x03\0\0\0\x03", left, right, bottom, top, 1u8, 1u8, 0u32).write_options(
342                writer,
343                options,
344                (),
345            ),
346            Self::Path { points } => (
347                b"\x03\0\0\0\x04",
348                [0u8; 0x10],
349                1u8,
350                1u8,
351                points.len() as u32,
352                1u8,
353                LvdList(points),
354            )
355                .write_options(writer, options, ()),
356            _ => unreachable!(),
357        }
358    }
359}
360
361impl BinWrite for PokemonTrainerRange {
362    type Args = ();
363
364    fn write_options<W: Write + Seek>(
365        &self,
366        writer: &mut W,
367        options: &WriteOptions,
368        _: Self::Args,
369    ) -> Result<(), binrw::Error> {
370        (
371            b"\x04\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02",
372            &self.entry,
373            1u8,
374            &self.boundary_min,
375            1u8,
376            &self.boundary_max,
377            1u8,
378            self.trainers.len() as u32,
379        )
380            .write_options(writer, options, ())?;
381
382        if !self.trainers.is_empty() {
383            1u8.write_options(writer, options, ())?;
384        }
385
386        (
387            LvdList(&self.trainers),
388            1u8,
389            string40(&self.platform_name),
390            1u8,
391            string40(&self.sub_name),
392        )
393            .write_options(writer, options, ())
394    }
395}
396
397impl BinWrite for PokemonTrainerPlatform {
398    type Args = ();
399
400    fn write_options<W: Write + Seek>(
401        &self,
402        writer: &mut W,
403        options: &WriteOptions,
404        _: Self::Args,
405    ) -> Result<(), binrw::Error> {
406        (
407            b"\x01\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02",
408            &self.entry,
409            1u8,
410            &self.pos,
411        )
412            .write_options(writer, options, ())
413    }
414}
415
416impl BinWrite for Point {
417    type Args = ();
418
419    fn write_options<W: Write + Seek>(
420        &self,
421        writer: &mut W,
422        options: &WriteOptions,
423        _: Self::Args,
424    ) -> Result<(), binrw::Error> {
425        (
426            b"\x01\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02",
427            &self.entry,
428            1u8,
429            self.id,
430            1u8,
431            self.ty,
432            &self.pos,
433            [0u8; 0x10],
434        )
435            .write_options(writer, options, ())
436    }
437}
438
439impl BinWrite for DamageShape {
440    type Args = ();
441
442    fn write_options<W: Write + Seek>(
443        &self,
444        writer: &mut W,
445        options: &WriteOptions,
446        _: Self::Args,
447    ) -> Result<(), binrw::Error> {
448        (
449            b"\x01\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02",
450            &self.entry,
451            1u8,
452            self.unk1,
453            self.unk2,
454            0u8,
455        )
456            .write_options(writer, options, ())
457    }
458}
459
460impl BinWrite for GeneralShape {
461    type Args = ();
462
463    fn write_options<W: Write + Seek>(
464        &self,
465        writer: &mut W,
466        options: &WriteOptions,
467        _: Self::Args,
468    ) -> Result<(), binrw::Error> {
469        (
470            b"\x01\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02",
471            &self.entry,
472            1u8,
473            self.unk1,
474            &self.shape,
475        )
476            .write_options(writer, options, ())
477    }
478}
479
480#[cfg(test)]
481mod tests {
482    use super::*;
483
484    #[test]
485    fn test_round_trip() {
486        let lvd = LvdFile::open("/home/jam/Downloads/param/pickel_world_00.lvd").unwrap();
487
488        lvd.save("test_out.lvd").unwrap();
489    }
490}