Krolyxon
Commit 90075f3

Branches

Refs →

budctlcratesbudctl-coresrcdevicesrealme_t310codec.rs

542 lines · 19.3 KB · Rust

1//! realme Buds T310 commands, tables and pure decoders. No I/O here, so every function can be
2//! tested with bytes from the spec. Each constant cites docs/protocol/realme_t310.md.
3
4use crate::error::{Error, Result};
5use crate::reader::Reader;
6use crate::status::{Battery, Charge, Gesture, NoiseControl, Switch};
7
8/// Spec: realme_t310.md#bitmap
9pub(super) const HANDSHAKE: u16 = 0x0100;
10/// Spec: realme_t310.md#identity
11pub(super) const PRODUCT_ID: u16 = 0x0103;
12/// Spec: realme_t310.md#firmware
13pub(super) const FIRMWARE: u16 = 0x0105;
14/// Spec: realme_t310.md#battery
15pub(super) const BATTERY: u16 = 0x0106;
16/// Spec: realme_t310.md#gestures
17pub(super) const GESTURES: u16 = 0x0108;
18/// Spec: realme_t310.md#anc-read
19pub(super) const NOISE_CONTROL: u16 = 0x010C;
20/// Spec: realme_t310.md#switches
21pub(super) const SWITCHES: u16 = 0x010D;
22/// Spec: realme_t310.md#eq-read
23pub(super) const EQ_PRESET: u16 = 0x010F;
24/// Spec: realme_t310.md#codec
25pub(super) const CODEC: u16 = 0x0114;
26/// Not sent: the T310 does not answer it (HW1). Kept for W6. Spec: realme_t310.md#bass-read
27#[allow(dead_code)]
28pub(super) const BASS_LEVEL: u16 = 0x0124;
29/// Spec: realme_t310.md#events-offered
30pub(super) const EVENTS_OFFERED: u16 = 0x0200;
31/// Spec: realme_t310.md#subscribe
32pub(super) const SUBSCRIBE: u16 = 0x0205;
33/// Spec: realme_t310.md#fit-test
34pub(super) const FIT_TEST: u16 = 0x0405;
35
36/// Spec: realme_t310.md#identity
37pub(super) const T310_PRODUCT_ID: u32 = 0x06_3C_12;
38
39/// Payload asking for the current noise-control mode. Spec: realme_t310.md#anc-read
40pub(super) const NOISE_MODE_QUERY: [u8; 2] = [0x01, 0x01];
41/// Payload asking for the hold's noise-control cycle (one setting for both buds).
42/// Spec: realme_t310.md#hold-cycle
43pub(super) const HOLD_CYCLE_QUERY: [u8; 2] = [0x02, 0x01];
44/// Count, then the T310's switch ids: `05 06 11 1A 1B 1D`. Spec: realme_t310.md#switches
45pub(super) const SWITCH_QUERY: [u8; 7] = [6, 0x05, 0x06, 0x11, 0x1A, 0x1B, 0x1D];
46
47/// Event codes to subscribe to; the optional ones only when their command is allowed.
48/// Spec: realme_t310.md#subscribe
49const EVENTS: [(u8, Option<u16>); 4] = [(0x01, None), (0x02, None), (0x03, None), (0x04, Some(FIT_TEST))];
50
51/// Spec: realme_t310.md#switches
52const SWITCH_NAMES: [(u8, &str, &str); 5] = [
53 (0x06, "game", "Game mode"),
54 (0x11, "dual", "Dual connection"),
55 (0x1A, "wind", "Wind-noise reduction"),
56 (0x1B, "spatial", "3D sound"),
57 (0x1D, "bass-boost", "Bass boost"),
58];
59
60/// Noise-control masks: mode and level. Spec: realme_t310.md#anc-read
61const NOISE_MODES: [(u32, &str, Option<&str>); 5] = [
62 (0x01, "off", None),
63 (0x02, "transparency", None),
64 (0x04, "anc", Some("light")),
65 (0x08, "anc", Some("deep")),
66 (0x10, "anc", Some("medium")),
67];
68
69/// Bits of the hold cycle. Spec: realme_t310.md#hold-cycle
70const CYCLE_MODES: [(u32, &str); 3] = [(0x01, "off"), (0x02, "transparency"), (0x08, "anc")];
71
72/// Built-in EQ presets by id. Spec: realme_t310.md#eq-read
73const EQ_PRESETS: [(u8, &str); 4] = [
74 (0x00, "pure-raw"),
75 (0x01, "deep-bass"),
76 (0x02, "pure-vocals"),
77 (0x03, "pure-bass"),
78];
79
80/// Spec: realme_t310.md#codec
81const CODECS: [(u8, &str); 8] = [
82 (0x01, "SBC"),
83 (0x02, "AAC"),
84 (0x03, "LDAC"),
85 (0x04, "aptX"),
86 (0x05, "aptX HD"),
87 (0x06, "aptX Adaptive"),
88 (0x07, "LHDC"),
89 (0x08, "LHDC V5"),
90];
91
92/// Gesture-table names. Spec: realme_t310.md#gestures
93const SIDES: [(u8, &str); 3] = [(0x01, "left"), (0x02, "right"), (0x04, "both")];
94/// Spec: realme_t310.md#gestures
95const MAIN_GROUP: u8 = 0x01;
96/// Spec: realme_t310.md#gestures
97const ACTS: [(u8, &str); 6] = [
98 (0x01, "single-tap"),
99 (0x02, "double-tap"),
100 (0x03, "triple-tap"),
101 (0x04, "hold"),
102 (0x05, "slide"),
103 (0x06, "long-hold"),
104];
105/// Spec: realme_t310.md#gestures
106const FUNCTIONS: [(u8, &str); 15] = [
107 (0x00, "none"),
108 (0x01, "play-pause"),
109 (0x03, "voice-assistant"),
110 (0x05, "previous"),
111 (0x06, "next"),
112 (0x07, "volume"),
113 (0x08, "noise-cycle"),
114 (0x09, "favourite-music"),
115 (0x0A, "switch-track"),
116 (0x0B, "volume-up"),
117 (0x0C, "volume-down"),
118 (0x0D, "switch-devices"),
119 (0x11, "game-mode"),
120 (0x1C, "decline-call"),
121 (0x1D, "answer-call"),
122];
123
124fn name_of<K: PartialEq + Copy>(table: &[(K, &'static str)], key: K) -> Option<&'static str> {
125 table.iter().find(|(k, _)| *k == key).map(|(_, name)| *name)
126}
127
128/// When a command may be sent. Spec: realme_t310.md#bitmap
129enum Gate {
130 Always,
131 Bit(u8),
132}
133
134/// The gate of every command budctl uses. An unlisted command is never sent.
135/// Spec: realme_t310.md#bitmap
136fn gate(cmd: u16) -> Option<Gate> {
137 Some(match cmd {
138 0x0100..=0x0104 | 0x0106 | 0x010B | 0x010D | 0x0200 => Gate::Always,
139 0x0105 => Gate::Bit(0),
140 0x0108 | 0x0401 => Gate::Bit(3),
141 0x0109 => Gate::Bit(4),
142 0x0400 => Gate::Bit(5),
143 0x0403 => Gate::Bit(7),
144 0x010C | 0x0404 => Gate::Bit(8),
145 0x0405 => Gate::Bit(9),
146 0x010F | 0x0406 => Gate::Bit(10),
147 0x0114 => Gate::Bit(17),
148 0x0205 => Gate::Bit(22),
149 0x0122 | 0x0418 => Gate::Bit(34),
150 0x0124 | 0x041B => Gate::Bit(38),
151 _ => return None,
152 })
153}
154
155/// The commands these buds accept, from the handshake reply. Spec: realme_t310.md#bitmap
156#[derive(Debug)]
157pub(super) struct Commands {
158 bitmap: Vec<u8>,
159}
160
161impl Commands {
162 pub(super) fn parse(reply: &[u8]) -> Result<Self> {
163 let mut r = ok_reply(reply, "the handshake")?;
164 Ok(Self {
165 bitmap: r.rest().to_vec(),
166 })
167 }
168
169 pub(super) fn allows(&self, cmd: u16) -> bool {
170 match gate(cmd) {
171 Some(Gate::Always) => true,
172 Some(Gate::Bit(n)) => self
173 .bitmap
174 .get(usize::from(n / 8))
175 .is_some_and(|byte| byte & (1 << (n % 8)) != 0),
176 None => false,
177 }
178 }
179}
180
181/// A reply whose status byte is `00`, positioned after it. Spec: realme_t310.md#seq
182fn ok_reply<'a>(reply: &'a [u8], what: &'static str) -> Result<Reader<'a>> {
183 let mut r = Reader::new(reply, what);
184 match r.u8()? {
185 0x00 => Ok(r),
186 status => Err(Error::Rejected {
187 what: what.to_owned(),
188 status,
189 }),
190 }
191}
192
193/// Spec: realme_t310.md#identity
194pub(super) fn decode_product_id(reply: &[u8]) -> Result<u32> {
195 let mut r = ok_reply(reply, "the product id query")?;
196 let [low, middle, high] = [r.u8()?, r.u8()?, r.u8()?];
197 Ok(u32::from_le_bytes([low, middle, high, 0]))
198}
199
200/// Spec: realme_t310.md#events-offered
201pub(super) fn decode_events_offered(reply: &[u8]) -> Result<Vec<u8>> {
202 let mut r = ok_reply(reply, "the event list")?;
203 let count = r.u8()?;
204 Ok(r.take(usize::from(count))?.to_vec())
205}
206
207/// The subscribe payload: count, then the wanted codes the buds offer.
208/// Spec: realme_t310.md#subscribe
209pub(super) fn subscribe_payload(offered: &[u8], commands: &Commands) -> Vec<u8> {
210 let codes: Vec<u8> = EVENTS
211 .iter()
212 .filter(|(code, needs)| offered.contains(code) && needs.is_none_or(|cmd| commands.allows(cmd)))
213 .map(|(code, _)| *code)
214 .collect();
215 let mut payload = vec![u8::try_from(codes.len()).unwrap_or_default()];
216 payload.extend(codes);
217 payload
218}
219
220/// Spec: realme_t310.md#battery
221pub(super) fn decode_battery(reply: &[u8]) -> Result<Battery> {
222 let mut r = ok_reply(reply, "the battery reply")?;
223 let mut battery = Battery::default();
224 for _ in 0..r.u8()? {
225 let part = r.u8()?;
226 let raw = r.u8()?;
227 let level = raw & 0x7F;
228 if level == 0 {
229 continue; // not known
230 }
231 let charge = Some(Charge {
232 level,
233 charging: raw & 0x80 != 0,
234 });
235 match part {
236 0x01 => battery.left = charge,
237 0x02 => battery.right = charge,
238 0x03 => battery.case = charge,
239 _ => {}
240 }
241 }
242 Ok(battery)
243}
244
245/// The versions of kind 2: plain numbers are joined with dots; full dotted versions (the T310's)
246/// are shown once each, joined with ` / `. The raw text when there are none.
247/// Spec: realme_t310.md#firmware
248pub(super) fn decode_firmware(reply: &[u8]) -> Result<String> {
249 let mut r = ok_reply(reply, "the firmware reply")?;
250 let _count = r.u8()?;
251 let text = String::from_utf8_lossy(r.rest()).into_owned();
252 let fields: Vec<&str> = text.split(',').map(str::trim).collect();
253 let mut versions: Vec<&str> = fields
254 .chunks(3)
255 .filter_map(|triple| match triple {
256 [_device, "2", version] => Some(*version),
257 _ => None,
258 })
259 .collect();
260 if versions.is_empty() {
261 return Ok(text);
262 }
263 if !versions.iter().any(|v| v.contains('.')) {
264 return Ok(versions.join("."));
265 }
266 let mut seen = Vec::new();
267 versions.retain(|v| {
268 let new = !seen.contains(v);
269 seen.push(*v);
270 new
271 });
272 Ok(versions.join(" / "))
273}
274
275/// Spec: realme_t310.md#codec
276pub(super) fn decode_codec(reply: &[u8]) -> Result<String> {
277 let codec = ok_reply(reply, "the codec reply")?.u8()?;
278 Ok(name_of(&CODECS, codec).map_or_else(|| format!("unknown (0x{codec:02X})"), str::to_owned))
279}
280
281/// Spec: realme_t310.md#anc-read
282pub(super) fn decode_noise_mode(reply: &[u8]) -> Result<NoiseControl> {
283 let mask = noise_mask(reply, NOISE_MODE_QUERY, "the noise-control reply")?;
284 Ok(match NOISE_MODES.iter().find(|(m, _, _)| *m == mask) {
285 Some((_, mode, level)) => NoiseControl {
286 mode: (*mode).to_owned(),
287 level: level.map(str::to_owned),
288 },
289 None => NoiseControl {
290 mode: format!("unknown (0x{mask:02X})"),
291 level: None,
292 },
293 })
294}
295
296/// Spec: realme_t310.md#hold-cycle
297pub(super) fn decode_hold_cycle(reply: &[u8]) -> Result<Vec<String>> {
298 let mask = noise_mask(reply, HOLD_CYCLE_QUERY, "the hold-cycle reply")?;
299 let mut modes: Vec<String> = CYCLE_MODES
300 .iter()
301 .filter(|(bit, _)| mask & bit != 0)
302 .map(|(_, name)| (*name).to_owned())
303 .collect();
304 let known = CYCLE_MODES.iter().fold(0, |all, (bit, _)| all | bit);
305 if mask & !known != 0 {
306 modes.push(format!("unknown (0x{:02X})", mask & !known));
307 }
308 Ok(modes)
309}
310
311/// `00 <echo of the query> <mask, 1-4 bytes LE>`. Spec: realme_t310.md#anc-read
312fn noise_mask(reply: &[u8], query: [u8; 2], what: &'static str) -> Result<u32> {
313 let mut r = ok_reply(reply, what)?;
314 if [r.u8()?, r.u8()?] != query {
315 return Err(Error::Protocol(format!("{what} answers a different question")));
316 }
317 let bytes = r.rest();
318 if bytes.is_empty() || bytes.len() > 4 {
319 return Err(Error::Protocol(format!("{what} has a {} byte value", bytes.len())));
320 }
321 Ok(bytes.iter().rev().fold(0, |mask, &b| (mask << 8) | u32::from(b)))
322}
323
324/// Spec: realme_t310.md#switches
325pub(super) fn decode_switches(reply: &[u8]) -> Result<Vec<Switch>> {
326 let mut r = ok_reply(reply, "the switch reply")?;
327 let mut switches = Vec::new();
328 for _ in 0..r.u8()? {
329 let id = r.u8()?;
330 let value = r.u8()?;
331 if let Some((_, key, label)) = SWITCH_NAMES.iter().find(|(known, _, _)| *known == id) {
332 switches.push(Switch {
333 key,
334 label,
335 on: value == 0x01,
336 });
337 }
338 }
339 Ok(switches)
340}
341
342/// Spec: realme_t310.md#eq-read
343pub(super) fn decode_eq(reply: &[u8]) -> Result<String> {
344 let id = ok_reply(reply, "the EQ reply")?.u8()?;
345 Ok(name_of(&EQ_PRESETS, id).map_or_else(|| format!("custom-{id}"), str::to_owned))
346}
347
348/// `00 <min> <max> <level>`, all signed. Not used until W6. Spec: realme_t310.md#bass-read
349#[allow(dead_code)]
350pub(super) fn decode_bass_level(reply: &[u8]) -> Result<i8> {
351 let mut r = ok_reply(reply, "the bass-level reply")?;
352 let (min, max, level) = (r.i8()?, r.i8()?, r.i8()?);
353 if !(min..=max).contains(&level) {
354 return Err(Error::Protocol(format!("bass level {level} outside {min}..={max}")));
355 }
356 Ok(level)
357}
358
359/// Spec: realme_t310.md#gestures
360pub(super) fn decode_gestures(reply: &[u8]) -> Result<Vec<Gesture>> {
361 let mut r = ok_reply(reply, "the gesture table")?;
362 let mut gestures = Vec::new();
363 for _ in 0..r.u8()? {
364 let [side, group, act, function] = [r.u8()?, r.u8()?, r.u8()?, r.u8()?];
365 gestures.push(Gesture {
366 side: name_of(&SIDES, side).map_or_else(|| format!("device-0x{side:02X}"), str::to_owned),
367 gesture: name_of(&ACTS, act).map_or_else(|| format!("act-0x{act:02X}"), str::to_owned),
368 action: name_of(&FUNCTIONS, function).map_or_else(|| format!("fn-0x{function:02X}"), str::to_owned),
369 group: (group != MAIN_GROUP).then_some(group),
370 });
371 }
372 Ok(gestures)
373}
374
375/// The handshake reply of the owner's T310 (HW1). Spec: realme_t310.md#bitmap-t310
376#[cfg(test)]
377pub(super) const T310_BITMAP_REPLY: &str = "00 BF 1F 5A 20 45 04 40 00";
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382 use crate::transport::mock::hex;
383
384 #[test]
385 fn t310_bitmap_allows_its_commands_and_nothing_else() {
386 let commands = Commands::parse(&hex(T310_BITMAP_REPLY)).unwrap();
387 // Every gated command in the T310's logged command set (realme_t310.md#bitmap-t310).
388 for cmd in [
389 0x0100, 0x0103, 0x0105, 0x0106, 0x0108, 0x0109, 0x010C, 0x010D, 0x010F, 0x0114, 0x0122, 0x0124, 0x0205,
390 0x0400, 0x0401, 0x0403, 0x0404, 0x0405, 0x0406, 0x0418, 0x041B, 0x0200,
391 ] {
392 assert!(commands.allows(cmd), "0x{cmd:04X} should be allowed");
393 }
394 // Commands the T310 lacks, and commands budctl has no gate for.
395 for cmd in [0x012F, 0x041E, 0x0413, 0x0130, 0x0427, 0x0423, 0x012A, 0x0422, 0x9999] {
396 assert!(!commands.allows(cmd), "0x{cmd:04X} should be refused");
397 }
398 }
399
400 #[test]
401 fn a_short_bitmap_allows_only_the_always_commands() {
402 let commands = Commands::parse(&hex("00")).unwrap();
403 assert!(commands.allows(BATTERY) && commands.allows(SWITCHES) && commands.allows(HANDSHAKE));
404 assert!(!commands.allows(FIRMWARE) && !commands.allows(NOISE_CONTROL));
405 assert!(matches!(
406 Commands::parse(&hex("01 FF")),
407 Err(Error::Rejected { status: 1, .. })
408 ));
409 }
410
411 #[test]
412 fn product_id_is_three_bytes_little_endian() {
413 assert_eq!(decode_product_id(&hex("00 12 3C 06")).unwrap(), T310_PRODUCT_ID);
414 assert_eq!(decode_product_id(&hex("00 14 54 06")).unwrap(), 0x06_54_14);
415 assert!(decode_product_id(&hex("00 12 3C")).is_err());
416 }
417
418 #[test]
419 fn subscribes_to_offered_events_only() {
420 let commands = Commands::parse(&hex(T310_BITMAP_REPLY)).unwrap();
421 let buds4_offer = hex("00 09 01 02 03 04 08 0B F1 F2 F3");
422 let offered = decode_events_offered(&buds4_offer).unwrap();
423 assert_eq!(subscribe_payload(&offered, &commands), hex("04 01 02 03 04"));
424 assert_eq!(subscribe_payload(&[0x02, 0x01], &commands), hex("02 01 02"));
425 let no_fit_test = Commands::parse(&hex("00 BD 0D")).unwrap();
426 assert_eq!(subscribe_payload(&offered, &no_fit_test), hex("03 01 02 03"));
427 }
428
429 #[test]
430 fn battery_levels_charging_and_unknown_parts() {
431 let battery = decode_battery(&hex("00 03 01 64 02 E4 03 00")).unwrap();
432 assert_eq!(
433 battery.left,
434 Some(Charge {
435 level: 100,
436 charging: false
437 })
438 );
439 assert_eq!(
440 battery.right,
441 Some(Charge {
442 level: 100,
443 charging: true
444 })
445 );
446 assert_eq!(battery.case, None);
447 assert_eq!(decode_battery(&hex("00 00")).unwrap(), Battery::default());
448 assert!(decode_battery(&hex("00 03 01 64")).is_err());
449 }
450
451 #[test]
452 fn firmware_shows_kind_two_versions() {
453 let mut reply = hex("00 04");
454 reply.extend(b"1,2,138,2,2,138,3,1,01,3,2,105");
455 assert_eq!(decode_firmware(&reply).unwrap(), "138.138.105");
456 // The owner's T310 (HW1): both buds report the same full version.
457 let mut t310 = hex("00 02");
458 t310.extend(b"1,2,1.1.0.43,2,2,1.1.0.43");
459 assert_eq!(decode_firmware(&t310).unwrap(), "1.1.0.43");
460 let mut differ = hex("00 02");
461 differ.extend(b"1,2,1.1.0.43,2,2,1.1.0.44");
462 assert_eq!(decode_firmware(&differ).unwrap(), "1.1.0.43 / 1.1.0.44");
463 let mut odd = hex("00 01");
464 odd.extend(b"V1.0.2");
465 assert_eq!(decode_firmware(&odd).unwrap(), "V1.0.2");
466 }
467
468 #[test]
469 fn noise_control_modes() {
470 let mode = |reply: &str| decode_noise_mode(&hex(reply)).unwrap();
471 assert_eq!(mode("00 01 01 01").mode, "off");
472 assert_eq!(mode("00 01 01 02 00").mode, "transparency");
473 let deep = mode("00 01 01 08");
474 assert_eq!((deep.mode.as_str(), deep.level.as_deref()), ("anc", Some("deep")));
475 assert_eq!(mode("00 01 01 10").level.as_deref(), Some("medium"));
476 assert_eq!(mode("00 01 01 00 08").mode, "unknown (0x800)");
477 assert!(decode_noise_mode(&hex("00 02 01 08")).is_err());
478 assert!(decode_noise_mode(&hex("00 01 01")).is_err());
479 assert!(matches!(
480 decode_noise_mode(&hex("0E 01 01 08")),
481 Err(Error::Rejected { status: 0x0E, .. })
482 ));
483 }
484
485 #[test]
486 fn hold_cycle_lists_modes() {
487 assert_eq!(decode_hold_cycle(&hex("00 02 01 0A")).unwrap(), ["transparency", "anc"]);
488 assert_eq!(
489 decode_hold_cycle(&hex("00 02 01 0B 00")).unwrap(),
490 ["off", "transparency", "anc"]
491 );
492 assert_eq!(decode_hold_cycle(&hex("00 02 01 14")).unwrap(), ["unknown (0x14)"]);
493 }
494
495 #[test]
496 fn switches_from_report_22_values() {
497 let switches = decode_switches(&hex("00 06 05 00 06 00 11 01 1A 00 1B 01 1D 01")).unwrap();
498 let on: Vec<(&str, bool)> = switches.iter().map(|s| (s.key, s.on)).collect();
499 assert_eq!(
500 on,
501 [
502 ("game", false),
503 ("dual", true),
504 ("wind", false),
505 ("spatial", true),
506 ("bass-boost", true)
507 ]
508 );
509 assert!(decode_switches(&hex("00 02 06 01")).is_err());
510 }
511
512 #[test]
513 fn eq_codec_and_bass_level() {
514 assert_eq!(decode_eq(&hex("00 00")).unwrap(), "pure-raw");
515 assert_eq!(decode_eq(&hex("00 03")).unwrap(), "pure-bass");
516 assert_eq!(decode_eq(&hex("00 05")).unwrap(), "custom-5");
517 assert_eq!(decode_codec(&hex("00 02")).unwrap(), "AAC");
518 assert_eq!(decode_codec(&hex("00 09")).unwrap(), "unknown (0x09)");
519 assert_eq!(decode_bass_level(&hex("00 FB 05 FE")).unwrap(), -2);
520 assert!(decode_bass_level(&hex("00 FB 05 07")).is_err());
521 }
522
523 #[test]
524 fn gesture_table_names_known_values() {
525 let gestures = decode_gestures(&hex("00 03 01 01 02 01 02 01 04 08 04 06 02 1D")).unwrap();
526 let rows: Vec<(&str, &str, &str, Option<u8>)> = gestures
527 .iter()
528 .map(|g| (g.side.as_str(), g.gesture.as_str(), g.action.as_str(), g.group))
529 .collect();
530 assert_eq!(
531 rows,
532 [
533 ("left", "double-tap", "play-pause", None),
534 ("right", "hold", "noise-cycle", None),
535 ("both", "double-tap", "answer-call", Some(6)),
536 ]
537 );
538 let odd = decode_gestures(&hex("00 01 09 01 07 42")).unwrap();
539 assert_eq!(odd.first().map(|g| g.action.as_str()), Some("fn-0x42"));
540 assert!(decode_gestures(&hex("00 02 01 01 02 01")).is_err());
541 }
542}