Skip to main content

ygopro_data/
utils.rs

1/// UTF-16 string helpers.
2pub mod string {
3    #![allow(dead_code)]
4
5    use std::ops::Deref;
6    use std::sync::OnceLock;
7
8    use binrw::BinRead;
9    use binrw::BinWrite;
10    use binrw::binrw;
11    use binrw::helpers::until_eof;
12
13    /// transform \[u16\] to string. \
14    /// return [`None`] if it's illegal.
15    pub fn cast_to_string(array: &[u16]) -> Option<String> {
16        let mut str = array;
17        if let Some(index) = array.iter().position(|&i| i == 0) {
18            str = &str[0..index as usize];
19        }
20        else { return None }
21        let body = unsafe { std::slice::from_raw_parts(str.as_ptr() as *const u8, str.len() * 2) };
22        let (cow, _, had_errors) = encoding_rs::UTF_16LE.decode(&body);
23        if had_errors { None }
24        else { Some(cow.to_string()) }
25    }
26
27    /// Transform string to \[u16\] without length limit but a \0 in the end.
28    pub fn cast_to_c_array(message: &str) -> Vec<u16> {
29        let mut vector: Vec<u16> = message.encode_utf16().collect();
30        vector.push(0);
31        vector
32    }
33
34    /// Transform string to \[u16\] with a fixed size. \
35    /// Differennt from ygopro, it will keeps 0 for residual part.
36    pub fn cast_to_fix_length_array<const N: usize>(message: &str) -> [u16; N] {
37        let mut data = [0u16; N];
38        for (index, chr) in message.encode_utf16().enumerate() {
39            data[index] = chr;
40        }
41        data
42    }
43
44    /// A fixed-length UTF-16 string, stored as a `u16` array with a lazily cached `String`.
45    #[derive(Clone, BinRead, BinWrite)]
46    pub struct FixedLengthString<const L: usize> {
47        data: [u16; L],
48        #[brw(ignore)]
49        str: OnceLock<String>
50    }
51
52    impl<const L: usize> std::fmt::Display for FixedLengthString<L> {
53        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54            write!(f, "{}", &cast_to_string(&self.data).unwrap_or("[ERROR]".to_string()))
55        }
56    }
57
58    impl<const L: usize> PartialEq for FixedLengthString<L> {
59        fn eq(&self, other: &Self) -> bool {
60            &**self == &**other
61        }
62    }
63
64    impl <const L: usize> std::fmt::Debug for FixedLengthString<L> {
65        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66            write!(f, "FixedLengthString[{:}] ", L)?;
67            write!(f, "\"{}\"", &cast_to_string(&self.data).unwrap_or("[ERROR]".to_string()))   
68        }
69    }
70
71    impl<const L: usize> FixedLengthString<L> {
72        /// Allocate an empty string with all-zero `u16` array.
73        pub fn allocate() -> Self {
74            Self {
75                data: [0u16; L],
76                str: OnceLock::new(),
77            }
78        }
79
80        /// Check whether the string is empty (all entries are zero).
81        pub fn is_empty(&self) -> bool {
82            self.data.iter().all(|&x| x == 0)
83        }
84
85        /// Create a string from a `String`, filling the fixed-length array.
86        pub fn new(str: String) -> Self {
87            let this = Self {
88                data: cast_to_fix_length_array(&str),
89                str: OnceLock::new()
90            };
91            this.str.set(str).ok();
92            this
93        }
94
95        /// Parse the `u16` array into the cached `String` if not yet done.
96        pub fn resolve_data(&mut self) {
97            if self.str.get() == None {
98                if let Some(str) = cast_to_string(&self.data) {
99                    self.str.set(str).ok();
100                }
101            }
102        }
103
104        /// Write the cached `String` back into the `u16` array if present.
105        pub fn resolve_str(&mut self) {
106            if let Some(str) = self.str.get() {
107                self.data = cast_to_fix_length_array(str);
108            }
109        }        
110    }
111
112    impl<const L: usize> Deref for FixedLengthString<L> {
113        type Target = str;
114
115        fn deref(&self) -> &Self::Target {
116            self.str.get_or_init(|| cast_to_string(&self.data).unwrap_or_default()).as_str()
117        }
118    }
119
120    impl<const L: usize> From<String> for FixedLengthString<L> {
121        fn from(value: String) -> Self {
122            FixedLengthString::new(value)
123        }
124    }
125
126    impl<'s, const L: usize> From<&'s str> for FixedLengthString<L> {
127        fn from(value: &'s str) -> Self {
128            FixedLengthString::new(value.to_string())
129        }
130    }
131
132    /// A UTF-16 string with no length limit, terminated by a `0`, with a lazily cached `String`.
133    #[binrw]
134    #[derive(Clone)]
135    pub struct U16String {
136        #[br(parse_with=until_eof)]
137        data: Vec<u16>,
138        #[brw(ignore)]
139        str: OnceLock<String>,
140    }
141
142    impl std::fmt::Debug for U16String {
143        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144            write!(f, "U16String[{:}] ", self.data.len())?;
145            write!(f, "\"{:}\"", &cast_to_string(&self.data).unwrap_or("[ERROR]".to_string()))
146        }
147    }
148    
149    impl std::fmt::Display for U16String {
150        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151            write!(f, "U16String[{:}] ", self.data.len())?;
152            write!(f, "\"{:}\"", &cast_to_string(&self.data).unwrap_or("[ERROR]".to_string()))
153        }
154    }
155
156    impl U16String {
157        /// Create a string from a `String`, appending a `0` terminator.
158        pub fn new(str: String) -> Self {
159            let this = Self {
160                data: cast_to_c_array(&str),
161                str: OnceLock::new()
162            };
163            this.str.set(str).ok();
164            this
165        }
166
167        /// Parse the `u16` vector into the cached `String` if not yet done.
168        pub fn resolve_data(&self) {
169            if self.str.get() == None {
170                if let Some(str) = cast_to_string(&self.data) {
171                    self.str.set(str).ok();
172                }
173            }
174        }
175
176        /// Write the cached `String` back into the `u16` vector if present.
177        pub fn resolve_str(&mut self) {
178            if let Some(str) = self.str.get() {
179                self.data = cast_to_c_array(str);
180            }
181        }        
182    }
183
184    impl Deref for U16String {
185        type Target = str;
186
187        fn deref(&self) -> &Self::Target {
188            self.str.get_or_init(|| cast_to_string(&self.data).unwrap_or_default()).as_str()
189        }
190    }
191
192    impl From<String> for U16String {
193        fn from(value: String) -> Self {
194            U16String::new(value)
195        }
196    }
197
198    impl<'s> From<&'s str> for U16String {
199        fn from(value: &'s str) -> Self {
200            U16String::new(value.to_string())
201        }
202    }
203
204    impl<'s> From<&'s [u16]> for U16String {
205        fn from(value: &'s [u16]) -> Self {
206            U16String {
207                data: value.to_vec(),
208                str: OnceLock::new()
209            }
210        }
211    }
212}
213
214/// Lazy-deserialized messages.
215pub mod complex {
216    use std::io::Cursor;
217    use std::io::Write;
218    use std::ops::Deref;
219    use std::sync::OnceLock;
220
221    use binrw::BinRead;
222    use binrw::BinWrite;
223    use bytes::Bytes;
224
225    use crate::message::PureMessage;
226
227    /// A lazy-deserialized message.
228    ///
229    /// It holds the raw wire bytes (`data`) and only parses them into a `Message` on
230    /// first access, caching the result in a [`OnceLock`]. When written out, [`BinWrite`]
231    /// emits the original raw bytes and never re-serializes.
232    ///
233    /// # Why it is fast
234    ///
235    /// - **No parse cost unless needed.** A message that is only forwarded or logged is
236    ///   never deserialized, so it skips the full decode cost entirely.
237    /// - **No re-serialize cost.** Writing emits the stored raw bytes, avoiding a re-encode
238    ///   of the already-parsed message.
239    /// - **Cheap clone.** Cloning copies only the reference-counted [`Bytes`] and resets the
240    ///   cache, so it does not deep-copy the message. [`super_clone`](Self::super_clone)
241    ///   is used when the cached message must also be cloned.
242    #[derive(Debug)]
243    pub struct Complex<Message> {
244        /// The raw wire bytes of the message.
245        pub data: Bytes,
246        /// The lazily-parsed message cache.
247        pub message: OnceLock<Message>,
248    }
249
250    impl<Message> Complex<Message> {
251        /// Create a `Complex` from raw wire bytes, without parsing.
252        pub fn new(data: Bytes) -> Self {
253            Self {
254                data,
255                message: OnceLock::new(),
256            }
257        }
258
259        /// Clone the `Complex`, including the cached message if present.
260        pub fn super_clone(&self) -> Self where Message: Clone {
261            Self {
262                data: self.data.clone(),
263                message: self.message.clone()
264            }
265        }
266
267        /// Get a reference to the raw wire bytes.
268        pub fn bytes(&self) -> &Bytes {
269            &self.data
270        }
271
272    }
273
274    impl<Message> Clone for Complex<Message> {
275        fn clone(&self) -> Self {
276            Self {
277                data: self.data.clone(),
278                message: OnceLock::new(),
279            }
280        }
281    }
282
283    impl<Message: BinWrite> Complex<Message> where Message: BinWrite,for<'a> <Message as BinWrite>::Args<'a>: Default {
284        /// Create a `Complex` from a parsed message, serializing it to raw bytes.
285        pub fn from_message(message: Message) -> Self {
286            let mut cursor = Cursor::new(Vec::new());
287            message.write_le(&mut cursor).expect("failed to serialize Complex message");
288            Self {
289                data: Bytes::from(cursor.into_inner()),
290                message: OnceLock::from(message),
291            }
292        }
293    }
294
295    impl<Message: BinRead> Complex<Message> where for<'a> <Message as BinRead>::Args<'a>: Default {
296        /// Parse the message from the raw bytes on first access, caching it.
297        pub fn try_get(&self) -> Result<&Message, binrw::Error> {
298            if let Some(message) = self.message.get() {
299                return Ok(message);
300            }
301            let message = Message::read_le(&mut Cursor::new(&self.data))?;
302            Ok(self.message.get_or_init(|| message))
303        }
304
305        /// Consume the `Complex`, returning the cached message if it was parsed.
306        pub fn into_inner(self) -> Option<Message> {
307            self.try_get().ok();
308            self.message.into_inner()
309        }
310    }
311
312    impl<Message: BinRead> Deref for Complex<Message> where
313        for<'a> <Message as BinRead>::Args<'a>: Default,
314    {
315        type Target = Message;
316
317        fn deref(&self) -> &Self::Target {
318            self.try_get().expect("failed to deserialize Complex message")
319        }
320    }
321    impl<Message: BinWrite> BinWrite for Complex<Message> {
322        type Args<'a> = <Message as BinWrite>::Args<'a>;
323
324        fn write_options<W: Write>(&self, writer: &mut W, _endian: binrw::Endian, _args: Self::Args<'_>) -> binrw::BinResult<()> {
325            writer.write_all(&self.data).map_err(binrw::Error::from)
326        }
327    }
328
329    impl<Message: PureMessage> From<Message> for Complex<Message>
330    where
331        Message: BinRead + BinWrite,
332        for<'a> <Message as BinRead>::Args<'a>: Default,
333        for<'a> <Message as BinWrite>::Args<'a>: Default,
334    {
335        fn from(message: Message) -> Self {
336            Self::from_message(message)
337        }
338    }
339
340    impl<Message: BinRead> From<Bytes> for Complex<Message> where for<'a> <Message as BinRead>::Args<'a>: Default {
341        fn from(bytes: Bytes) -> Self {
342            Self::new(bytes)
343        }
344    }
345
346    impl<Message> From<&Complex<Message>> for crate::message::client_to_server::MessageType {
347        fn from(value: &Complex<Message>) -> Self {
348            Self::from(value.data[0])
349        }
350    }
351    impl<Message> From<&Complex<Message>> for crate::message::server_to_client::MessageType {
352        fn from(value: &Complex<Message>) -> Self {
353            Self::from(value.data[0])
354        }
355    }
356    impl<Message> From<&Complex<Message>> for crate::message::game_message::MessageType {
357        fn from(value: &Complex<Message>) -> Self {
358            Self::from(value.data[0])
359        }
360    }
361}