Skip to main content

steamkit/base/generated/
steam_language_internal.rs

1//! Wire serialisation for the non-protobuf Steam message and header structures.
2//!
3//! Every field is written and read little endian, one at a time, so the byte
4//! layout is exact. The protobuf header type (`CMsgProtoBufHeader`) itself
5//! comes from `steamkit_protos`.
6//!
7//! # Where the structures live
8//!
9//! The plain structures from the Valve definition files are generated into
10//! [`crate::base::generated::steam_language::internal`]. That module says
11//! *what* a message contains; this one says *how* it is laid out on the wire.
12//! The generated types are re-exported here too.
13//!
14//! The two protobuf headers are the exception. The generated structures only
15//! keep the raw header bytes, so [`MsgHdrProtoBuf`] and [`MsgGCHdrProtoBuf`]
16//! are defined below with a typed `CMsgProtoBufHeader` instead.
17//!
18//! # SteamID fields
19//!
20//! SteamID fields are stored as the raw `u64`. Use `SteamID::from_id(value)`
21//! to get a [`SteamID`](crate::types::steam_id::SteamID).
22
23use prost::Message as ProstMessage;
24
25use crate::base::generated::steam_language::internal as generated;
26use crate::base::generated::steam_language::{
27    EMsg, EAppUsageEvent, EChatAction, EChatActionResult, EChatEntryType, EChatInfoType,
28    EChatPermission, EChatRoomEnterResponse, EChatRoomType, EClanRank, EClanRelationship,
29    EDenyReason, EIntroducerRouting, EResult, EServerType, EUdpPacketType, EUniverse,
30};
31use crate::base::msg_base::Reader;
32use crate::error::SteamError;
33use steamkit_protos::gc::CMsgProtoBufHeader as GcCMsgProtoBufHeader;
34use steamkit_protos::steam::CMsgProtoBufHeader;
35
36pub use crate::base::generated::steam_language::internal::{
37    Accept, ChallengeData, ConnectData, Datagram, Disconnect, ExtendedClientMsgHdr,
38    MsgChannelEncryptRequest, MsgChannelEncryptResponse, MsgChannelEncryptResult,
39    MsgClientAppUsageEvent, MsgClientChatAction, MsgClientChatActionResult, MsgClientChatEnter,
40    MsgClientChatMemberInfo, MsgClientChatMsg, MsgClientChatRoomInfo, MsgClientCreateChat,
41    MsgClientCreateChatResponse, MsgClientGenericResponse, MsgClientGetFriendsWhoPlayGame,
42    MsgClientGetFriendsWhoPlayGameResponse, MsgClientGetLegacyGameKey,
43    MsgClientGetLegacyGameKeyResponse, MsgClientJoinChat, MsgClientJustStrings,
44    MsgClientLogOnResponse,
45    MsgClientLoggedOff, MsgClientLogon, MsgClientMarketingMessageUpdate2,
46    MsgClientOGSBeginSession, MsgClientOGSBeginSessionResponse, MsgClientOGSEndSession,
47    MsgClientOGSEndSessionResponse, MsgClientOGSWriteRow, MsgClientP2PIntroducerMessage,
48    MsgClientServerUnavailable, MsgClientSetIgnoreFriend, MsgClientSetIgnoreFriendResponse,
49    MsgClientUpdateGuestPassesList, MsgClientVACBanStatus, MsgGCHdr, MsgGSDeny, MsgGSApprove,
50    MsgGSGetPlayStatsResponse, MsgGSGetReputationResponse, MsgGSGetUserGroupStatus,
51    MsgGSGetUserGroupStatusResponse, MsgGSKick, MsgGSPerformHardwareSurvey, MsgHdr, UdpHeader,
52};
53
54/// The write half of a serialisable wire type.
55pub trait SteamSerializable: Send + Sync {
56    /// Appends this value's wire representation to `out`.
57    fn write(&self, out: &mut Vec<u8>);
58}
59
60/// The read half of a serialisable wire type.
61pub trait SteamSerializableDeserialize: SteamSerializable + Default + Sized {
62    /// Reads a value of this type from `r`.
63    fn read(r: &mut Reader<'_>) -> Result<Self, SteamError>;
64}
65
66/// A type that can be both written and read.
67pub trait ISteamSerializable: SteamSerializableDeserialize {}
68
69impl<T> ISteamSerializable for T where T: SteamSerializableDeserialize {}
70
71/// A message header.
72pub trait ISteamSerializableHeader: ISteamSerializable {
73    /// Sets the message type carried by this header.
74    fn set_emsg(&mut self, msg: EMsg);
75}
76
77/// A message body with a fixed message type.
78pub trait ISteamSerializableMessage: ISteamSerializable {
79    /// The network message type of this body.
80    fn get_emsg(&self) -> EMsg;
81}
82
83/// A game coordinator message header.
84pub trait IGCSerializableHeader: ISteamSerializable {
85    /// Sets the game coordinator message type carried by this header.
86    fn set_gc_emsg(&mut self, msg: u32);
87}
88
89/// A game coordinator message body with a fixed message type.
90pub trait IGCSerializableMessage: ISteamSerializable {
91    /// The game coordinator message type of this body.
92    fn get_gc_emsg(&self) -> u32;
93}
94
95/// Shorter alias for [`ISteamSerializableHeader`].
96pub use ISteamSerializableHeader as SteamSerializableHeader;
97/// Shorter alias for [`ISteamSerializableMessage`].
98pub use ISteamSerializableMessage as SteamSerializableMessage;
99/// Shorter alias for [`IGCSerializableHeader`].
100pub use IGCSerializableHeader as GcSerializableHeader;
101/// Shorter alias for [`IGCSerializableMessage`].
102pub use IGCSerializableMessage as GcSerializableMessage;
103
104/// A Valve enum that travels on the wire as an integer.
105pub trait WireEnum: Copy {
106    /// The numeric value written to the wire.
107    fn wire_value(self) -> i32;
108    /// Parses a numeric value read from the wire.
109    fn from_wire_value(value: i32) -> Option<Self>;
110    /// The number of bytes this enum occupies on the wire.
111    fn wire_size() -> usize {
112        4
113    }
114}
115
116/// A value that can be written to, and read from, the Steam wire format.
117///
118/// This lives on the type because a `macro_rules!` `ty` fragment can't be
119/// matched against literal tokens, so the macro can't tell a `u32` field from
120/// an enum field on its own.
121///
122/// Byte arrays aren't covered: their length comes from the surrounding frame,
123/// so the structures that have one (`MsgClientP2PIntroducerMessage`, the
124/// protobuf headers) write them by hand.
125pub trait WireValue: Sized {
126    /// Writes this value to the wire.
127    fn write_wire(&self, out: &mut Vec<u8>);
128    /// Reads a value of this type from the wire.
129    fn read_wire(r: &mut Reader<'_>) -> Result<Self, SteamError>;
130}
131
132macro_rules! impl_wire_value {
133    ( $( $ty:ty ),* $(,)? ) => {
134        $(
135            impl WireValue for $ty {
136                fn write_wire(&self, out: &mut Vec<u8>) {
137                    let raw = self.wire_value();
138                    if Self::wire_size() == 1 {
139                        out.push(raw as u8);
140                    } else {
141                        out.extend_from_slice(&raw.to_le_bytes());
142                    }
143                }
144
145                fn read_wire(r: &mut Reader<'_>) -> Result<Self, SteamError> {
146                    let raw = if Self::wire_size() == 1 {
147                        i32::from(r.read_u8()?)
148                    } else {
149                        r.read_i32()?
150                    };
151                    Self::from_wire_value(raw)
152                        .ok_or_else(|| SteamError::Malformed(format!("unknown enum value {raw}")))
153                }
154            }
155        )*
156    };
157}
158
159macro_rules! impl_wire_enum {
160    ( $( $ty:ty ),* $(,)? ) => {
161        $(
162            impl WireEnum for $ty {
163                fn wire_value(self) -> i32 {
164                    self.value()
165                }
166
167                fn from_wire_value(value: i32) -> Option<Self> {
168                    Self::from_value(value)
169                }
170            }
171        )*
172    };
173}
174
175impl_wire_enum!(
176    EMsg,
177    EAppUsageEvent,
178    EChatAction,
179    EChatActionResult,
180    EChatEntryType,
181    EChatInfoType,
182    EChatPermission,
183    EChatRoomEnterResponse,
184    EChatRoomType,
185    EClanRank,
186    EClanRelationship,
187    EDenyReason,
188    EIntroducerRouting,
189    EResult,
190    EServerType,
191    EUniverse,
192);
193
194impl WireEnum for EUdpPacketType {
195    fn wire_value(self) -> i32 {
196        self.value() as i32
197    }
198
199    fn from_wire_value(value: i32) -> Option<Self> {
200        u8::try_from(value).ok().and_then(EUdpPacketType::from_value)
201    }
202
203    /// `PacketType` is a single byte on the wire.
204    fn wire_size() -> usize {
205        1
206    }
207}
208
209impl_wire_value!(
210    EMsg,
211    EAppUsageEvent,
212    EChatAction,
213    EChatActionResult,
214    EChatEntryType,
215    EChatInfoType,
216    EChatPermission,
217    EChatRoomEnterResponse,
218    EChatRoomType,
219    EClanRank,
220    EClanRelationship,
221    EDenyReason,
222    EIntroducerRouting,
223    EResult,
224    EServerType,
225    EUniverse,
226    EUdpPacketType,
227);
228
229macro_rules! impl_wire_value_primitive {
230    ( $( $ty:ident : $write:ident , $read:ident );* $(;)? ) => {
231        $(
232            impl WireValue for $ty {
233                fn write_wire(&self, out: &mut Vec<u8>) {
234                    $write(self, out);
235                }
236
237                fn read_wire(r: &mut Reader<'_>) -> Result<Self, SteamError> {
238                    $read(r)
239                }
240            }
241        )*
242    };
243}
244
245fn write_u8(v: &u8, out: &mut Vec<u8>) {
246    out.push(*v);
247}
248fn write_i8(v: &i8, out: &mut Vec<u8>) {
249    out.push(*v as u8);
250}
251fn write_u16(v: &u16, out: &mut Vec<u8>) {
252    out.extend_from_slice(&v.to_le_bytes());
253}
254fn write_i16(v: &i16, out: &mut Vec<u8>) {
255    out.extend_from_slice(&v.to_le_bytes());
256}
257fn write_u32(v: &u32, out: &mut Vec<u8>) {
258    out.extend_from_slice(&v.to_le_bytes());
259}
260fn write_i32(v: &i32, out: &mut Vec<u8>) {
261    out.extend_from_slice(&v.to_le_bytes());
262}
263fn write_u64(v: &u64, out: &mut Vec<u8>) {
264    out.extend_from_slice(&v.to_le_bytes());
265}
266fn write_i64(v: &i64, out: &mut Vec<u8>) {
267    out.extend_from_slice(&v.to_le_bytes());
268}
269fn write_f32(v: &f32, out: &mut Vec<u8>) {
270    out.extend_from_slice(&v.to_le_bytes());
271}
272fn write_f64(v: &f64, out: &mut Vec<u8>) {
273    out.extend_from_slice(&v.to_le_bytes());
274}
275fn write_bool(v: &bool, out: &mut Vec<u8>) {
276    out.push(if *v { 1u8 } else { 0u8 });
277}
278
279fn read_u8(r: &mut Reader<'_>) -> Result<u8, SteamError> {
280    r.read_u8()
281}
282fn read_i8(r: &mut Reader<'_>) -> Result<i8, SteamError> {
283    r.read_i8()
284}
285fn read_u16(r: &mut Reader<'_>) -> Result<u16, SteamError> {
286    r.read_u16()
287}
288fn read_i16(r: &mut Reader<'_>) -> Result<i16, SteamError> {
289    r.read_i16()
290}
291fn read_u32(r: &mut Reader<'_>) -> Result<u32, SteamError> {
292    r.read_u32()
293}
294fn read_i32(r: &mut Reader<'_>) -> Result<i32, SteamError> {
295    r.read_i32()
296}
297fn read_u64(r: &mut Reader<'_>) -> Result<u64, SteamError> {
298    r.read_u64()
299}
300fn read_i64(r: &mut Reader<'_>) -> Result<i64, SteamError> {
301    r.read_i64()
302}
303fn read_f32(r: &mut Reader<'_>) -> Result<f32, SteamError> {
304    r.read_f32()
305}
306fn read_f64(r: &mut Reader<'_>) -> Result<f64, SteamError> {
307    r.read_f64()
308}
309/// Reads a boolean byte.
310///
311/// Only `1` counts as true; any other byte reads back as `false`, which is
312/// how Steam's own structs behave.
313fn read_bool(r: &mut Reader<'_>) -> Result<bool, SteamError> {
314    Ok(r.read_u8()? == 1)
315}
316
317impl_wire_value_primitive!(
318    u8 : write_u8 , read_u8;
319    i8 : write_i8 , read_i8;
320    u16 : write_u16 , read_u16;
321    i16 : write_i16 , read_i16;
322    u32 : write_u32 , read_u32;
323    i32 : write_i32 , read_i32;
324    u64 : write_u64 , read_u64;
325    i64 : write_i64 , read_i64;
326    f32 : write_f32 , read_f32;
327    f64 : write_f64 , read_f64;
328    bool : write_bool , read_bool;
329);
330
331/// Reads a four byte enum value.
332///
333/// Returns [`SteamError::Malformed`] when the value does not name a member of
334/// the enum.
335pub fn read_enum<T: WireEnum>(r: &mut Reader<'_>) -> Result<T, SteamError> {
336    let raw = r.read_i32()?;
337    T::from_wire_value(raw).ok_or_else(|| SteamError::Malformed(format!("unknown enum value {raw}")))
338}
339
340/// The flag that marks a message type as protobuf backed.
341const PROTO_MASK: u32 = 0x8000_0000;
342
343/// Strips the protobuf flag and returns the message type.
344pub fn get_msg(msg: u32) -> Option<EMsg> {
345    EMsg::from_value((msg & !PROTO_MASK) as i32)
346}
347
348/// Strips the protobuf flag from a game coordinator message type.
349pub fn get_gc_msg(msg: u32) -> u32 {
350    msg & !PROTO_MASK
351}
352
353/// Sets the protobuf flag on a message type if `protobuf` is true.
354pub fn make_msg(msg: EMsg, protobuf: bool) -> u32 {
355    let raw = msg.value() as u32;
356    if protobuf {
357        raw | PROTO_MASK
358    } else {
359        raw
360    }
361}
362
363/// Sets the protobuf flag on a game coordinator message type if `protobuf`
364/// is true.
365pub fn make_gc_msg(msg: u32, protobuf: bool) -> u32 {
366    if protobuf {
367        msg | PROTO_MASK
368    } else {
369        msg
370    }
371}
372
373/// Implements [`SteamSerializable`] and [`SteamSerializableDeserialize`] for a
374/// plain wire structure.
375///
376/// The wire behaviour of each field comes from its own type, via [`WireValue`].
377macro_rules! steam_serializable {
378    (
379        $ty:ty {
380            $( $field:ident : $kind:ty = $default:expr ),* $(,)?
381        }
382    ) => {
383        impl SteamSerializable for $ty {
384            fn write(&self, out: &mut Vec<u8>) {
385                let _ = out;
386                $( WireValue::write_wire(&self.$field, out); )*
387            }
388        }
389
390        impl SteamSerializableDeserialize for $ty {
391            fn read(r: &mut Reader<'_>) -> Result<Self, SteamError> {
392                let _ = r;
393                Ok(Self {
394                    $( $field: WireValue::read_wire(r)?, )*
395                })
396            }
397        }
398    };
399}
400
401/// Implements [`ISteamSerializableMessage`] with a fixed message type.
402macro_rules! steam_message {
403    ( $ty:ty => $emsg:expr ) => {
404        impl ISteamSerializableMessage for $ty {
405            fn get_emsg(&self) -> EMsg {
406                $emsg
407            }
408        }
409    };
410}
411
412/// Implements [`ISteamSerializableHeader`] for a header that carries a `msg`.
413macro_rules! steam_header {
414    ( $ty:ty ) => {
415        impl ISteamSerializableHeader for $ty {
416            fn set_emsg(&mut self, msg: EMsg) {
417                self.msg = msg;
418            }
419        }
420    };
421}
422
423/// The protobuf client message header.
424///
425/// Wire layout: the protobuf-flagged message type (4 bytes), the length of the
426/// protobuf header (4 bytes) and then that many bytes of `CMsgProtoBufHeader`.
427#[derive(Clone, Debug, PartialEq)]
428pub struct MsgHdrProtoBuf {
429    /// The network message type.
430    pub msg: EMsg,
431    /// The length of the serialised protobuf header, in bytes.
432    pub header_length: i32,
433    /// The protobuf header itself.
434    pub proto: CMsgProtoBufHeader,
435}
436
437impl Default for MsgHdrProtoBuf {
438    fn default() -> Self {
439        MsgHdrProtoBuf {
440            msg: EMsg::Invalid,
441            header_length: 0,
442            proto: CMsgProtoBufHeader::default(),
443        }
444    }
445}
446
447impl SteamSerializable for MsgHdrProtoBuf {
448    fn write(&self, out: &mut Vec<u8>) {
449        let proto_bytes = self.proto.encode_to_vec();
450        out.extend_from_slice(&(make_msg(self.msg, true) as i32).to_le_bytes());
451        out.extend_from_slice(&(proto_bytes.len() as i32).to_le_bytes());
452        out.extend_from_slice(&proto_bytes);
453    }
454}
455
456impl SteamSerializableDeserialize for MsgHdrProtoBuf {
457    fn read(r: &mut Reader<'_>) -> Result<Self, SteamError> {
458        let raw = r.read_i32()?;
459        let msg = get_msg(raw as u32).unwrap_or(EMsg::Invalid);
460        let header_length = r.read_i32()?;
461        if header_length < 0 {
462            return Err(SteamError::Malformed(format!(
463                "negative protobuf header length {header_length}"
464            )));
465        }
466        let bytes = r.read_bytes(header_length as usize)?;
467        let proto = CMsgProtoBufHeader::decode(bytes.as_slice())?;
468        Ok(MsgHdrProtoBuf {
469            msg,
470            header_length,
471            proto,
472        })
473    }
474}
475
476steam_header!(MsgHdrProtoBuf);
477
478/// The protobuf game coordinator message header.
479///
480/// Wire layout: the protobuf-flagged message type (4 bytes), the length of the
481/// protobuf header (4 bytes) and then that many bytes of `CMsgProtoBufHeader`.
482#[derive(Clone, Debug, PartialEq)]
483pub struct MsgGCHdrProtoBuf {
484    /// The game coordinator message type.
485    pub msg: u32,
486    /// The length of the serialised protobuf header, in bytes.
487    pub header_length: i32,
488    /// The protobuf header itself.
489    pub proto: GcCMsgProtoBufHeader,
490}
491
492impl Default for MsgGCHdrProtoBuf {
493    fn default() -> Self {
494        MsgGCHdrProtoBuf {
495            msg: 0,
496            header_length: 0,
497            proto: GcCMsgProtoBufHeader::default(),
498        }
499    }
500}
501
502impl SteamSerializable for MsgGCHdrProtoBuf {
503    fn write(&self, out: &mut Vec<u8>) {
504        let proto_bytes = self.proto.encode_to_vec();
505        out.extend_from_slice(&make_gc_msg(self.msg, true).to_le_bytes());
506        out.extend_from_slice(&(proto_bytes.len() as i32).to_le_bytes());
507        out.extend_from_slice(&proto_bytes);
508    }
509}
510
511impl SteamSerializableDeserialize for MsgGCHdrProtoBuf {
512    fn read(r: &mut Reader<'_>) -> Result<Self, SteamError> {
513        let raw = r.read_u32()?;
514        let header_length = r.read_i32()?;
515        if header_length < 0 {
516            return Err(SteamError::Malformed(format!(
517                "negative protobuf header length {header_length}"
518            )));
519        }
520        let bytes = r.read_bytes(header_length as usize)?;
521        let proto = GcCMsgProtoBufHeader::decode(bytes.as_slice())?;
522        Ok(MsgGCHdrProtoBuf {
523            msg: get_gc_msg(raw),
524            header_length,
525            proto,
526        })
527    }
528}
529
530impl IGCSerializableHeader for MsgGCHdrProtoBuf {
531    fn set_gc_emsg(&mut self, msg: u32) {
532        self.msg = msg;
533    }
534}
535
536steam_serializable!(generated::UdpHeader {
537    magic: u32 = 0x3130_5356,
538    payload_size: u16 = 0,
539    packet_type: EUdpPacketType = EUdpPacketType::Invalid,
540    flags: u8 = 0,
541    source_conn_id: u32 = 512,
542    dest_conn_id: u32 = 0,
543    seq_this: u32 = 0,
544    seq_ack: u32 = 0,
545    packets_in_msg: u32 = 0,
546    msg_start_seq: u32 = 0,
547    msg_size: u32 = 0,
548});
549
550steam_serializable!(generated::ChallengeData {
551    challenge_value: u32 = 0,
552    server_load: u32 = 0,
553});
554
555steam_serializable!(generated::ConnectData {
556    challenge_value: u32 = 0,
557});
558
559steam_serializable!(generated::Accept {});
560
561steam_serializable!(generated::Datagram {});
562
563steam_serializable!(generated::Disconnect {});
564
565steam_serializable!(generated::MsgHdr {
566    msg: EMsg = EMsg::Invalid,
567    target_job_id: u64 = u64::MAX,
568    source_job_id: u64 = u64::MAX,
569});
570
571steam_header!(generated::MsgHdr);
572
573steam_serializable!(generated::ExtendedClientMsgHdr {
574    msg: EMsg = EMsg::Invalid,
575    header_size: u8 = 36,
576    header_version: u16 = 2,
577    target_job_id: u64 = u64::MAX,
578    source_job_id: u64 = u64::MAX,
579    header_canary: u8 = 239,
580    steam_id: u64 = 0,
581    session_id: i32 = 0,
582});
583
584steam_header!(generated::ExtendedClientMsgHdr);
585
586steam_serializable!(generated::MsgGCHdr {
587    header_version: u16 = 1,
588    target_job_id: u64 = u64::MAX,
589    source_job_id: u64 = u64::MAX,
590});
591
592impl IGCSerializableHeader for generated::MsgGCHdr {
593    /// Deliberately a no-op: this header has no message type field to set.
594    fn set_gc_emsg(&mut self, _msg: u32) {}
595}
596
597impl SteamSerializable for generated::MsgHdrProtoBuf {
598    fn write(&self, out: &mut Vec<u8>) {
599        out.extend_from_slice(&(make_msg(self.msg, true) as i32).to_le_bytes());
600        out.extend_from_slice(&self.header_length.to_le_bytes());
601        out.extend_from_slice(&self.proto);
602    }
603}
604
605impl SteamSerializableDeserialize for generated::MsgHdrProtoBuf {
606    fn read(r: &mut Reader<'_>) -> Result<Self, SteamError> {
607        let raw = r.read_i32()?;
608        let header_length = r.read_i32()?;
609        if header_length < 0 {
610            return Err(SteamError::Malformed(format!(
611                "negative protobuf header length {header_length}"
612            )));
613        }
614        let proto = r.read_bytes(header_length as usize)?;
615        Ok(generated::MsgHdrProtoBuf {
616            msg: get_msg(raw as u32).unwrap_or(EMsg::Invalid),
617            header_length,
618            proto,
619        })
620    }
621}
622
623impl SteamSerializable for generated::MsgGCHdrProtoBuf {
624    fn write(&self, out: &mut Vec<u8>) {
625        out.extend_from_slice(&make_gc_msg(self.msg, true).to_le_bytes());
626        out.extend_from_slice(&self.header_length.to_le_bytes());
627        out.extend_from_slice(&self.proto);
628    }
629}
630
631impl SteamSerializableDeserialize for generated::MsgGCHdrProtoBuf {
632    fn read(r: &mut Reader<'_>) -> Result<Self, SteamError> {
633        let raw = r.read_u32()?;
634        let header_length = r.read_i32()?;
635        if header_length < 0 {
636            return Err(SteamError::Malformed(format!(
637                "negative protobuf header length {header_length}"
638            )));
639        }
640        let proto = r.read_bytes(header_length as usize)?;
641        Ok(generated::MsgGCHdrProtoBuf {
642            msg: get_gc_msg(raw),
643            header_length,
644            proto,
645        })
646    }
647}
648
649impl IGCSerializableHeader for generated::MsgGCHdrProtoBuf {
650    fn set_gc_emsg(&mut self, msg: u32) {
651        self.msg = msg;
652    }
653}
654
655steam_serializable!(generated::MsgClientJustStrings {});
656
657steam_message!(generated::MsgClientJustStrings => EMsg::Invalid);
658
659steam_serializable!(generated::MsgClientGenericResponse {
660    result: EResult = EResult::Invalid,
661});
662
663steam_message!(generated::MsgClientGenericResponse => EMsg::Invalid);
664
665steam_serializable!(generated::MsgChannelEncryptRequest {
666    protocol_version: u32 = 1,
667    universe: EUniverse = EUniverse::Invalid,
668});
669
670steam_message!(generated::MsgChannelEncryptRequest => EMsg::ChannelEncryptRequest);
671
672steam_serializable!(generated::MsgChannelEncryptResponse {
673    protocol_version: u32 = 1,
674    key_size: u32 = 128,
675});
676
677steam_message!(generated::MsgChannelEncryptResponse => EMsg::ChannelEncryptResponse);
678
679steam_serializable!(generated::MsgChannelEncryptResult {
680    result: EResult = EResult::Invalid,
681});
682
683steam_message!(generated::MsgChannelEncryptResult => EMsg::ChannelEncryptResult);
684
685steam_serializable!(generated::MsgClientLogon {});
686
687steam_message!(generated::MsgClientLogon => EMsg::ClientLogon);
688
689steam_serializable!(generated::MsgClientVACBanStatus {
690    num_bans: u32 = 0,
691});
692
693steam_message!(generated::MsgClientVACBanStatus => EMsg::ClientVACBanStatus);
694
695steam_serializable!(generated::MsgClientAppUsageEvent {
696    app_usage_event: EAppUsageEvent = EAppUsageEvent::GameLaunch,
697    game_id: u64 = 0,
698    offline: u16 = 0,
699});
700
701steam_message!(generated::MsgClientAppUsageEvent => EMsg::ClientAppUsageEvent);
702
703steam_serializable!(generated::MsgClientUpdateGuestPassesList {
704    result: EResult = EResult::Invalid,
705    count_guest_passes_to_give: i32 = 0,
706    count_guest_passes_to_redeem: i32 = 0,
707});
708
709steam_message!(generated::MsgClientUpdateGuestPassesList => EMsg::ClientUpdateGuestPassesList);
710
711/// The fixed size of the `Data` buffer of `MsgClientP2PIntroducerMessage`.
712///
713/// The buffer is always 1450 bytes on the wire, no matter how much payload
714/// `DataLen` announces.
715const P2P_INTRODUCER_DATA_SIZE: usize = 1450;
716
717impl SteamSerializable for generated::MsgClientP2PIntroducerMessage {
718    fn write(&self, out: &mut Vec<u8>) {
719        out.extend_from_slice(&self.steam_id.to_le_bytes());
720        out.extend_from_slice(&self.routing_type.wire_value().to_le_bytes());
721        // `Data` is a fixed size buffer, so exactly `P2P_INTRODUCER_DATA_SIZE`
722        // bytes always go on the wire.
723        let take = self.data.len().min(P2P_INTRODUCER_DATA_SIZE);
724        out.extend_from_slice(&self.data[..take]);
725        out.resize(out.len() + (P2P_INTRODUCER_DATA_SIZE - take), 0);
726        out.extend_from_slice(&self.data_len.to_le_bytes());
727    }
728}
729
730impl SteamSerializableDeserialize for generated::MsgClientP2PIntroducerMessage {
731    fn read(r: &mut Reader<'_>) -> Result<Self, SteamError> {
732        Ok(generated::MsgClientP2PIntroducerMessage {
733            steam_id: r.read_u64()?,
734            routing_type: read_enum(r)?,
735            data: r.read_bytes(P2P_INTRODUCER_DATA_SIZE)?,
736            data_len: r.read_u32()?,
737        })
738    }
739}
740
741steam_message!(
742    generated::MsgClientP2PIntroducerMessage => EMsg::ClientP2PIntroducerMessage
743);
744
745steam_serializable!(generated::MsgClientOGSBeginSession {
746    account_type: u8 = 0,
747    account_id: u64 = 0,
748    app_id: u32 = 0,
749    time_started: u32 = 0,
750});
751
752steam_message!(generated::MsgClientOGSBeginSession => EMsg::ClientOGSBeginSession);
753
754steam_serializable!(generated::MsgClientOGSBeginSessionResponse {
755    result: EResult = EResult::Invalid,
756    collecting_any: bool = false,
757    collecting_details: bool = false,
758    session_id: u64 = 0,
759});
760
761steam_message!(
762    generated::MsgClientOGSBeginSessionResponse => EMsg::ClientOGSBeginSessionResponse
763);
764
765steam_serializable!(generated::MsgClientOGSEndSession {
766    session_id: u64 = 0,
767    time_ended: u32 = 0,
768    reason_code: i32 = 0,
769    count_attributes: i32 = 0,
770});
771
772steam_message!(generated::MsgClientOGSEndSession => EMsg::ClientOGSEndSession);
773
774steam_serializable!(generated::MsgClientOGSEndSessionResponse {
775    result: EResult = EResult::Invalid,
776});
777
778steam_message!(generated::MsgClientOGSEndSessionResponse => EMsg::ClientOGSEndSessionResponse);
779
780steam_serializable!(generated::MsgClientOGSWriteRow {
781    session_id: u64 = 0,
782    count_attributes: i32 = 0,
783});
784
785steam_message!(generated::MsgClientOGSWriteRow => EMsg::ClientOGSWriteRow);
786
787steam_serializable!(generated::MsgClientGetFriendsWhoPlayGame {
788    game_id: u64 = 0,
789});
790
791steam_message!(
792    generated::MsgClientGetFriendsWhoPlayGame => EMsg::ClientGetFriendsWhoPlayGame
793);
794
795steam_serializable!(generated::MsgClientGetFriendsWhoPlayGameResponse {
796    result: EResult = EResult::Invalid,
797    game_id: u64 = 0,
798    count_friends: u32 = 0,
799});
800
801steam_message!(
802    generated::MsgClientGetFriendsWhoPlayGameResponse => EMsg::ClientGetFriendsWhoPlayGameResponse
803);
804
805steam_serializable!(generated::MsgGSPerformHardwareSurvey {
806    flags: u32 = 0,
807});
808
809steam_message!(generated::MsgGSPerformHardwareSurvey => EMsg::GSPerformHardwareSurvey);
810
811steam_serializable!(generated::MsgGSGetPlayStatsResponse {
812    result: EResult = EResult::Invalid,
813    rank: i32 = 0,
814    lifetime_connects: u32 = 0,
815    lifetime_minutes_played: u32 = 0,
816});
817
818steam_message!(generated::MsgGSGetPlayStatsResponse => EMsg::GSGetPlayStatsResponse);
819
820steam_serializable!(generated::MsgGSGetReputationResponse {
821    result: EResult = EResult::Invalid,
822    reputation_score: u32 = 0,
823    banned: bool = false,
824    banned_ip: u32 = 0,
825    banned_port: u16 = 0,
826    banned_game_id: u64 = 0,
827    time_ban_expires: u32 = 0,
828});
829
830steam_message!(generated::MsgGSGetReputationResponse => EMsg::GSGetReputationResponse);
831
832steam_serializable!(generated::MsgGSDeny {
833    steam_id: u64 = 0,
834    deny_reason: EDenyReason = EDenyReason::InvalidVersion,
835});
836
837steam_message!(generated::MsgGSDeny => EMsg::GSDeny);
838
839steam_serializable!(generated::MsgGSApprove {
840    steam_id: u64 = 0,
841});
842
843steam_message!(generated::MsgGSApprove => EMsg::GSApprove);
844
845steam_serializable!(generated::MsgGSKick {
846    steam_id: u64 = 0,
847    deny_reason: EDenyReason = EDenyReason::InvalidVersion,
848    wait_til_map_change: i32 = 0,
849});
850
851steam_message!(generated::MsgGSKick => EMsg::GSKick);
852
853steam_serializable!(generated::MsgGSGetUserGroupStatus {
854    steam_id_user: u64 = 0,
855    steam_id_group: u64 = 0,
856});
857
858steam_message!(generated::MsgGSGetUserGroupStatus => EMsg::GSGetUserGroupStatus);
859
860steam_serializable!(generated::MsgGSGetUserGroupStatusResponse {
861    steam_id_user: u64 = 0,
862    steam_id_group: u64 = 0,
863    clan_relationship: EClanRelationship = EClanRelationship::None,
864    clan_rank: EClanRank = EClanRank::None,
865});
866
867steam_message!(
868    generated::MsgGSGetUserGroupStatusResponse => EMsg::GSGetUserGroupStatusResponse
869);
870
871steam_serializable!(generated::MsgClientJoinChat {
872    steam_id_chat: u64 = 0,
873    is_voice_speaker: bool = false,
874});
875
876steam_message!(generated::MsgClientJoinChat => EMsg::ClientJoinChat);
877
878steam_serializable!(generated::MsgClientChatEnter {
879    steam_id_chat: u64 = 0,
880    steam_id_friend: u64 = 0,
881    chat_room_type: EChatRoomType = EChatRoomType::Friend,
882    steam_id_owner: u64 = 0,
883    steam_id_clan: u64 = 0,
884    chat_flags: u8 = 0,
885    enter_response: EChatRoomEnterResponse = EChatRoomEnterResponse::Success,
886    num_members: i32 = 0,
887});
888
889steam_message!(generated::MsgClientChatEnter => EMsg::ClientChatEnter);
890
891steam_serializable!(generated::MsgClientChatMsg {
892    steam_id_chatter: u64 = 0,
893    steam_id_chat_room: u64 = 0,
894    chat_msg_type: EChatEntryType = EChatEntryType::Invalid,
895});
896
897steam_message!(generated::MsgClientChatMsg => EMsg::ClientChatMsg);
898
899steam_serializable!(generated::MsgClientChatMemberInfo {
900    steam_id_chat: u64 = 0,
901    r#type: EChatInfoType = EChatInfoType::StateChange,
902});
903
904steam_message!(generated::MsgClientChatMemberInfo => EMsg::ClientChatMemberInfo);
905
906steam_serializable!(generated::MsgClientChatAction {
907    steam_id_chat: u64 = 0,
908    steam_id_user_to_act_on: u64 = 0,
909    chat_action: EChatAction = EChatAction::InviteChat,
910});
911
912steam_message!(generated::MsgClientChatAction => EMsg::ClientChatAction);
913
914steam_serializable!(generated::MsgClientChatActionResult {
915    steam_id_chat: u64 = 0,
916    steam_id_user_acted_on: u64 = 0,
917    chat_action: EChatAction = EChatAction::InviteChat,
918    action_result: EChatActionResult = EChatActionResult::Success,
919});
920
921steam_message!(generated::MsgClientChatActionResult => EMsg::ClientChatActionResult);
922
923steam_serializable!(generated::MsgClientChatRoomInfo {
924    steam_id_chat: u64 = 0,
925    r#type: EChatInfoType = EChatInfoType::StateChange,
926});
927
928steam_message!(generated::MsgClientChatRoomInfo => EMsg::ClientChatRoomInfo);
929
930steam_serializable!(generated::MsgClientSetIgnoreFriend {
931    my_steam_id: u64 = 0,
932    steam_id_friend: u64 = 0,
933    ignore: u8 = 0,
934});
935
936steam_message!(generated::MsgClientSetIgnoreFriend => EMsg::ClientSetIgnoreFriend);
937
938steam_serializable!(generated::MsgClientSetIgnoreFriendResponse {
939    friend_id: u64 = 0,
940    result: EResult = EResult::Invalid,
941});
942
943steam_message!(generated::MsgClientSetIgnoreFriendResponse => EMsg::ClientSetIgnoreFriendResponse);
944
945steam_serializable!(generated::MsgClientLoggedOff {
946    result: EResult = EResult::Invalid,
947    sec_min_reconnect_hint: i32 = 0,
948    sec_max_reconnect_hint: i32 = 0,
949});
950
951steam_message!(generated::MsgClientLoggedOff => EMsg::ClientLoggedOff);
952
953steam_serializable!(generated::MsgClientLogOnResponse {
954    result: EResult = EResult::Invalid,
955    out_of_game_heartbeat_rate_sec: i32 = 0,
956    in_game_heartbeat_rate_sec: i32 = 0,
957    client_supplied_steam_id: u64 = 0,
958    ip_public: u32 = 0,
959    server_real_time: u32 = 0,
960});
961
962steam_message!(generated::MsgClientLogOnResponse => EMsg::ClientLogOnResponse);
963
964steam_serializable!(generated::MsgClientServerUnavailable {
965    jobid_sent: u64 = 0,
966    e_msg_sent: u32 = 0,
967    e_server_type_unavailable: EServerType = EServerType::First,
968});
969
970steam_message!(generated::MsgClientServerUnavailable => EMsg::ClientServerUnavailable);
971
972steam_serializable!(generated::MsgClientCreateChat {
973    chat_room_type: EChatRoomType = EChatRoomType::Friend,
974    game_id: u64 = 0,
975    steam_id_clan: u64 = 0,
976    permission_officer: EChatPermission = EChatPermission::Close,
977    permission_member: EChatPermission = EChatPermission::Close,
978    permission_all: EChatPermission = EChatPermission::Close,
979    members_max: u32 = 0,
980    chat_flags: u8 = 0,
981    steam_id_friend_chat: u64 = 0,
982    steam_id_invited: u64 = 0,
983});
984
985steam_message!(generated::MsgClientCreateChat => EMsg::ClientCreateChat);
986
987steam_serializable!(generated::MsgClientCreateChatResponse {
988    result: EResult = EResult::Invalid,
989    steam_id_chat: u64 = 0,
990    chat_room_type: EChatRoomType = EChatRoomType::Friend,
991    steam_id_friend_chat: u64 = 0,
992});
993
994steam_message!(generated::MsgClientCreateChatResponse => EMsg::ClientCreateChatResponse);
995
996steam_serializable!(generated::MsgClientMarketingMessageUpdate2 {
997    marketing_message_update_time: u32 = 0,
998    count: u32 = 0,
999});
1000
1001steam_message!(generated::MsgClientMarketingMessageUpdate2 => EMsg::ClientMarketingMessageUpdate2);
1002
1003steam_serializable!(generated::MsgClientGetLegacyGameKey {
1004    app_id: u32 = 0,
1005});
1006
1007steam_message!(generated::MsgClientGetLegacyGameKey => EMsg::ClientGetLegacyGameKey);
1008
1009steam_serializable!(generated::MsgClientGetLegacyGameKeyResponse {
1010    app_id: u32 = 0,
1011    result: EResult = EResult::Invalid,
1012    length: u32 = 0,
1013});
1014
1015steam_message!(generated::MsgClientGetLegacyGameKeyResponse => EMsg::ClientGetLegacyGameKeyResponse);
1016
1017#[cfg(test)]
1018mod temp_wire_verification {
1019    use super::*;
1020    use crate::base::generated::steam_language::internal as gen;
1021
1022    fn roundtrip<T>(value: &T, expected_len: usize, label: &str)
1023    where
1024        T: SteamSerializable + SteamSerializableDeserialize + PartialEq + std::fmt::Debug,
1025    {
1026        let mut buf = Vec::new();
1027        value.write(&mut buf);
1028        assert_eq!(buf.len(), expected_len, "{label}: serialized length");
1029        let mut r = Reader::new(&buf);
1030        let back = match T::read(&mut r) {
1031            Ok(v) => v,
1032            Err(e) => panic!("{label}: read failed: {e}"),
1033        };
1034        assert_eq!(*value, back, "{label}: round trip mismatch");
1035        assert_eq!(r.position(), buf.len(), "{label}: trailing bytes");
1036    }
1037
1038    #[test]
1039    fn wire_layout_sizes() {
1040        roundtrip(&gen::UdpHeader::default(), 36, "UdpHeader");
1041        roundtrip(&gen::ChallengeData::default(), 8, "ChallengeData");
1042        roundtrip(&gen::ConnectData::default(), 4, "ConnectData");
1043        roundtrip(&gen::Accept::default(), 0, "Accept");
1044        roundtrip(&gen::Datagram::default(), 0, "Datagram");
1045        roundtrip(&gen::Disconnect::default(), 0, "Disconnect");
1046        roundtrip(&gen::MsgHdr::default(), 20, "MsgHdr");
1047        roundtrip(&gen::ExtendedClientMsgHdr::default(), 36, "ExtendedClientMsgHdr");
1048        roundtrip(&gen::MsgGCHdr::default(), 18, "MsgGCHdr");
1049        roundtrip(&gen::MsgClientJustStrings::default(), 0, "MsgClientJustStrings");
1050        roundtrip(&gen::MsgClientGenericResponse::default(), 4, "MsgClientGenericResponse");
1051        roundtrip(&gen::MsgChannelEncryptRequest::default(), 8, "MsgChannelEncryptRequest");
1052        roundtrip(&gen::MsgChannelEncryptResponse::default(), 8, "MsgChannelEncryptResponse");
1053        roundtrip(&gen::MsgChannelEncryptResult::default(), 4, "MsgChannelEncryptResult");
1054        roundtrip(&gen::MsgClientLogon::default(), 0, "MsgClientLogon");
1055        roundtrip(&gen::MsgClientVACBanStatus::default(), 4, "MsgClientVACBanStatus");
1056        roundtrip(&gen::MsgClientAppUsageEvent::default(), 14, "MsgClientAppUsageEvent");
1057        roundtrip(&gen::MsgClientUpdateGuestPassesList::default(), 12, "MsgClientUpdateGuestPassesList");
1058        roundtrip(&gen::MsgClientOGSBeginSession::default(), 17, "MsgClientOGSBeginSession");
1059        roundtrip(&gen::MsgClientOGSBeginSessionResponse::default(), 14, "MsgClientOGSBeginSessionResponse");
1060        roundtrip(&gen::MsgClientOGSEndSession::default(), 20, "MsgClientOGSEndSession");
1061        roundtrip(&gen::MsgClientOGSEndSessionResponse::default(), 4, "MsgClientOGSEndSessionResponse");
1062        roundtrip(&gen::MsgClientOGSWriteRow::default(), 12, "MsgClientOGSWriteRow");
1063        roundtrip(&gen::MsgClientGetFriendsWhoPlayGame::default(), 8, "MsgClientGetFriendsWhoPlayGame");
1064        roundtrip(&gen::MsgClientGetFriendsWhoPlayGameResponse::default(), 16, "MsgClientGetFriendsWhoPlayGameResponse");
1065        roundtrip(&gen::MsgGSPerformHardwareSurvey::default(), 4, "MsgGSPerformHardwareSurvey");
1066        roundtrip(&gen::MsgGSGetPlayStatsResponse::default(), 16, "MsgGSGetPlayStatsResponse");
1067        roundtrip(&gen::MsgGSGetReputationResponse::default(), 27, "MsgGSGetReputationResponse");
1068        roundtrip(&gen::MsgGSDeny::default(), 12, "MsgGSDeny");
1069        roundtrip(&gen::MsgGSApprove::default(), 8, "MsgGSApprove");
1070        roundtrip(&gen::MsgGSKick::default(), 16, "MsgGSKick");
1071        roundtrip(&gen::MsgGSGetUserGroupStatus::default(), 16, "MsgGSGetUserGroupStatus");
1072        roundtrip(&gen::MsgGSGetUserGroupStatusResponse::default(), 24, "MsgGSGetUserGroupStatusResponse");
1073        roundtrip(&gen::MsgClientJoinChat::default(), 9, "MsgClientJoinChat");
1074        roundtrip(&gen::MsgClientChatEnter::default(), 45, "MsgClientChatEnter");
1075        roundtrip(&gen::MsgClientChatMsg::default(), 20, "MsgClientChatMsg");
1076        roundtrip(&gen::MsgClientChatMemberInfo::default(), 12, "MsgClientChatMemberInfo");
1077        roundtrip(&gen::MsgClientChatAction::default(), 20, "MsgClientChatAction");
1078        roundtrip(&gen::MsgClientChatActionResult::default(), 24, "MsgClientChatActionResult");
1079        roundtrip(&gen::MsgClientChatRoomInfo::default(), 12, "MsgClientChatRoomInfo");
1080        roundtrip(&gen::MsgClientSetIgnoreFriend::default(), 17, "MsgClientSetIgnoreFriend");
1081        roundtrip(&gen::MsgClientSetIgnoreFriendResponse::default(), 12, "MsgClientSetIgnoreFriendResponse");
1082        roundtrip(&gen::MsgClientLoggedOff::default(), 12, "MsgClientLoggedOff");
1083        roundtrip(&gen::MsgClientLogOnResponse::default(), 28, "MsgClientLogOnResponse");
1084        roundtrip(&gen::MsgClientServerUnavailable::default(), 16, "MsgClientServerUnavailable");
1085        roundtrip(&gen::MsgClientCreateChat::default(), 53, "MsgClientCreateChat");
1086        roundtrip(&gen::MsgClientCreateChatResponse::default(), 24, "MsgClientCreateChatResponse");
1087        roundtrip(&gen::MsgClientMarketingMessageUpdate2::default(), 8, "MsgClientMarketingMessageUpdate2");
1088        roundtrip(&gen::MsgClientGetLegacyGameKey::default(), 4, "MsgClientGetLegacyGameKey");
1089        roundtrip(&gen::MsgClientGetLegacyGameKeyResponse::default(), 12, "MsgClientGetLegacyGameKeyResponse");
1090
1091        let p2p = gen::MsgClientP2PIntroducerMessage {
1092            steam_id: 0x0100_0000_0000_0002,
1093            routing_type: EIntroducerRouting::P2PNetworking,
1094            data: vec![0xABu8; 1450],
1095            data_len: 1450,
1096        };
1097        roundtrip(&p2p, 1466, "MsgClientP2PIntroducerMessage");
1098    }
1099
1100    #[test]
1101    fn udp_header_bytes_are_byte_exact() {
1102        let mut buf = Vec::new();
1103        let hdr = gen::UdpHeader {
1104            magic: 0x3130_5356,
1105            payload_size: 0x1234,
1106            packet_type: EUdpPacketType::ChallengeReq,
1107            flags: 0x07,
1108            source_conn_id: 0x1111_1111,
1109            dest_conn_id: 0x2222_2222,
1110            seq_this: 0x3333_3333,
1111            seq_ack: 0x4444_4444,
1112            packets_in_msg: 0x5555_5555,
1113            msg_start_seq: 0x6666_6666,
1114            msg_size: 0x7777_7777,
1115        };
1116        hdr.write(&mut buf);
1117
1118        let expected: Vec<u8> = vec![
1119            0x56, 0x53, 0x30, 0x31, // magic
1120            0x34, 0x12, // payload size
1121            0x01, // packet type - ONE byte
1122            0x07, // flags
1123            0x11, 0x11, 0x11, 0x11, // source conn id
1124            0x22, 0x22, 0x22, 0x22, // dest conn id
1125            0x33, 0x33, 0x33, 0x33, // seq this
1126            0x44, 0x44, 0x44, 0x44, // seq ack
1127            0x55, 0x55, 0x55, 0x55, // packets in msg
1128            0x66, 0x66, 0x66, 0x66, // msg start seq
1129            0x77, 0x77, 0x77, 0x77, // msg size
1130        ];
1131        assert_eq!(buf, expected);
1132        assert_eq!(buf.len(), 36);
1133
1134        let mut r = Reader::new(&buf);
1135        let back = gen::UdpHeader::read(&mut r).expect("udp header read");
1136        assert_eq!(back, hdr);
1137    }
1138
1139    #[test]
1140    fn enum_widths_are_four_bytes_except_udp_packet_type() {
1141        let mut buf = Vec::new();
1142        gen::MsgHdr::default().write(&mut buf);
1143        assert_eq!(buf.len(), 20, "MsgHdr = 4 byte EMsg + 2 x u64");
1144
1145        let mut buf = Vec::new();
1146        gen::MsgClientGenericResponse::default().write(&mut buf);
1147        assert_eq!(buf.len(), 4, "EResult is four bytes");
1148
1149        let mut buf = Vec::new();
1150        gen::MsgClientChatEnter::default().write(&mut buf);
1151        assert_eq!(buf.len(), 45, "EChatRoomType + EChatRoomEnterResponse are four bytes");
1152
1153        let mut buf = Vec::new();
1154        gen::MsgClientOGSBeginSessionResponse::default().write(&mut buf);
1155        assert_eq!(buf.len(), 14, "two bools are one byte each");
1156
1157        let mut buf = Vec::new();
1158        gen::MsgClientP2PIntroducerMessage {
1159            steam_id: 1,
1160            routing_type: EIntroducerRouting::P2PVoiceChat,
1161            data: Vec::new(),
1162            data_len: 0,
1163        }
1164        .write(&mut buf);
1165        assert_eq!(buf.len(), 1466, "u64 + 4 byte enum + 1450 bytes + u32");
1166    }
1167}
1168