lvd/
lib.rs

1//! A crate for working with level data files from the Super Smash Brothers Series.
2//!
3//! ## Minimal Example
4//!
5//! ```no_run
6//! let file = lvd::open("./pickel_world_00.lvd").unwrap();
7//!
8//! println!("{:?}", &file.collisions[0].vertices[0]);
9//!
10//! file.save("./pickel_world_00_edited.lvd").unwrap();
11//! ```
12use 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/// The top-level structure representing the LVD file containing all the sections included within
34/// it.
35///
36/// ```no_run
37/// let file = lvd::open("./pickel_world_00.lvd").unwrap();
38///
39/// println!("{:?}", &file.collisions[0].vertices[0]);
40///
41/// file.save("./pickel_world_00_edited.lvd").unwrap();
42/// ```
43#[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    /// Collisions for the various platforms of the stage.
48    ///
49    /// These can have arbitrary 2d shapes with ledges, various types of collisions, different
50    /// properties, etc. See [`Collision`] for more info.
51    pub collisions: Section<Collision>,
52
53    /// The initial spawnpoints of characters when starting the match
54    pub spawns: Section<Spawn>,
55
56    /// The points in space where respawn platforms arrive
57    pub respawns: Section<Spawn>,
58
59    /// The furthest edges of the stage that the camera can pan to
60    pub camera_boundary: Section<Bounds>,
61
62    /// The bounds of how far fighters can go before they die from the blast zones
63    pub blast_zone: Section<Bounds>,
64
65    /// Locations where enemies can be spawned
66    pub enemy_generators: UnsupportedSection,
67
68    pub unk1: UnsupportedSection,
69    pub unk2: UnsupportedSection,
70    pub unk3: UnsupportedSection,
71
72    /// Points where the final smash area camera is placed
73    pub fs_area_cam: UnsupportedSection,
74
75    /// Limit to panning of the final smash camera
76    pub fs_cam_limit: UnsupportedSection,
77
78    /// Places in the stage with hurtboxes attackable by players
79    pub damage_shapes: Section<DamageShape>,
80
81    /// Areas of the stage in which items can spawn
82    pub item_spawners: Section<ItemSpawner>,
83
84    /// Areas within the stage that pokemon trainers can move around on
85    pub ptrainer_ranges: Section<PokemonTrainerRange>, // version 13 only
86
87    /// Platforms where pokemon trainers hover
88    pub ptrainer_platforms: Section<PokemonTrainerPlatform>, // version 13 only
89
90    /// Generic shapes describing features of the stage
91    pub general_shapes: Section<GeneralShape>,
92
93    /// Generic points describing locations on the stage (used for final smash locations like Ike's, for
94    /// example)
95    pub general_points: Section<Point>,
96
97    pub unk4: UnsupportedSection,
98    pub unk5: UnsupportedSection,
99    pub unk6: UnsupportedSection,
100    pub unk7: UnsupportedSection,
101
102    /// Camera boundary but after it has shrunken for sudden death
103    pub shrunken_camera_boundary: Section<Bounds>, // version 13 only
104
105    /// Blast zone boundary but after it has shrunken for sudden death
106    pub shrunken_blast_zone: Section<Bounds>,      // version 13 only
107}
108
109/// The generic object data all entries in an LVD file have
110#[derive(BinRead, Debug)]
111#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
112pub struct LvdEntry {
113    /// The name of the entry
114    #[br(pad_before = 1, pad_size_to = 0x38, map = NullString::into_string)]
115    pub name: String,
116
117    /// Additional name-like data that specifies certain properties about the entry. Tends to
118    /// affect behavior of the object.
119    #[br(pad_before = 1, pad_size_to = 0x40, map = NullString::into_string)]
120    pub subname: String,
121
122    /// The position the entry starts at, also the origin of the object. Any coordinates under the
123    /// object (such as vertices) are **not** relative to this and are absolute world coordinates.
124    #[br(pad_before = 1)]
125    pub start_pos: Vector3,
126
127    /// Whether or not the `start_pos` data is utilized in the positioning of the object
128    #[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    /// The name of the bone the given object is bound to
140    #[br(pad_before = 1, pad_size_to = 0x40, map = NullString::into_string)]
141    pub bone_name: String,
142}
143
144/// A single collision in the level. Includes shape, ledges, collision material, flags about the
145/// collision properties, and all other properties of the collision.
146#[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    /// The generic object data of the collision
152    pub entry: LvdEntry,
153
154    /// The flags specifying certain aspects of the collision behavior (whether the collision is
155    /// rigged to an animated bone, whether the platform can be dropped through, etc.)
156    pub col_flags: ColFlags,
157
158    #[br(temp, pad_before = 1)]
159    pub vertex_count: u32,
160
161    /// The vertices describing the shape of the collision
162    #[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    /// The collision "normals", these are unit vectors describing the direction the collision is
169    /// solid from. It effectively points "outward". So a normal of `Vector2(0.0, 1.0)` means the
170    /// collision is solid from the top side.
171    #[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    /// Data describing grabbable ledges, internally called "cliffs", present in this collision.
178    /// This describes how far away the ledge can be grabbed from as well as what vertex/edge the
179    /// cliff is a part of.
180    #[br(count = cliff_count)]
181    pub cliffs: Vec<CollisionCliff>,
182
183    #[br(temp, pad_before = 1)]
184    pub line_count: u32,
185
186    /// For each segment in the collision, describe the "material". For example, a slippery stage
187    /// may have a `line_material` of `GroundCollAttr::GROUND_COLL_ATTR_ICE`. This has no relation
188    /// to graphical materials, only physics/sounds are affected by this.
189    #[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/// The flags specifying certain aspects of the collision behavior (whether the collision is
200/// rigged to an animated bone, whether the platform can be dropped through, etc.)
201#[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    /// Whether the collision is rigged to an animated bone
209    #[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    /// Whether characters can press down in order to drop through the collision
218    #[br(map = cbool)]
219    #[binwrite(map(c_bool))]
220    pub drop_through: bool,
221}
222
223/// Data describing grabbable ledges, internally called "cliffs", present in this collision.
224///
225/// This describes how far away the ledge can be grabbed from as well as what vertex/edge the
226/// cliff is a part of.
227#[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/// A type representing the properties of a given collision segment which affects the physics,
239/// gameplay effects and sounds used for the segment.
240///
241/// For example, a slippery stage may have a `line_material` of `GroundCollAttr::GROUND_COLL_ATTR_ICE`.
242#[derive(BinRead, Debug)]
243#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
244pub struct CollisionMaterial {
245    /// The type of a given ground collision (whether it is ice, wood, rock, wood, metal, etc)
246    #[br(pad_after = 4)]
247    pub line_material: GroundCollAttr,
248
249    /// The various properties of a given segment that affect gameplay in non-physics manner, such
250    /// as whether it is breakable or whether wall clings work on the given segment.
251    pub line_flags: LineFlags,
252}
253
254/// The type of a given ground collision (whether it is ice, wood, rock, wood, metal, etc)
255#[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/// A hurtbox present as a part of the level itself. An example being Luigi's Mansion's pillar
307/// hurtboxes that allow for parts of the stage to break.
308#[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    /// The generic object data (positioning, name, etc)
313    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/// Shape data that can be used for various forms of "where is a shape located on the stage"
323#[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    /// The generic object data (positioning, name, etc)
328    pub entry: LvdEntry,
329
330    #[br(pad_before = 1)]
331    pub unk1: u32,
332
333    pub shape: LvdShape,
334}
335
336/// Your guess is as good as mine. If you know what this is submit a PR
337#[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    /// The generic object data (positioning, name, etc)
342    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/// A location for a spawn or respawn point
357#[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    /// The generic object data (name, subname, etc)
362    pub entry: LvdEntry,
363
364    /// The position of the spawnpoint
365    #[br(pad_before = 1)]
366    pub pos: Vector2,
367}
368
369/// The bounds of a given rectangular area. Used for deathzones (blastzones) and camera pan
370/// boundaries.
371#[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    /// The generic object data (name, subname, etc)
376    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    /// The generic object data (name, subname, etc)
391    pub entry: LvdEntry,
392
393    /// ID of the Spawner if needed to be recognized uniquely by the level's specialized code
394    #[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    /// The various physical areas the item spawner takes up (multiple are allowed)
403    #[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/// The area of the stage where the Pokemon Trainer can run when attempting to follow the
416/// player-controlled pokemon characters. Alternative to [`PokemonTrainerPlatform`], which instead
417/// utilizes flying platforms due to not having a good location on the stage itself for them.
418#[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    /// The generic object data (name, subname, etc)
424    pub entry: LvdEntry,
425
426    /// The lower bounds of the pokemon trainer area (usually the left side)
427    #[br(pad_before = 1)]
428    pub boundary_min: Vector3,
429
430    /// The upper bounds of the pokemon trainer area (usually the right side)
431    #[br(pad_before = 1)]
432    pub boundary_max: Vector3,
433
434    #[br(temp, pad_before = 1)]
435    pub trainer_count: u32,
436
437    /// A list of where all the trainers start when the match begins
438    #[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    /// The name of the platform the pokemon trainer stands on. This is used to ensure the trainer
450    /// continues to stay on that platform even if it rudely attempts to leave them.
451    #[br(pad_before = 1, pad_size_to = 0x40, map = NullString::into_string)]
452    pub platform_name: String,
453
454    /// The subname of the platform the pokemon trainer stands on
455    #[br(pad_before = 1, pad_size_to = 0x40, map = NullString::into_string)]
456    pub sub_name: String,
457}
458
459/// The location on the stage where Pokemon Trainers can float on a cute little platform while
460/// watching their pokemon battle to the death. Alternative to [`PokemonTrainerRange`], which
461/// utilizes existing models to have the trainers run "on" the stage.
462#[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    /// The generic object data (name, subname, etc)
467    pub entry: LvdEntry,
468
469    /// The position of the platform
470    #[br(pad_before = 1)]
471    pub pos: Vector3,
472}
473
474/// A generic location in the level that can be accessed by the specialized level code. Also used
475/// for things like points on the stage that character mechanics interact with (usually final
476/// smashes).
477#[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    /// The generic object data (name, subname, etc)
482    pub entry: LvdEntry,
483
484    /// A unique ID for the point
485    #[br(pad_before = 1)]
486    pub id: u32,
487
488    /// The type of generic point this is
489    #[br(pad_before = 1)]
490    pub ty: u32,
491
492    /// The absolute position in the level
493    #[br(pad_after = 0x10)]
494    pub pos: Vector3,
495}
496
497/// A frequently-reused shape type for being able to describe the shape of various objects in LVD
498/// files.
499#[binread]
500#[derive(Debug)]
501#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
502pub enum LvdShape {
503    /// A volume-less point in space
504    #[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        /// The number of path points, should also be zero
513        #[br(temp, pad_before = 1)]
514        point_count: u32,
515    },
516
517    /// A 2d circle with no notion of the Z-axis
518    #[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        /// The number of path points, should also be zero
528        #[br(temp, pad_after = 1)]
529        point_count: u32,
530    },
531
532    /// A rectangle bound in space
533    #[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        /// The number of path points, should also be zero
544        #[br(temp, pad_before = 1)]
545        point_count: u32,
546    },
547
548    /// A set of points connected by segments describing a path
549    #[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    /// An unknown shape. If you know what this is, submit a PR
559    Invalid {
560        magic: u32,
561    },
562}
563
564/// A generic type for a section which isn't supported. Submit a PR to add support for any missing
565/// sections.
566#[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/// A generic list of items of the same type grouped into a section. Sections in LVD come in a
575/// specific order for which type is which.
576#[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/// A 2d point, size, or direction
603#[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/// A 3d point, size, or direction
617#[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    /// Read and parse an LVD from a file at a given path
641    ///
642    /// ```no_run
643    /// use lvd::LvdFile;
644    ///
645    /// let file = LvdFile::open("path/to/file.lvd").unwrap();
646    /// ```
647    pub fn open<P: AsRef<Path>>(path: P) -> BinResult<Self> {
648        // TODO: make this binrw::io::BufReader
649        let mut f = std::io::BufReader::new(std::fs::File::open(path.as_ref())?);
650
651        f.read_be()
652    }
653}
654
655/// Read and parse an LVD from a file at a given path
656///
657/// ```no_run
658/// let file = lvd::open("path/to/file.lvd").unwrap();
659/// ```
660pub fn open<P: AsRef<Path>>(path: P) -> BinResult<LvdFile> {
661    // TODO: make this binrw::io::BufReader
662    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        //dbg!(x);
677        //dbg!(&x.collisions.collisions[0].vertices.seperators);
678    }
679}