1use binrw::{binread, prelude::*, punctuated::Punctuated, NullString, VecArgs};
13use core::fmt;
14use std::path::Path;
15use writer::c_bool;
16
17#[cfg(feature = "serde_support")]
18use serde::{Deserialize, Serialize};
19
20mod writer;
21
22mod line_flags;
23pub use line_flags::LineFlags;
24
25fn read_punctuated<T: BinRead<Args = ()>, R: binrw::io::Read + binrw::io::Seek>(
26 reader: &mut R,
27 options: &binrw::ReadOptions,
28 args: VecArgs<()>,
29) -> BinResult<Vec<T>> {
30 Punctuated::<T, u8>::separated(reader, options, args).map(Punctuated::into_values)
31}
32
33#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
44#[derive(BinRead, Debug)]
45#[br(big, magic = b"\x00\x00\x00\x01\x0D\x01LVD\x31")]
46pub struct LvdFile {
47 pub collisions: Section<Collision>,
52
53 pub spawns: Section<Spawn>,
55
56 pub respawns: Section<Spawn>,
58
59 pub camera_boundary: Section<Bounds>,
61
62 pub blast_zone: Section<Bounds>,
64
65 pub enemy_generators: UnsupportedSection,
67
68 pub unk1: UnsupportedSection,
69 pub unk2: UnsupportedSection,
70 pub unk3: UnsupportedSection,
71
72 pub fs_area_cam: UnsupportedSection,
74
75 pub fs_cam_limit: UnsupportedSection,
77
78 pub damage_shapes: Section<DamageShape>,
80
81 pub item_spawners: Section<ItemSpawner>,
83
84 pub ptrainer_ranges: Section<PokemonTrainerRange>, pub ptrainer_platforms: Section<PokemonTrainerPlatform>, pub general_shapes: Section<GeneralShape>,
92
93 pub general_points: Section<Point>,
96
97 pub unk4: UnsupportedSection,
98 pub unk5: UnsupportedSection,
99 pub unk6: UnsupportedSection,
100 pub unk7: UnsupportedSection,
101
102 pub shrunken_camera_boundary: Section<Bounds>, pub shrunken_blast_zone: Section<Bounds>, }
108
109#[derive(BinRead, Debug)]
111#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
112pub struct LvdEntry {
113 #[br(pad_before = 1, pad_size_to = 0x38, map = NullString::into_string)]
115 pub name: String,
116
117 #[br(pad_before = 1, pad_size_to = 0x40, map = NullString::into_string)]
120 pub subname: String,
121
122 #[br(pad_before = 1)]
125 pub start_pos: Vector3,
126
127 #[br(map = cbool)]
129 pub use_start: bool,
130
131 #[br(pad_before = 1)]
132 pub unk: u32,
133
134 #[br(pad_before = 1)]
135 pub unk2: Vector3,
136
137 pub unk3: u32,
138
139 #[br(pad_before = 1, pad_size_to = 0x40, map = NullString::into_string)]
141 pub bone_name: String,
142}
143
144#[binread]
147#[derive(Debug)]
148#[br(magic = b"\x04\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02")]
149#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
150pub struct Collision {
151 pub entry: LvdEntry,
153
154 pub col_flags: ColFlags,
157
158 #[br(temp, pad_before = 1)]
159 pub vertex_count: u32,
160
161 #[br(pad_before = 1, parse_with = read_punctuated, count = vertex_count)]
163 pub vertices: Vec<Vector2>,
164
165 #[br(temp, pad_before = 1)]
166 pub normal_count: u32,
167
168 #[br(pad_before = 1, parse_with = read_punctuated, count = normal_count)]
172 pub normals: Vec<Vector2>,
173
174 #[br(temp, pad_before = 1)]
175 pub cliff_count: u32,
176
177 #[br(count = cliff_count)]
181 pub cliffs: Vec<CollisionCliff>,
182
183 #[br(temp, pad_before = 1)]
184 pub line_count: u32,
185
186 #[br(pad_before = 1, parse_with = read_punctuated, count = line_count)]
190 pub materials: Vec<CollisionMaterial>,
191
192 #[br(temp, pad_before = 1)]
193 pub unk_count: u32,
194
195 #[br(count = unk_count)]
196 pub unknowns: Vec<UnknownEntry>,
197}
198
199#[derive(BinRead, BinWrite, Debug)]
202#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
203pub struct ColFlags {
204 #[br(map = cbool)]
205 #[binwrite(map(c_bool))]
206 pub flag1: bool,
207
208 #[br(map = cbool)]
210 #[binwrite(map(c_bool))]
211 pub rig_col: bool,
212
213 #[br(map = cbool)]
214 #[binwrite(map(c_bool))]
215 pub flag3: bool,
216
217 #[br(map = cbool)]
219 #[binwrite(map(c_bool))]
220 pub drop_through: bool,
221}
222
223#[derive(BinRead, Debug)]
228#[br(magic = b"\x03\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02")]
229#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
230pub struct CollisionCliff {
231 pub entry: LvdEntry,
232 #[br(pad_before = 1)]
233 pub pos: Vector2,
234 pub angle: f32,
235 pub line_index: i32,
236}
237
238#[derive(BinRead, Debug)]
243#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
244pub struct CollisionMaterial {
245 #[br(pad_after = 4)]
247 pub line_material: GroundCollAttr,
248
249 pub line_flags: LineFlags,
252}
253
254#[allow(non_camel_case_types)]
256#[derive(BinRead, Debug, Clone, Copy)]
257#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
258#[br(repr(u32))]
259pub enum GroundCollAttr {
260 GROUND_COLL_ATTR_NONE = 0,
261 GROUND_COLL_ATTR_ROCK = 1,
262 GROUND_COLL_ATTR_GRASS = 2,
263 GROUND_COLL_ATTR_SOIL = 3,
264 GROUND_COLL_ATTR_WOOD = 4,
265 GROUND_COLL_ATTR_IRON = 5,
266 GROUND_COLL_ATTR_NIBUIRON = 6,
267 GROUND_COLL_ATTR_CARPET = 7,
268 GROUND_COLL_ATTR_NUMENUME = 8,
269 GROUND_COLL_ATTR_CREATURE = 9,
270 GROUND_COLL_ATTR_ASASE = 10,
271 GROUND_COLL_ATTR_SOFT = 11,
272 GROUND_COLL_ATTR_TURUTURU = 12,
273 GROUND_COLL_ATTR_SNOW = 13,
274 GROUND_COLL_ATTR_ICE = 14,
275 GROUND_COLL_ATTR_GAMEWATCH = 15,
276 GROUND_COLL_ATTR_OIL = 16,
277 GROUND_COLL_ATTR_DANBOURU = 17,
278 GROUND_COLL_ATTR_DAMAGE1 = 18,
279 GROUND_COLL_ATTR_DAMAGE2 = 19,
280 GROUND_COLL_ATTR_DAMAGE3 = 20,
281 GROUND_COLL_ATTR_PLANKTON = 21,
282 GROUND_COLL_ATTR_CLOUD = 22,
283 GROUND_COLL_ATTR_AKUUKAN = 23,
284 GROUND_COLL_ATTR_BRICK = 24,
285 GROUND_COLL_ATTR_NOATTR = 25,
286 GROUND_COLL_ATTR_MARIO = 26,
287 GROUND_COLL_ATTR_WIRENETTING = 27,
288 GROUND_COLL_ATTR_SAND = 28,
289 GROUND_COLL_ATTR_HOMERUN = 29,
290 GROUND_COLL_ATTR_ASASE_EARTH = 30,
291 GROUND_COLL_ATTR_DEATH = 31,
292 GROUND_COLL_ATTR_RINGMAT = 32,
293 GROUND_COLL_ATTR_GLASS = 33,
294 GROUND_COLL_ATTR_SLIPDX = 34,
295 GROUND_COLL_ATTR_SP_POISON = 35,
296 GROUND_COLL_ATTR_SP_FLAME = 36,
297 GROUND_COLL_ATTR_SP_ELECTRIC_SHOCK = 37,
298 GROUND_COLL_ATTR_SP_SLEEP = 38,
299 GROUND_COLL_ATTR_SP_FREEZING = 39,
300 GROUND_COLL_ATTR_SP_ADHESION = 40,
301 GROUND_COLL_ATTR_ICE_NO_SLIP = 41,
302 GROUND_COLL_ATTR_CLOUD_NO_THROUGH = 42,
303 GROUND_COLL_ATTR_JACK_MEMENTOES = 43,
304}
305
306#[derive(BinRead, Debug)]
309#[br(magic = b"\x01\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02")]
310#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
311pub struct DamageShape {
312 pub entry: LvdEntry,
314
315 #[br(pad_before = 1)]
316 pub unk1: u32,
317
318 #[br(pad_after = 1)]
319 pub unk2: [f32; 8],
320}
321
322#[derive(BinRead, Debug)]
324#[br(magic = b"\x01\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02")]
325#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
326pub struct GeneralShape {
327 pub entry: LvdEntry,
329
330 #[br(pad_before = 1)]
331 pub unk1: u32,
332
333 pub shape: LvdShape,
334}
335
336#[derive(BinRead, Debug)]
338#[br(magic = b"\x02\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02")]
339#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
340pub struct UnknownEntry {
341 pub entry: LvdEntry,
343
344 pub unk: u32,
345
346 #[br(pad_before = 1, pad_size_to = 0x40, map = NullString::into_string)]
347 pub string: String,
348
349 pub unk2: Vector2,
350
351 pub unk3: Vector2,
352
353 pub unk4: [u8; 8],
354}
355
356#[derive(BinRead, Debug)]
358#[br(magic = b"\x02\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02")]
359#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
360pub struct Spawn {
361 pub entry: LvdEntry,
363
364 #[br(pad_before = 1)]
366 pub pos: Vector2,
367}
368
369#[derive(BinRead, Debug)]
372#[br(magic = b"\x02\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02")]
373#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
374pub struct Bounds {
375 pub entry: LvdEntry,
377
378 #[br(pad_before = 1)]
379 pub left: f32,
380 pub right: f32,
381 pub top: f32,
382 pub bottom: f32,
383}
384
385#[binread]
386#[derive(Debug)]
387#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
388#[br(magic = b"\x01\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02")]
389pub struct ItemSpawner {
390 pub entry: LvdEntry,
392
393 #[br(pad_before = 1)]
395 pub id: u32,
396
397 pub unk: u8,
398
399 #[br(temp, pad_before = 1)]
400 pub section_count: u32,
401
402 #[br(
404 pad_before = if section_count > 0 {
405 1
406 } else {
407 0
408 },
409 parse_with = read_punctuated,
410 count = section_count
411 )]
412 pub sections: Vec<LvdShape>,
413}
414
415#[binread]
419#[derive(Debug)]
420#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
421#[br(magic = b"\x04\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02")]
422pub struct PokemonTrainerRange {
423 pub entry: LvdEntry,
425
426 #[br(pad_before = 1)]
428 pub boundary_min: Vector3,
429
430 #[br(pad_before = 1)]
432 pub boundary_max: Vector3,
433
434 #[br(temp, pad_before = 1)]
435 pub trainer_count: u32,
436
437 #[br(
439 pad_before = if trainer_count > 0 {
440 1
441 } else {
442 0
443 },
444 parse_with = read_punctuated,
445 count = trainer_count
446 )]
447 pub trainers: Vec<Vector3>,
448
449 #[br(pad_before = 1, pad_size_to = 0x40, map = NullString::into_string)]
452 pub platform_name: String,
453
454 #[br(pad_before = 1, pad_size_to = 0x40, map = NullString::into_string)]
456 pub sub_name: String,
457}
458
459#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
463#[derive(BinRead, Debug)]
464#[br(magic = b"\x01\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02")]
465pub struct PokemonTrainerPlatform {
466 pub entry: LvdEntry,
468
469 #[br(pad_before = 1)]
471 pub pos: Vector3,
472}
473
474#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
478#[derive(BinRead, Debug)]
479#[br(magic = b"\x01\x04\x01\x01\x77\x35\xBB\x75\x00\x00\x00\x02")]
480pub struct Point {
481 pub entry: LvdEntry,
483
484 #[br(pad_before = 1)]
486 pub id: u32,
487
488 #[br(pad_before = 1)]
490 pub ty: u32,
491
492 #[br(pad_after = 0x10)]
494 pub pos: Vector3,
495}
496
497#[binread]
500#[derive(Debug)]
501#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
502pub enum LvdShape {
503 #[br(magic = b"\x03\0\0\0\x01")]
505 Point {
506 x: f32,
507 y: f32,
508
509 #[br(temp, pad_before = 8)]
510 unk: u8,
511
512 #[br(temp, pad_before = 1)]
514 point_count: u32,
515 },
516
517 #[br(magic = b"\x03\0\0\0\x02")]
519 Circle {
520 x: f32,
521 y: f32,
522 radius: f32,
523
524 #[br(temp, pad_before = 4)]
525 unk: u8,
526
527 #[br(temp, pad_after = 1)]
529 point_count: u32,
530 },
531
532 #[br(magic = b"\x03\0\0\0\x03")]
534 Rectangle {
535 left: f32,
536 right: f32,
537 bottom: f32,
538 top: f32,
539
540 #[br(temp)]
541 unk: u8,
542
543 #[br(temp, pad_before = 1)]
545 point_count: u32,
546 },
547
548 #[br(magic = b"\x03\0\0\0\x04")]
550 Path {
551 #[br(temp, pad_before = 0x12)]
552 point_count: u32,
553
554 #[br(pad_before = 1, parse_with = read_punctuated, count = point_count)]
555 points: Vec<Vector2>,
556 },
557
558 Invalid {
560 magic: u32,
561 },
562}
563
564#[derive(BinRead, Debug)]
567#[br(assert(count == 0))]
568#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
569pub struct UnsupportedSection {
570 #[br(pad_before = 1)]
571 pub count: u32,
572}
573
574#[binread]
577#[derive(Debug)]
578#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
579#[cfg_attr(feature = "serde_support", serde(transparent))]
580pub struct Section<T: BinRead<Args = ()>> {
581 #[br(temp, pad_before = 1)]
582 pub count: u32,
583
584 #[br(count = count)]
585 pub data: Vec<T>,
586}
587
588impl<T: BinRead<Args = ()>> core::ops::Deref for Section<T> {
589 type Target = Vec<T>;
590
591 fn deref(&self) -> &Self::Target {
592 &self.data
593 }
594}
595
596impl<T: BinRead<Args = ()>> core::ops::DerefMut for Section<T> {
597 fn deref_mut(&mut self) -> &mut Self::Target {
598 &mut self.data
599 }
600}
601
602#[derive(BinRead, BinWrite)]
604#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
605pub struct Vector2 {
606 pub x: f32,
607 pub y: f32,
608}
609
610impl fmt::Debug for Vector2 {
611 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
612 f.debug_tuple("Vec").field(&self.x).field(&self.y).finish()
613 }
614}
615
616#[derive(BinRead, BinWrite)]
618#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
619pub struct Vector3 {
620 pub x: f32,
621 pub y: f32,
622 pub z: f32,
623}
624
625impl fmt::Debug for Vector3 {
626 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
627 f.debug_tuple("Vec")
628 .field(&self.x)
629 .field(&self.y)
630 .field(&self.z)
631 .finish()
632 }
633}
634
635fn cbool(x: u8) -> bool {
636 x != 0
637}
638
639impl LvdFile {
640 pub fn open<P: AsRef<Path>>(path: P) -> BinResult<Self> {
648 let mut f = std::io::BufReader::new(std::fs::File::open(path.as_ref())?);
650
651 f.read_be()
652 }
653}
654
655pub fn open<P: AsRef<Path>>(path: P) -> BinResult<LvdFile> {
661 let mut f = std::io::BufReader::new(std::fs::File::open(path.as_ref())?);
663
664 f.read_be()
665}
666
667#[cfg(test)]
668mod tests {
669 use super::*;
670
671 #[test]
672 fn test_parse() {
673 let mut f = std::fs::File::open("/home/jam/Downloads/param/pickel_world_00.lvd").unwrap();
674
675 let x: LvdFile = f.read_be().unwrap();
676 }
679}