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}