Skip to main content

rakata_formats/lyt/
mod.rs

1//! LYT ASCII reader and writer.
2//!
3//! LYT (layout) resources describe room placement and optional track/obstacle
4//! props for a module.
5//!
6//! ## Shape of the file
7//!
8//! Four count-led sections between `beginlayout` and `donelayout`: rooms,
9//! tracks, obstacles and doorhooks. Each declares its entry count and the
10//! entries follow as whitespace-separated fields. Doorhooks are the odd one,
11//! carrying a quaternion as well as a position.
12//!
13//! This parser reads more loosely than the engine. The engine skips only the
14//! preamble before `beginlayout`, while this ignores any line it does not
15//! recognise as a known count section, wherever it sits. The tolerance runs
16//! one way. Output is canonical.
17//!
18//! Text is decoded and encoded as Windows-1252. The grammar and the per-section
19//! field lists are in `docs/src/formats/text/lyt.md`, with the engine's own
20//! parse sequence and its ordering requirement.
21
22mod reader;
23mod writer;
24
25pub use reader::{read_lyt, read_lyt_from_bytes};
26pub use writer::{write_lyt, write_lyt_to_vec};
27
28use thiserror::Error;
29
30use rakata_core::{DecodeTextError, EncodeTextError};
31
32use crate::binary::{DecodeBinary, EncodeBinary};
33
34/// In-memory LYT layout.
35#[derive(Debug, Clone, PartialEq, Default)]
36pub struct Lyt {
37    /// Room entries.
38    pub rooms: Vec<LytRoom>,
39    /// Track entries.
40    pub tracks: Vec<LytTrack>,
41    /// Obstacle entries.
42    pub obstacles: Vec<LytObstacle>,
43    /// Door-hook entries.
44    pub doorhooks: Vec<LytDoorHook>,
45}
46
47impl Lyt {
48    /// Creates an empty layout.
49    pub fn new() -> Self {
50        Self::default()
51    }
52}
53
54impl DecodeBinary for Lyt {
55    type Error = LytError;
56
57    fn decode_binary(bytes: &[u8]) -> Result<Self, Self::Error> {
58        read_lyt_from_bytes(bytes)
59    }
60}
61
62impl EncodeBinary for Lyt {
63    type Error = LytError;
64
65    fn encode_binary(&self) -> Result<Vec<u8>, Self::Error> {
66        write_lyt_to_vec(self)
67    }
68}
69
70/// One room entry in a LYT layout.
71#[derive(Debug, Clone, PartialEq)]
72pub struct LytRoom {
73    /// Room model name.
74    pub model: String,
75    /// Room world position.
76    pub position: Vec3,
77}
78
79/// One track entry in a LYT layout.
80#[derive(Debug, Clone, PartialEq)]
81pub struct LytTrack {
82    /// Track model name.
83    pub model: String,
84    /// Track world position.
85    pub position: Vec3,
86}
87
88/// One obstacle entry in a LYT layout.
89#[derive(Debug, Clone, PartialEq)]
90pub struct LytObstacle {
91    /// Obstacle model name.
92    pub model: String,
93    /// Obstacle world position.
94    pub position: Vec3,
95}
96
97/// One door-hook entry in a LYT layout.
98#[derive(Debug, Clone, PartialEq)]
99pub struct LytDoorHook {
100    /// Room name associated with this hook.
101    pub room: String,
102    /// Door identifier within the room.
103    pub door: String,
104    /// Door world position.
105    pub position: Vec3,
106    /// Door orientation quaternion.
107    pub orientation: Quaternion,
108}
109
110/// Three-dimensional vector value used by LYT entries.
111#[derive(Debug, Clone, Copy, PartialEq)]
112pub struct Vec3 {
113    /// X component.
114    pub x: f32,
115    /// Y component.
116    pub y: f32,
117    /// Z component.
118    pub z: f32,
119}
120
121impl Vec3 {
122    /// Creates a new 3D vector.
123    pub const fn new(x: f32, y: f32, z: f32) -> Self {
124        Self { x, y, z }
125    }
126}
127
128/// Quaternion value used by LYT door-hook orientation.
129#[derive(Debug, Clone, Copy, PartialEq)]
130pub struct Quaternion {
131    /// X component.
132    pub x: f32,
133    /// Y component.
134    pub y: f32,
135    /// Z component.
136    pub z: f32,
137    /// W component.
138    pub w: f32,
139}
140
141impl Quaternion {
142    /// Creates a new quaternion.
143    pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
144        Self { x, y, z, w }
145    }
146}
147
148/// Errors produced while parsing or serializing LYT ASCII data.
149#[derive(Debug, Error)]
150pub enum LytError {
151    /// I/O read/write failure.
152    #[error(transparent)]
153    Io(#[from] std::io::Error),
154    /// Text content is malformed.
155    #[error("invalid LYT data: {0}")]
156    InvalidData(String),
157    /// Text cannot be represented in Windows-1252 output.
158    #[error("LYT text encoding failed for {context}: {source}")]
159    TextEncoding {
160        /// Value context.
161        context: String,
162        /// Encoding error details.
163        #[source]
164        source: EncodeTextError,
165    },
166    /// Input bytes could not be decoded losslessly as Windows-1252.
167    #[error("LYT text decoding failed for {context}: {source}")]
168    TextDecoding {
169        /// Value context.
170        context: String,
171        /// Decoding error details.
172        #[source]
173        source: DecodeTextError,
174    },
175    /// A name token contains disallowed whitespace.
176    #[error("invalid LYT {field} token `{value}` (whitespace is not allowed)")]
177    InvalidName {
178        /// Field context.
179        field: &'static str,
180        /// Invalid name value.
181        value: String,
182    },
183}