1use crate::error::Result;
2use crate::io::BitReader;
3
4use super::field_value::FieldValue;
5use super::quantized_float::QuantizedFloat;
6
7pub struct FieldDecodeContext {
9 pub tick_interval: f32,
11 pub string_buf: Vec<u8>,
13}
14
15impl FieldDecodeContext {
16 pub fn new(tick_interval: f32) -> Self {
17 Self {
18 tick_interval,
19 string_buf: Vec::with_capacity(512),
20 }
21 }
22}
23
24#[derive(Debug, Clone)]
29pub enum Decoder {
30 Bool,
31 I64,
32 U64,
33 U64Fixed64,
34 F32NoScale,
35 F32SimulationTime,
36 F32Coord,
37 F32Normal,
38 F32Quantized(QuantizedFloat),
39 String,
40 Vector2(Box<Decoder>),
41 Vector3(Box<Decoder>),
42 Vector3Normal,
43 Vector4(Box<Decoder>),
44 QAnglePitchYaw {
45 bit_count: usize,
46 },
47 QAnglePrecise,
48 QAngleBitCount {
49 bit_count: usize,
50 },
51 QAngleCoord,
52 Default,
54}
55
56impl Decoder {
57 pub fn decode(&self, ctx: &mut FieldDecodeContext, br: &mut BitReader) -> Result<FieldValue> {
59 match self {
60 Decoder::Bool => Ok(FieldValue::Bool(br.read_bool()?)),
61
62 Decoder::I64 => Ok(FieldValue::I64(br.read_varint64()?)),
63
64 Decoder::U64 => Ok(FieldValue::U64(br.read_uvarint64()?)),
65
66 Decoder::U64Fixed64 => {
67 let mut buf = [0u8; 8];
68 br.read_bytes(&mut buf)?;
69 Ok(FieldValue::U64(u64::from_le_bytes(buf)))
70 }
71
72 Decoder::F32NoScale => Ok(FieldValue::F32(br.read_f32()?)),
73
74 Decoder::F32SimulationTime => {
75 let ticks = br.read_uvarint32()?;
76 Ok(FieldValue::F32(ticks as f32 * ctx.tick_interval))
77 }
78
79 Decoder::F32Coord => Ok(FieldValue::F32(br.read_bitcoord()?)),
80
81 Decoder::F32Normal => Ok(FieldValue::F32(br.read_bitnormal()?)),
82
83 Decoder::F32Quantized(qf) => Ok(FieldValue::F32(qf.decode(br)?)),
84
85 Decoder::String => {
86 ctx.string_buf.clear();
87 br.read_string_raw(&mut ctx.string_buf)?;
88 Ok(FieldValue::String(ctx.string_buf.clone()))
89 }
90
91 Decoder::Vector2(inner) => {
92 let x = inner.decode_f32(ctx, br)?;
93 let y = inner.decode_f32(ctx, br)?;
94 Ok(FieldValue::Vector2([x, y]))
95 }
96
97 Decoder::Vector3(inner) => {
98 let x = inner.decode_f32(ctx, br)?;
99 let y = inner.decode_f32(ctx, br)?;
100 let z = inner.decode_f32(ctx, br)?;
101 Ok(FieldValue::Vector3([x, y, z]))
102 }
103
104 Decoder::Vector3Normal => Ok(FieldValue::Vector3(br.read_bitvec3normal()?)),
105
106 Decoder::Vector4(inner) => {
107 let x = inner.decode_f32(ctx, br)?;
108 let y = inner.decode_f32(ctx, br)?;
109 let z = inner.decode_f32(ctx, br)?;
110 let w = inner.decode_f32(ctx, br)?;
111 Ok(FieldValue::Vector4([x, y, z, w]))
112 }
113
114 Decoder::QAnglePitchYaw { bit_count } => {
115 let pitch = br.read_bitangle(*bit_count)?;
116 let yaw = br.read_bitangle(*bit_count)?;
117 Ok(FieldValue::QAngle([pitch, yaw, 0.0]))
118 }
119
120 Decoder::QAnglePrecise => {
121 let mut v = [0.0f32; 3];
122 let rx = br.read_bool()?;
123 let ry = br.read_bool()?;
124 let rz = br.read_bool()?;
125 if rx {
126 v[0] = br.read_bitangle(20)?;
127 }
128 if ry {
129 v[1] = br.read_bitangle(20)?;
130 }
131 if rz {
132 v[2] = br.read_bitangle(20)?;
133 }
134 Ok(FieldValue::QAngle(v))
135 }
136
137 Decoder::QAngleBitCount { bit_count } => {
138 let x = br.read_bitangle(*bit_count)?;
139 let y = br.read_bitangle(*bit_count)?;
140 let z = br.read_bitangle(*bit_count)?;
141 Ok(FieldValue::QAngle([x, y, z]))
142 }
143
144 Decoder::QAngleCoord => Ok(FieldValue::QAngle(br.read_bitvec3coord()?)),
145
146 Decoder::Default => Ok(FieldValue::U64(br.read_uvarint64()?)),
147 }
148 }
149
150 fn decode_f32(&self, ctx: &mut FieldDecodeContext, br: &mut BitReader) -> Result<f32> {
152 match self.decode(ctx, br)? {
153 FieldValue::F32(v) => Ok(v),
154 _ => Ok(0.0),
155 }
156 }
157
158 #[allow(clippy::only_used_in_recursion)]
161 pub fn skip(&self, ctx: &mut FieldDecodeContext, br: &mut BitReader) -> Result<()> {
162 match self {
163 Decoder::Bool => {
164 br.skip_bits(1)?;
165 }
166
167 Decoder::I64 => {
168 br.skip_varint()?;
169 }
170
171 Decoder::U64 => {
172 br.skip_varint()?;
173 }
174
175 Decoder::U64Fixed64 => {
176 br.skip_bits(64)?;
177 }
178
179 Decoder::F32NoScale => {
180 br.skip_bits(32)?;
181 }
182
183 Decoder::F32SimulationTime => {
184 br.skip_varint()?;
185 }
186
187 Decoder::F32Coord => {
188 br.skip_bitcoord()?;
189 }
190
191 Decoder::F32Normal => {
192 br.skip_bitnormal()?;
193 }
194
195 Decoder::F32Quantized(qf) => {
196 qf.skip(br)?;
197 }
198
199 Decoder::String => {
200 br.skip_string()?;
201 }
202
203 Decoder::Vector2(inner) => {
204 inner.skip(ctx, br)?;
205 inner.skip(ctx, br)?;
206 }
207
208 Decoder::Vector3(inner) => {
209 inner.skip(ctx, br)?;
210 inner.skip(ctx, br)?;
211 inner.skip(ctx, br)?;
212 }
213
214 Decoder::Vector3Normal => {
215 br.skip_bitvec3normal()?;
216 }
217
218 Decoder::Vector4(inner) => {
219 inner.skip(ctx, br)?;
220 inner.skip(ctx, br)?;
221 inner.skip(ctx, br)?;
222 inner.skip(ctx, br)?;
223 }
224
225 Decoder::QAnglePitchYaw { bit_count } => {
226 br.skip_bits(*bit_count * 2)?;
227 }
228
229 Decoder::QAnglePrecise => {
230 let rx = br.read_bool()?;
231 let ry = br.read_bool()?;
232 let rz = br.read_bool()?;
233 if rx {
234 br.skip_bits(20)?;
235 }
236 if ry {
237 br.skip_bits(20)?;
238 }
239 if rz {
240 br.skip_bits(20)?;
241 }
242 }
243
244 Decoder::QAngleBitCount { bit_count } => {
245 br.skip_bits(*bit_count * 3)?;
246 }
247
248 Decoder::QAngleCoord => {
249 br.skip_bitvec3coord()?;
250 }
251
252 Decoder::Default => {
253 br.skip_varint()?;
254 }
255 }
256 Ok(())
257 }
258}
259
260#[derive(Debug, Clone)]
263pub enum FieldSpecialDescriptor {
264 FixedArray { length: usize },
266 DynamicArray { inner_decoder: Decoder },
268 DynamicSerializerArray,
270 Pointer,
272}
273
274#[derive(Debug, Clone)]
276pub struct FieldMetadata {
277 pub decoder: Decoder,
278 pub special: Option<FieldSpecialDescriptor>,
279}
280
281impl Default for FieldMetadata {
282 fn default() -> Self {
283 Self {
284 decoder: Decoder::Default,
285 special: None,
286 }
287 }
288}
289
290impl FieldMetadata {
291 pub fn is_dynamic_array(&self) -> bool {
292 matches!(
293 self.special,
294 Some(FieldSpecialDescriptor::DynamicArray { .. })
295 | Some(FieldSpecialDescriptor::DynamicSerializerArray)
296 )
297 }
298
299 pub fn is_fixed_array(&self) -> bool {
300 matches!(
301 self.special,
302 Some(FieldSpecialDescriptor::FixedArray { .. })
303 )
304 }
305
306 pub fn fixed_array_length(&self) -> Option<usize> {
307 match &self.special {
308 Some(FieldSpecialDescriptor::FixedArray { length }) => Some(*length),
309 _ => None,
310 }
311 }
312
313 pub fn is_dynamic_serializer_array(&self) -> bool {
314 matches!(
315 self.special,
316 Some(FieldSpecialDescriptor::DynamicSerializerArray)
317 )
318 }
319
320 pub fn is_pointer(&self) -> bool {
321 matches!(self.special, Some(FieldSpecialDescriptor::Pointer))
322 }
323
324 pub fn dynamic_array_inner_metadata(&self) -> FieldMetadata {
325 match &self.special {
326 Some(FieldSpecialDescriptor::DynamicArray { inner_decoder }) => FieldMetadata {
327 decoder: inner_decoder.clone(),
328 special: None,
329 },
330 _ => FieldMetadata::default(),
331 }
332 }
333}
334
335fn build_f32_decoder(
337 var_name: &str,
338 bit_count: Option<i32>,
339 low_value: Option<f32>,
340 high_value: Option<f32>,
341 encode_flags: Option<i32>,
342 var_encoder: Option<&str>,
343) -> Decoder {
344 if var_name == "m_flSimulationTime" || var_name == "m_flAnimTime" {
346 return Decoder::F32SimulationTime;
347 }
348
349 if let Some(encoder) = var_encoder {
351 match encoder {
352 "coord" => return Decoder::F32Coord,
353 "normal" => return Decoder::F32Normal,
354 _ => {}
355 }
356 }
357
358 let bc = bit_count.unwrap_or(0);
359 if bc == 0 || bc == 32 {
360 return Decoder::F32NoScale;
361 }
362
363 match QuantizedFloat::new(
365 bc,
366 encode_flags.unwrap_or(0),
367 low_value.unwrap_or(0.0),
368 high_value.unwrap_or(0.0),
369 ) {
370 Ok(qf) => Decoder::F32Quantized(qf),
371 Err(_) => Decoder::F32NoScale,
372 }
373}
374
375#[allow(clippy::too_many_arguments)]
399pub fn get_field_metadata(
400 var_type: &str,
401 var_name: &str,
402 bit_count: Option<i32>,
403 low_value: Option<f32>,
404 high_value: Option<f32>,
405 encode_flags: Option<i32>,
406 var_encoder: Option<&str>,
407 has_field_serializer: bool,
408) -> FieldMetadata {
409 let trimmed = var_type.trim();
411
412 if trimmed.ends_with('*') {
414 return FieldMetadata {
415 decoder: Decoder::Bool,
416 special: Some(FieldSpecialDescriptor::Pointer),
417 };
418 }
419
420 if let Some(bracket_pos) = trimmed.find('[')
422 && trimmed.ends_with(']')
423 {
424 let base = trimmed[..bracket_pos].trim();
425 let len_str = trimmed[bracket_pos + 1..trimmed.len() - 1].trim();
426
427 if base == "char" {
429 return FieldMetadata {
430 decoder: Decoder::String,
431 special: None,
432 };
433 }
434
435 let length = len_str.parse::<usize>().unwrap_or(match len_str {
436 "MAX_ABILITY_DRAFT_ABILITIES" => 48,
437 "DOTA_ABILITY_DRAFT_HEROES_PER_GAME" => 10,
438 _ => 64,
439 });
440
441 let inner = get_field_metadata(
442 base,
443 var_name,
444 bit_count,
445 low_value,
446 high_value,
447 encode_flags,
448 var_encoder,
449 has_field_serializer,
450 );
451
452 return FieldMetadata {
453 decoder: inner.decoder,
454 special: Some(FieldSpecialDescriptor::FixedArray { length }),
455 };
456 }
457
458 if let Some(angle_pos) = trimmed.find('<')
460 && let Some(close_pos) = trimmed.rfind('>')
461 {
462 let base = trimmed[..angle_pos].trim();
463 let inner_type = trimmed[angle_pos + 1..close_pos].trim();
464
465 let is_vector_base = matches!(
466 base,
467 "CNetworkUtlVectorBase" | "CUtlVectorEmbeddedNetworkVar" | "CUtlVector"
468 );
469
470 if is_vector_base {
471 if has_field_serializer {
472 return FieldMetadata {
473 decoder: Decoder::U64,
474 special: Some(FieldSpecialDescriptor::DynamicSerializerArray),
475 };
476 }
477
478 let inner = get_field_metadata(
479 inner_type,
480 var_name,
481 bit_count,
482 low_value,
483 high_value,
484 encode_flags,
485 var_encoder,
486 has_field_serializer,
487 );
488
489 return FieldMetadata {
490 decoder: Decoder::U64,
491 special: Some(FieldSpecialDescriptor::DynamicArray {
492 inner_decoder: inner.decoder,
493 }),
494 };
495 }
496
497 return get_field_metadata(
499 base,
500 var_name,
501 bit_count,
502 low_value,
503 high_value,
504 encode_flags,
505 var_encoder,
506 has_field_serializer,
507 );
508 }
509
510 match trimmed {
512 "int8" | "int16" | "int32" | "int64" => FieldMetadata {
514 decoder: Decoder::I64,
515 special: None,
516 },
517
518 "bool" => FieldMetadata {
519 decoder: Decoder::Bool,
520 special: None,
521 },
522
523 "float32" | "CNetworkedQuantizedFloat" | "GameTime_t" => {
524 let decoder = build_f32_decoder(
525 var_name,
526 bit_count,
527 low_value,
528 high_value,
529 encode_flags,
530 var_encoder,
531 );
532 FieldMetadata {
533 decoder,
534 special: None,
535 }
536 }
537
538 "CBodyComponentDCGBaseAnimating"
541 | "CBodyComponentBaseAnimating"
542 | "CBodyComponentBaseAnimatingOverlay"
543 | "CBodyComponentBaseModelEntity"
544 | "CBodyComponent"
545 | "CBodyComponentSkeletonInstance"
546 | "CBodyComponentPoint"
547 | "CLightComponent"
548 | "CRenderComponent"
549 | "C_BodyComponentBaseAnimating"
550 | "C_BodyComponentBaseAnimatingOverlay"
551 | "CPhysicsComponent" => FieldMetadata {
552 decoder: Decoder::Bool,
553 special: Some(FieldSpecialDescriptor::Pointer),
554 },
555
556 "CUtlSymbolLarge" | "CUtlString" => FieldMetadata {
558 decoder: Decoder::String,
559 special: None,
560 },
561
562 "QAngle" => {
564 let bc = bit_count.unwrap_or(0) as usize;
565 let decoder = if let Some(encoder) = var_encoder {
566 match encoder {
567 "qangle_pitch_yaw" => Decoder::QAnglePitchYaw { bit_count: bc },
568 "qangle_precise" => Decoder::QAnglePrecise,
569 _ => {
570 if bc == 0 {
571 Decoder::QAngleCoord
572 } else {
573 Decoder::QAngleBitCount { bit_count: bc }
574 }
575 }
576 }
577 } else if bc == 0 {
578 Decoder::QAngleCoord
579 } else {
580 Decoder::QAngleBitCount { bit_count: bc }
581 };
582 FieldMetadata {
583 decoder,
584 special: None,
585 }
586 }
587
588 "Vector" | "VectorWS" => {
590 if var_encoder == Some("normal") {
591 FieldMetadata {
592 decoder: Decoder::Vector3Normal,
593 special: None,
594 }
595 } else {
596 let inner = build_f32_decoder(
597 var_name,
598 bit_count,
599 low_value,
600 high_value,
601 encode_flags,
602 var_encoder,
603 );
604 FieldMetadata {
605 decoder: Decoder::Vector3(Box::new(inner)),
606 special: None,
607 }
608 }
609 }
610
611 "Vector2D" => {
612 let inner = build_f32_decoder(
613 var_name,
614 bit_count,
615 low_value,
616 high_value,
617 encode_flags,
618 var_encoder,
619 );
620 FieldMetadata {
621 decoder: Decoder::Vector2(Box::new(inner)),
622 special: None,
623 }
624 }
625
626 "Vector4D" => {
627 let inner = build_f32_decoder(
628 var_name,
629 bit_count,
630 low_value,
631 high_value,
632 encode_flags,
633 var_encoder,
634 );
635 FieldMetadata {
636 decoder: Decoder::Vector4(Box::new(inner)),
637 special: None,
638 }
639 }
640
641 "m_SpeechBubbles" | "DOTA_CombatLogQueryProgress" => FieldMetadata {
643 decoder: Decoder::U64,
644 special: Some(FieldSpecialDescriptor::DynamicSerializerArray),
645 },
646
647 _ => {
649 let decoder = if var_encoder == Some("fixed64") {
650 Decoder::U64Fixed64
651 } else {
652 Decoder::U64
653 };
654 FieldMetadata {
655 decoder,
656 special: None,
657 }
658 }
659 }
660}
661
662#[cfg(test)]
663mod tests {
664 use super::*;
665 use crate::io::BitReader;
666
667 fn meta(var_type: &str, var_name: &str) -> FieldMetadata {
668 get_field_metadata(var_type, var_name, None, None, None, None, None, false)
669 }
670
671 #[allow(clippy::too_many_arguments)]
672 fn meta_full(
673 var_type: &str,
674 var_name: &str,
675 bit_count: Option<i32>,
676 low: Option<f32>,
677 high: Option<f32>,
678 encode_flags: Option<i32>,
679 var_encoder: Option<&str>,
680 has_fs: bool,
681 ) -> FieldMetadata {
682 get_field_metadata(
683 var_type,
684 var_name,
685 bit_count,
686 low,
687 high,
688 encode_flags,
689 var_encoder,
690 has_fs,
691 )
692 }
693
694 #[test]
697 fn pointer_type() {
698 let m = meta("CBaseEntity*", "m_hOwner");
699 assert!(matches!(m.decoder, Decoder::Bool));
700 assert!(m.is_pointer());
701 }
702
703 #[test]
704 fn bool_type() {
705 let m = meta("bool", "m_bActive");
706 assert!(matches!(m.decoder, Decoder::Bool));
707 assert!(m.special.is_none());
708 }
709
710 #[test]
711 fn int32_type() {
712 let m = meta("int32", "m_iHealth");
713 assert!(matches!(m.decoder, Decoder::I64));
714 }
715
716 #[test]
717 fn float32_no_scale() {
718 let m = meta("float32", "m_flValue");
719 assert!(matches!(m.decoder, Decoder::F32NoScale));
720 }
721
722 #[test]
723 fn simulation_time() {
724 let m = meta("float32", "m_flSimulationTime");
725 assert!(matches!(m.decoder, Decoder::F32SimulationTime));
726 }
727
728 #[test]
729 fn coord_encoder() {
730 let m = meta_full(
731 "float32",
732 "m_x",
733 None,
734 None,
735 None,
736 None,
737 Some("coord"),
738 false,
739 );
740 assert!(matches!(m.decoder, Decoder::F32Coord));
741 }
742
743 #[test]
744 fn quantized_float() {
745 let m = meta_full(
746 "float32",
747 "m_val",
748 Some(8),
749 Some(0.0),
750 Some(255.0),
751 None,
752 None,
753 false,
754 );
755 assert!(matches!(m.decoder, Decoder::F32Quantized(_)));
756 }
757
758 #[test]
759 fn string_utl_symbol() {
760 let m = meta("CUtlSymbolLarge", "m_iszName");
761 assert!(matches!(m.decoder, Decoder::String));
762 }
763
764 #[test]
765 fn char_array_is_string() {
766 let m = meta("char[256]", "m_szName");
767 assert!(matches!(m.decoder, Decoder::String));
768 assert!(!m.is_fixed_array());
769 }
770
771 #[test]
772 fn int32_array_is_fixed_array() {
773 let m = meta("int32[4]", "m_values");
774 assert!(m.is_fixed_array());
775 assert_eq!(m.fixed_array_length(), Some(4));
776 }
777
778 #[test]
779 fn dynamic_array_without_serializer() {
780 let m = meta("CNetworkUtlVectorBase< int32 >", "m_items");
781 assert!(m.is_dynamic_array());
782 assert!(!m.is_dynamic_serializer_array());
783 }
784
785 #[test]
786 fn dynamic_serializer_array() {
787 let m = meta_full(
788 "CNetworkUtlVectorBase< SomeType >",
789 "m_items",
790 None,
791 None,
792 None,
793 None,
794 None,
795 true,
796 );
797 assert!(m.is_dynamic_serializer_array());
798 }
799
800 #[test]
801 fn qangle_no_encoder_no_bits() {
802 let m = meta("QAngle", "m_angle");
803 assert!(matches!(m.decoder, Decoder::QAngleCoord));
804 }
805
806 #[test]
807 fn qangle_with_bitcount() {
808 let m = meta_full("QAngle", "m_angle", Some(16), None, None, None, None, false);
809 assert!(matches!(
810 m.decoder,
811 Decoder::QAngleBitCount { bit_count: 16 }
812 ));
813 }
814
815 #[test]
816 fn qangle_pitch_yaw() {
817 let m = meta_full(
818 "QAngle",
819 "m_angle",
820 Some(10),
821 None,
822 None,
823 None,
824 Some("qangle_pitch_yaw"),
825 false,
826 );
827 assert!(matches!(
828 m.decoder,
829 Decoder::QAnglePitchYaw { bit_count: 10 }
830 ));
831 }
832
833 #[test]
836 fn decode_bool_from_1bit() {
837 let data = [0x01];
838 let mut br = BitReader::new(&data);
839 let mut ctx = FieldDecodeContext::new(1.0 / 64.0);
840 let val = Decoder::Bool.decode(&mut ctx, &mut br).unwrap();
841 assert!(matches!(val, FieldValue::Bool(true)));
842 }
843
844 #[test]
845 fn decode_f32_no_scale() {
846 let bytes = 1.5f32.to_le_bytes();
847 let mut br = BitReader::new(&bytes);
848 let mut ctx = FieldDecodeContext::new(1.0 / 64.0);
849 let val = Decoder::F32NoScale.decode(&mut ctx, &mut br).unwrap();
850 if let FieldValue::F32(f) = val {
851 assert!((f - 1.5).abs() < f32::EPSILON);
852 } else {
853 panic!("expected F32");
854 }
855 }
856
857 #[test]
858 fn decode_string_null_terminated() {
859 let data = b"hello\0";
860 let mut br = BitReader::new(data);
861 let mut ctx = FieldDecodeContext::new(1.0 / 64.0);
862 let val = Decoder::String.decode(&mut ctx, &mut br).unwrap();
863 if let FieldValue::String(s) = val {
864 assert_eq!(&s, b"hello");
865 } else {
866 panic!("expected String");
867 }
868 }
869
870 #[test]
873 fn field_metadata_helpers() {
874 let dyn_arr = FieldMetadata {
875 decoder: Decoder::U64,
876 special: Some(FieldSpecialDescriptor::DynamicArray {
877 inner_decoder: Decoder::I64,
878 }),
879 };
880 assert!(dyn_arr.is_dynamic_array());
881 assert!(!dyn_arr.is_fixed_array());
882 assert!(!dyn_arr.is_pointer());
883
884 let fixed = FieldMetadata {
885 decoder: Decoder::I64,
886 special: Some(FieldSpecialDescriptor::FixedArray { length: 8 }),
887 };
888 assert!(fixed.is_fixed_array());
889 assert_eq!(fixed.fixed_array_length(), Some(8));
890
891 let ptr = FieldMetadata {
892 decoder: Decoder::Bool,
893 special: Some(FieldSpecialDescriptor::Pointer),
894 };
895 assert!(ptr.is_pointer());
896 }
897}