From 3c32042d654eebb045eab7d73062fbbf692de503 Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Wed, 14 Sep 2016 19:03:37 -0400 Subject: [PATCH] decode the fields --- ent/array.go | 17 +++++++++++++++++ ent/class.go | 15 ++++++++++----- ent/cutl.go | 20 ++++++++++++++++++++ ent/decoders.go | 43 ++++++++++++++++++++++++++----------------- ent/dict.go | 4 ++-- ent/entity.go | 20 +++++++++++++++----- ent/field.go | 34 ++++++++++++++++++++++++++++++++-- ent/float_decoders.go | 4 +--- ent/namespace.go | 26 ++++++++++++++++++++++++++ ent/selection.go | 16 +++++++++------- ent/type_spec.go | 8 ++++++++ 11 files changed, 166 insertions(+), 41 deletions(-) create mode 100644 ent/array.go create mode 100644 ent/cutl.go diff --git a/ent/array.go b/ent/array.go new file mode 100644 index 0000000..7788c8a --- /dev/null +++ b/ent/array.go @@ -0,0 +1,17 @@ +package ent + +import ( + "strconv" +) + +type array struct { + slots []interface{} + _slotType string + decoder +} + +func (a *array) slotName(slot int) string { return strconv.Itoa(slot) } +func (a *array) slotValue(slot int) interface{} { return a.slots[slot] } +func (a *array) slotType(slot int) string { return a._slotType } +func (a *array) slotDecoder(slot int) decoder { return a.decoder } +func (a *array) setSlotValue(slot int, val interface{}) { a.slots[slot] = val } diff --git a/ent/class.go b/ent/class.go index b38e53b..e9d724e 100644 --- a/ent/class.go +++ b/ent/class.go @@ -19,12 +19,17 @@ type Class struct { fieldNames map[string]int } -func (c *Class) New(serial int) *Entity { - return &Entity{ - Class: c, - slots: make([]interface{}, len(c.Fields)), - serial: serial, +func (c *Class) New(serial int, baseline bool) *Entity { + e := &Entity{ + Class: c, + slots: make([]interface{}, len(c.Fields)), + serial: serial, + isBaseline: baseline, } + for slot := range e.slots { + e.slots[slot] = c.Fields[slot].initializer() + } + return e } func (c Class) String() string { diff --git a/ent/cutl.go b/ent/cutl.go new file mode 100644 index 0000000..3b4b968 --- /dev/null +++ b/ent/cutl.go @@ -0,0 +1,20 @@ +package ent + +import ( + "strconv" +) + +type cutlVector struct { + slots []interface{} + _slotType string + decoder +} + +func (v *cutlVector) slotName(slot int) string { return strconv.Itoa(slot) } +func (v *cutlVector) slotValue(slot int) interface{} { return v.slots[slot] } +func (v *cutlVector) slotType(slot int) string { return v._slotType } +func (v *cutlVector) slotDecoder(slot int) decoder { return v.decoder } + +func (v *cutlVector) setSlotValue(slot int, val interface{}) { + v.slots[slot] = val +} diff --git a/ent/decoders.go b/ent/decoders.go index c806102..c47fc51 100644 --- a/ent/decoders.go +++ b/ent/decoders.go @@ -44,26 +44,27 @@ func newFieldDecoder(n *Namespace, f *Field) decoder { // for compound types such as handles, vectors (in the c++ std::vector // sense), and arrays - ts := parseTypeName(n, typeName) - switch ts.kind { + switch f.typeSpec.kind { case t_element: - Debug.Printf("weird typespec: we shouldn't have elements here: %v", ts) + Debug.Printf("weird typespec: we shouldn't have elements here: %v", f.typeSpec) return func(bit.Reader) interface{} { - Info.Fatalf("unable to decode element of type: %v", ts.name) + Info.Fatalf("unable to decode element of type: %v", f.typeSpec.name) return nil } case t_object: return func(br bit.Reader) interface{} { - Debug.Printf("unable to decode object of type: %v", ts.name) + Debug.Printf("unable to decode object of type: %v", f.typeSpec.name) return decodeVarInt32(br) } case t_array: - return arrayDecoder(n, f, ts) + return arrayDecoder(n, f) case t_template: - return templateDecoder(f, ts) + return templateDecoder(f) + case t_pointer: + return decodeBool } - return nil + panic("fart") } func decodeBool(br bit.Reader) interface{} { return bit.ReadBool(br) } @@ -85,10 +86,8 @@ func decodeColor(br bit.Reader) interface{} { func entityDecoder(c *Class) decoder { return func(br bit.Reader) interface{} { - if bit.ReadBool(br) { - return c.New(-1) - } - return nil + bit.ReadBool(br) // what does this do + return c.New(-1, false) } } @@ -201,18 +200,28 @@ func qangleDecoder(f *Field) decoder { } } -func arrayDecoder(n *Namespace, f *Field, ts typeSpec) decoder { - return decodeVarInt32 +func arrayDecoder(n *Namespace, f *Field) decoder { + return func(br bit.Reader) interface{} { + Debug.Printf("dunno what this int32 val does in array decoder: %d", bit.ReadVarInt32(br)) + if f.initializer != nil { + return f.initializer() + } + return nil + } } -func templateDecoder(f *Field, ts typeSpec) decoder { - switch ts.template { +func templateDecoder(f *Field) decoder { + switch f.typeSpec.template { case "CHandle": return decodeVarInt32 case "CStrongHandle": return decodeVarInt64 case "CUtlVector": - return decodeVarInt32 + return func(br bit.Reader) interface{} { + v := decodeVarInt32(br) + Debug.Printf("dunno what this varint is for in the cutlvector decoder: %v", v) + return v + } } return nil } diff --git a/ent/dict.go b/ent/dict.go index 29e73a6..680dde0 100644 --- a/ent/dict.go +++ b/ent/dict.go @@ -72,7 +72,7 @@ func (d *Dict) createEntity(id int) error { if class == nil { return fmt.Errorf("unable to create entity %d: no class found for class name %s, version %d", className, classV) } - e := class.New(serial) + e := class.New(serial, false) d.entities[id] = e Debug.Printf("create entity id: %d serial: %d classId: %d className: %v class: %v\n", id, serial, classId, className, class) return fillSlots(e, class.Name.String(), d.sr, d.br) @@ -190,7 +190,7 @@ func (d *Dict) syncBaselines() { } if c.baseline == nil { - c.baseline = c.New(-1) + c.baseline = c.New(-1, true) } if e.Value == nil || len(e.Value) == 0 { diff --git a/ent/entity.go b/ent/entity.go index e7a6c1d..cc19d92 100644 --- a/ent/entity.go +++ b/ent/entity.go @@ -2,12 +2,22 @@ package ent type Entity struct { *Class - serial int - slots []interface{} + serial int + slots []interface{} + isBaseline bool } -func (e *Entity) slotName(n int) string { return e.Class.Fields[n].name.String() } -func (e *Entity) slotType(n int) string { return e.Class.Fields[n]._type.String() } -func (e *Entity) slotValue(n int) interface{} { return e.slots[n] } +func (e *Entity) slotName(n int) string { return e.Class.Fields[n].name.String() } +func (e *Entity) slotType(n int) string { return e.Class.Fields[n]._type.String() } +func (e *Entity) slotValue(n int) interface{} { + v := e.slots[n] + if v != nil { + return v + } + if !e.isBaseline && e.Class.baseline != nil { + return e.Class.baseline.slotValue(n) + } + return nil +} func (e *Entity) slotDecoder(n int) decoder { return e.Class.Fields[n].decoder } func (e *Entity) setSlotValue(n int, v interface{}) { e.slots[n] = v } diff --git a/ent/field.go b/ent/field.go index 4dfb2ec..4d70e60 100644 --- a/ent/field.go +++ b/ent/field.go @@ -8,7 +8,8 @@ import ( ) type Field struct { - _type Symbol // type of data held by the field + _type Symbol // type of data held by the field + typeSpec typeSpec name Symbol // name of the field sendNode Symbol // not sure what this is bits uint // number of bits used to encode field? @@ -20,7 +21,8 @@ type Field struct { class *Class // source class on which the field was originally defined encoder *Symbol // binary encoder, named explicitly in protobuf decoder // decodes field values from a bit stream - isTemplate bool // whether or not the field is a template type + initializer func() interface{} + isTemplate bool // whether or not the field is a template type templateType string elemType string } @@ -56,6 +58,7 @@ func (f *Field) fromProto(flat *dota.ProtoFlattenedSerializerFieldT, t *SymbolTa f.flags = int(flat.GetEncodeFlags()) f.low = flat.GetLowValue() f.high = flat.GetHighValue() + f.initializer = nilInitializer if flat.FieldSerializerNameSym == nil { f.serializer = nil @@ -69,3 +72,30 @@ func (f *Field) fromProto(flat *dota.ProtoFlattenedSerializerFieldT, t *SymbolTa f.sendNode = t.Symbol(int(*flat.SendNodeSym)) Debug.Printf("new field: %v", f) } + +// creates a new field which is a sort of virtual field that represents what a +// field woudl look like if we had one for a container field's elements. +// honestly this is a really shitty hack it just seems easier than rewriting +// the newFieldDecoder logic. +func (f *Field) memberField() *Field { + mf := new(Field) + *mf = *f + mf.typeSpec = *f.typeSpec.member + // yeahhhh + mf._type = Symbol{0, &SymbolTable{mf.typeSpec.name}} + return mf +} + +func (f *Field) isContainer() bool { + if f.typeSpec.kind == t_array { + return true + } + if f.typeSpec.kind == t_template { + if f.typeSpec.template == "CUtlVector" { + return true + } + } + return false +} + +func nilInitializer() interface{} { return nil } diff --git a/ent/float_decoders.go b/ent/float_decoders.go index 2ae0d54..6d1613d 100644 --- a/ent/float_decoders.go +++ b/ent/float_decoders.go @@ -86,7 +86,5 @@ func floatDecoder(f *Field) decoder { // reads an IEEE 754 binary float value off of the stream func ieeeFloat32Decoder(br bit.Reader) interface{} { - u := uint32(br.ReadBits(32)) - Debug.Printf("ieee float32 decode bits: %d", u) - return math.Float32frombits(u) + return math.Float32frombits(uint32(br.ReadBits(32))) } diff --git a/ent/namespace.go b/ent/namespace.go index 9fe2579..23c93fa 100644 --- a/ent/namespace.go +++ b/ent/namespace.go @@ -107,6 +107,32 @@ func (n *Namespace) mergeSendTables(st *dota.CDemoSendTables) error { } } + // for some fields, we want to initialize a zero value on the baseline + // instance. specifically, we do this for arrays so that we can't try + // to index into nil. + f.typeSpec = parseTypeName(n, f._type.String()) + if f.isContainer() { + mf := f.memberField() + fn := newFieldDecoder(n, mf) + if f.typeSpec.kind == t_array { + f.initializer = func() interface{} { + return &array{ + slots: make([]interface{}, f.typeSpec.size), + _slotType: mf._type.String(), + decoder: fn, + } + } + } else if f.typeSpec.kind == t_template && f.typeSpec.template == "CUtlVector" { + f.initializer = func() interface{} { + return &cutlVector{ + slots: make([]interface{}, f.typeSpec.size), + _slotType: mf._type.String(), + decoder: fn, + } + } + } + } + // we also wait until after we've discovered all of the classes to // build the field decoder functions, because some fields are // themselves entities. diff --git a/ent/selection.go b/ent/selection.go index 681016b..7c4709c 100644 --- a/ent/selection.go +++ b/ent/selection.go @@ -21,22 +21,24 @@ func (s selection) path() []int { return s.vals[:s.count] } func (s selection) fill(offset int, displayPath string, dest slotted, br bit.Reader) error { slot := s.vals[offset] - switch s.count - offset { - case 0: + if s.count-offset <= 0 { panic("selection makes no sense") + } + switch s.count - offset { case 1: fn := dest.slotDecoder(slot) if fn == nil { - switch v := dest.(type) { + switch dest.(type) { case *Entity: Debug.Printf("%s %s (%s)", s, fmt.Sprintf("%s.%s", displayPath, dest.slotName(slot)), dest.slotType(slot)) - Info.Fatalf("%v entity has no decoder for slot %d (%v)", v.Class, slot, v.Class.Fields[slot]) + return nil + // Info.Fatalf("%v entity has no decoder for slot %d (%v)", v.Class, slot, v.Class.Fields[slot]) default: - Info.Fatalf("slotted value %v has no decoder for slot %d", dest, slot) + Info.Printf("slotted value %v has no decoder for slot %d", dest, slot) + return nil } } old := dest.slotValue(slot) - Debug.Printf("%s %s (%s): %v", s, fmt.Sprintf("%s.%s", displayPath, dest.slotName(slot)), dest.slotType(slot), old) val := fn(br) dest.setSlotValue(slot, val) Debug.Printf("%s %s (%s): %v -> %v", s, fmt.Sprintf("%s.%s", displayPath, dest.slotName(slot)), dest.slotType(slot), old, val) @@ -45,7 +47,7 @@ func (s selection) fill(offset int, displayPath string, dest slotted, br bit.Rea v := dest.slotValue(slot) vs, ok := v.(slotted) if !ok { - Info.Fatalf("child selection %s at offset %d refers to a slot (%d: %s) that contains a non-slotted type: %s", s, offset, slot, fmt.Sprintf("%s.%s", displayPath, dest.slotName(slot)), dest.slotType(slot)) + Info.Printf("child selection %s at offset %d refers to a slot (%d: %s) that contains a non-slotted type: %s with value: %v", s, offset, slot, fmt.Sprintf("%s.%s", displayPath, dest.slotName(slot)), dest.slotType(slot), v) return fmt.Errorf("child selection refers to a slot that doesn't contain a slotted value") } return s.fill(offset+1, fmt.Sprintf("%s.%s", displayPath, dest.slotName(slot)), vs, br) diff --git a/ent/type_spec.go b/ent/type_spec.go index e469bfd..fc8f79c 100644 --- a/ent/type_spec.go +++ b/ent/type_spec.go @@ -11,6 +11,7 @@ const ( t_object // c++ object type that is not a known element class t_array // c++ array type t_template // c++ template type + t_pointer // c++ pointer ) // constant identifiers @@ -40,6 +41,13 @@ func parseTypeName(n *Namespace, s string) typeSpec { return t } + if strings.HasSuffix(s, "*") { + t.kind = t_pointer + t.member = new(typeSpec) + *t.member = parseTypeName(n, s[:len(s)-2]) + return t + } + // presumably this is some sort of array type if strings.ContainsRune(s, '[') { memName, count := parseArrayName(s)